1/*
2**
3** Copyright 2007, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17package com.android.packageinstaller;
18
19import android.app.Activity;
20import android.app.ActivityManagerNative;
21import android.app.AlertDialog;
22import android.app.Dialog;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.DialogInterface.OnCancelListener;
26import android.content.Intent;
27import android.content.SharedPreferences;
28import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageInfo;
30import android.content.pm.PackageManager;
31import android.content.pm.PackageUserState;
32import android.content.pm.PackageManager.NameNotFoundException;
33import android.content.pm.PackageParser;
34import android.content.pm.VerificationParams;
35import android.graphics.Rect;
36import android.net.Uri;
37import android.os.Bundle;
38import android.provider.Settings;
39import android.support.v4.view.PagerAdapter;
40import android.support.v4.view.ViewPager;
41import android.util.Log;
42import android.view.LayoutInflater;
43import android.view.View;
44import android.view.View.OnClickListener;
45import android.view.ViewGroup;
46import android.widget.AppSecurityPermissions;
47import android.widget.Button;
48import android.widget.ScrollView;
49import android.widget.TabHost;
50import android.widget.TabWidget;
51import android.widget.TextView;
52
53import java.io.File;
54import java.util.ArrayList;
55
56/*
57 * This activity is launched when a new application is installed via side loading
58 * The package is first parsed and the user is notified of parse errors via a dialog.
59 * If the package is successfully parsed, the user is notified to turn on the install unknown
60 * applications setting. A memory check is made at this point and the user is notified of out
61 * of memory conditions if any. If the package is already existing on the device,
62 * a confirmation dialog (to replace the existing package) is presented to the user.
63 * Based on the user response the package is then installed by launching InstallAppConfirm
64 * sub activity. All state transitions are handled in this activity
65 */
66public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
67    private static final String TAG = "PackageInstaller";
68    private Uri mPackageURI;
69    private Uri mOriginatingURI;
70    private Uri mReferrerURI;
71    private int mOriginatingUid = VerificationParams.NO_UID;
72
73    private boolean localLOGV = false;
74    PackageManager mPm;
75    PackageInfo mPkgInfo;
76    ApplicationInfo mSourceInfo;
77
78    // ApplicationInfo object primarily used for already existing applications
79    private ApplicationInfo mAppInfo = null;
80
81    // View for install progress
82    View mInstallConfirm;
83    // Buttons to indicate user acceptance
84    private Button mOk;
85    private Button mCancel;
86    CaffeinatedScrollView mScrollView = null;
87    private boolean mOkCanInstall = false;
88
89    static final String PREFS_ALLOWED_SOURCES = "allowed_sources";
90
91    // Dialog identifiers used in showDialog
92    private static final int DLG_BASE = 0;
93    private static final int DLG_UNKNOWN_APPS = DLG_BASE + 1;
94    private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
95    private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
96    private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
97    private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;
98
99    /**
100     * This is a helper class that implements the management of tabs and all
101     * details of connecting a ViewPager with associated TabHost.  It relies on a
102     * trick.  Normally a tab host has a simple API for supplying a View or
103     * Intent that each tab will show.  This is not sufficient for switching
104     * between pages.  So instead we make the content part of the tab host
105     * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
106     * view to show as the tab content.  It listens to changes in tabs, and takes
107     * care of switch to the correct paged in the ViewPager whenever the selected
108     * tab changes.
109     */
110    public static class TabsAdapter extends PagerAdapter
111            implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
112        private final Context mContext;
113        private final TabHost mTabHost;
114        private final ViewPager mViewPager;
115        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
116        private final Rect mTempRect = new Rect();
117
118        static final class TabInfo {
119            private final String tag;
120            private final View view;
121
122            TabInfo(String _tag, View _view) {
123                tag = _tag;
124                view = _view;
125            }
126        }
127
128        static class DummyTabFactory implements TabHost.TabContentFactory {
129            private final Context mContext;
130
131            public DummyTabFactory(Context context) {
132                mContext = context;
133            }
134
135            @Override
136            public View createTabContent(String tag) {
137                View v = new View(mContext);
138                v.setMinimumWidth(0);
139                v.setMinimumHeight(0);
140                return v;
141            }
142        }
143
144        public TabsAdapter(Activity activity, TabHost tabHost, ViewPager pager) {
145            mContext = activity;
146            mTabHost = tabHost;
147            mViewPager = pager;
148            mTabHost.setOnTabChangedListener(this);
149            mViewPager.setAdapter(this);
150            mViewPager.setOnPageChangeListener(this);
151        }
152
153        public void addTab(TabHost.TabSpec tabSpec, View view) {
154            tabSpec.setContent(new DummyTabFactory(mContext));
155            String tag = tabSpec.getTag();
156
157            TabInfo info = new TabInfo(tag, view);
158            mTabs.add(info);
159            mTabHost.addTab(tabSpec);
160            notifyDataSetChanged();
161        }
162
163        @Override
164        public int getCount() {
165            return mTabs.size();
166        }
167
168        @Override
169        public Object instantiateItem(ViewGroup container, int position) {
170            View view = mTabs.get(position).view;
171            container.addView(view);
172            return view;
173        }
174
175        @Override
176        public void destroyItem(ViewGroup container, int position, Object object) {
177            container.removeView((View)object);
178        }
179
180        @Override
181        public boolean isViewFromObject(View view, Object object) {
182            return view == object;
183        }
184
185        @Override
186        public void onTabChanged(String tabId) {
187            int position = mTabHost.getCurrentTab();
188            mViewPager.setCurrentItem(position);
189        }
190
191        @Override
192        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
193        }
194
195        @Override
196        public void onPageSelected(int position) {
197            // Unfortunately when TabHost changes the current tab, it kindly
198            // also takes care of putting focus on it when not in touch mode.
199            // The jerk.
200            // This hack tries to prevent this from pulling focus out of our
201            // ViewPager.
202            TabWidget widget = mTabHost.getTabWidget();
203            int oldFocusability = widget.getDescendantFocusability();
204            widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
205            mTabHost.setCurrentTab(position);
206            widget.setDescendantFocusability(oldFocusability);
207
208            // Scroll the current tab into visibility if needed.
209            View tab = widget.getChildTabViewAt(position);
210            mTempRect.set(tab.getLeft(), tab.getTop(), tab.getRight(), tab.getBottom());
211            widget.requestRectangleOnScreen(mTempRect, false);
212
213            // Make sure the scrollbars are visible for a moment after selection
214            final View contentView = mTabs.get(position).view;
215            if (contentView instanceof CaffeinatedScrollView) {
216                ((CaffeinatedScrollView) contentView).awakenScrollBars();
217            }
218        }
219
220        @Override
221        public void onPageScrollStateChanged(int state) {
222        }
223    }
224
225    private void startInstallConfirm() {
226        TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
227        tabHost.setup();
228        ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
229        TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
230
231        boolean permVisible = false;
232        mScrollView = null;
233        mOkCanInstall = false;
234        int msg = 0;
235        if (mPkgInfo != null) {
236            AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
237            final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
238            final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
239            if (mAppInfo != null) {
240                msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
241                        ? R.string.install_confirm_question_update_system
242                        : R.string.install_confirm_question_update;
243                mScrollView = new CaffeinatedScrollView(this);
244                mScrollView.setFillViewport(true);
245                if (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0) {
246                    permVisible = true;
247                    mScrollView.addView(perms.getPermissionsView(
248                            AppSecurityPermissions.WHICH_NEW));
249                } else {
250                    LayoutInflater inflater = (LayoutInflater)getSystemService(
251                            Context.LAYOUT_INFLATER_SERVICE);
252                    TextView label = (TextView)inflater.inflate(R.layout.label, null);
253                    label.setText(R.string.no_new_perms);
254                    mScrollView.addView(label);
255                }
256                adapter.addTab(tabHost.newTabSpec("new").setIndicator(
257                        getText(R.string.newPerms)), mScrollView);
258            } else  {
259                findViewById(R.id.tabscontainer).setVisibility(View.GONE);
260                findViewById(R.id.divider).setVisibility(View.VISIBLE);
261            }
262            if (NP > 0 || ND > 0) {
263                permVisible = true;
264                LayoutInflater inflater = (LayoutInflater)getSystemService(
265                        Context.LAYOUT_INFLATER_SERVICE);
266                View root = inflater.inflate(R.layout.permissions_list, null);
267                if (mScrollView == null) {
268                    mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
269                }
270                if (NP > 0) {
271                    ((ViewGroup)root.findViewById(R.id.privacylist)).addView(
272                            perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
273                } else {
274                    root.findViewById(R.id.privacylist).setVisibility(View.GONE);
275                }
276                if (ND > 0) {
277                    ((ViewGroup)root.findViewById(R.id.devicelist)).addView(
278                            perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
279                } else {
280                    root.findViewById(R.id.devicelist).setVisibility(View.GONE);
281                }
282                adapter.addTab(tabHost.newTabSpec("all").setIndicator(
283                        getText(R.string.allPerms)), root);
284            }
285        }
286        if (!permVisible) {
287            if (mAppInfo != null) {
288                // This is an update to an application, but there are no
289                // permissions at all.
290                msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
291                        ? R.string.install_confirm_question_update_system_no_perms
292                        : R.string.install_confirm_question_update_no_perms;
293            } else {
294                // This is a new application with no permissions.
295                msg = R.string.install_confirm_question_no_perms;
296            }
297            tabHost.setVisibility(View.GONE);
298            findViewById(R.id.filler).setVisibility(View.VISIBLE);
299            findViewById(R.id.divider).setVisibility(View.GONE);
300            mScrollView = null;
301        }
302        if (msg != 0) {
303            ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
304        }
305        mInstallConfirm.setVisibility(View.VISIBLE);
306        mOk = (Button)findViewById(R.id.ok_button);
307        mCancel = (Button)findViewById(R.id.cancel_button);
308        mOk.setOnClickListener(this);
309        mCancel.setOnClickListener(this);
310        if (mScrollView == null) {
311            // There is nothing to scroll view, so the ok button is immediately
312            // set to install.
313            mOk.setText(R.string.install);
314            mOkCanInstall = true;
315        } else {
316            mScrollView.setFullScrollAction(new Runnable() {
317                @Override
318                public void run() {
319                    mOk.setText(R.string.install);
320                    mOkCanInstall = true;
321                }
322            });
323        }
324    }
325
326    private void showDialogInner(int id) {
327        // TODO better fix for this? Remove dialog so that it gets created again
328        removeDialog(id);
329        showDialog(id);
330    }
331
332    @Override
333    public Dialog onCreateDialog(int id, Bundle bundle) {
334        switch (id) {
335        case DLG_UNKNOWN_APPS:
336            return new AlertDialog.Builder(this)
337                    .setTitle(R.string.unknown_apps_dlg_title)
338                    .setMessage(R.string.unknown_apps_dlg_text)
339                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
340                        public void onClick(DialogInterface dialog, int which) {
341                            Log.i(TAG, "Finishing off activity so that user can navigate to settings manually");
342                            finish();
343                        }})
344                    .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
345                        public void onClick(DialogInterface dialog, int which) {
346                            Log.i(TAG, "Launching settings");
347                            launchSettingsAppAndFinish();
348                        }
349                    })
350                    .setOnCancelListener(this)
351                    .create();
352        case DLG_PACKAGE_ERROR :
353            return new AlertDialog.Builder(this)
354                    .setTitle(R.string.Parse_error_dlg_title)
355                    .setMessage(R.string.Parse_error_dlg_text)
356                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
357                        public void onClick(DialogInterface dialog, int which) {
358                            finish();
359                        }
360                    })
361                    .setOnCancelListener(this)
362                    .create();
363        case DLG_OUT_OF_SPACE:
364            // Guaranteed not to be null. will default to package name if not set by app
365            CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
366            String dlgText = getString(R.string.out_of_space_dlg_text,
367                    appTitle.toString());
368            return new AlertDialog.Builder(this)
369                    .setTitle(R.string.out_of_space_dlg_title)
370                    .setMessage(dlgText)
371                    .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
372                        public void onClick(DialogInterface dialog, int which) {
373                            //launch manage applications
374                            Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
375                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
376                            startActivity(intent);
377                            finish();
378                        }
379                    })
380                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
381                        public void onClick(DialogInterface dialog, int which) {
382                            Log.i(TAG, "Canceling installation");
383                            finish();
384                        }
385                  })
386                  .setOnCancelListener(this)
387                  .create();
388        case DLG_INSTALL_ERROR :
389            // Guaranteed not to be null. will default to package name if not set by app
390            CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
391            String dlgText1 = getString(R.string.install_failed_msg,
392                    appTitle1.toString());
393            return new AlertDialog.Builder(this)
394                    .setTitle(R.string.install_failed)
395                    .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
396                        public void onClick(DialogInterface dialog, int which) {
397                            finish();
398                        }
399                    })
400                    .setMessage(dlgText1)
401                    .setOnCancelListener(this)
402                    .create();
403        case DLG_ALLOW_SOURCE:
404            CharSequence appTitle2 = mPm.getApplicationLabel(mSourceInfo);
405            String dlgText2 = getString(R.string.allow_source_dlg_text,
406                    appTitle2.toString());
407            return new AlertDialog.Builder(this)
408                    .setTitle(R.string.allow_source_dlg_title)
409                    .setMessage(dlgText2)
410                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
411                        public void onClick(DialogInterface dialog, int which) {
412                            setResult(RESULT_CANCELED);
413                            finish();
414                        }})
415                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
416                        public void onClick(DialogInterface dialog, int which) {
417                            SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES,
418                                    Context.MODE_PRIVATE);
419                            prefs.edit().putBoolean(mSourceInfo.packageName, true).apply();
420                            startInstallConfirm();
421                        }
422                    })
423                    .setOnCancelListener(this)
424                    .create();
425       }
426       return null;
427   }
428
429    private void launchSettingsAppAndFinish() {
430        // Create an intent to launch SettingsTwo activity
431        Intent launchSettingsIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
432        launchSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
433        startActivity(launchSettingsIntent);
434        finish();
435    }
436
437    private boolean isInstallingUnknownAppsAllowed() {
438        return Settings.Global.getInt(getContentResolver(),
439            Settings.Global.INSTALL_NON_MARKET_APPS, 0) > 0;
440    }
441
442    private void initiateInstall() {
443        String pkgName = mPkgInfo.packageName;
444        // Check if there is already a package on the device with this name
445        // but it has been renamed to something else.
446        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
447        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
448            pkgName = oldName[0];
449            mPkgInfo.packageName = pkgName;
450            mPkgInfo.applicationInfo.packageName = pkgName;
451        }
452        // Check if package is already installed. display confirmation dialog if replacing pkg
453        try {
454            // This is a little convoluted because we want to get all uninstalled
455            // apps, but this may include apps with just data, and if it is just
456            // data we still want to count it as "installed".
457            mAppInfo = mPm.getApplicationInfo(pkgName,
458                    PackageManager.GET_UNINSTALLED_PACKAGES);
459            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
460                mAppInfo = null;
461            }
462        } catch (NameNotFoundException e) {
463            mAppInfo = null;
464        }
465        startInstallConfirm();
466    }
467
468    void setPmResult(int pmResult) {
469        Intent result = new Intent();
470        result.putExtra(Intent.EXTRA_INSTALL_RESULT, pmResult);
471        setResult(pmResult == PackageManager.INSTALL_SUCCEEDED
472                ? RESULT_OK : RESULT_FIRST_USER, result);
473    }
474
475    @Override
476    protected void onCreate(Bundle icicle) {
477        super.onCreate(icicle);
478
479        // get intent information
480        final Intent intent = getIntent();
481        mPackageURI = intent.getData();
482        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
483        mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
484        mPm = getPackageManager();
485
486        final String scheme = mPackageURI.getScheme();
487        if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
488            Log.w(TAG, "Unsupported scheme " + scheme);
489            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
490            return;
491        }
492
493        final PackageUtil.AppSnippet as;
494        if ("package".equals(mPackageURI.getScheme())) {
495            try {
496                mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
497                        PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
498            } catch (NameNotFoundException e) {
499            }
500            if (mPkgInfo == null) {
501                Log.w(TAG, "Requested package " + mPackageURI.getScheme()
502                        + " not available. Discontinuing installation");
503                showDialogInner(DLG_PACKAGE_ERROR);
504                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
505                return;
506            }
507            as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
508                    mPm.getApplicationIcon(mPkgInfo.applicationInfo));
509        } else {
510            final File sourceFile = new File(mPackageURI.getPath());
511            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
512
513            // Check for parse errors
514            if (parsed == null) {
515                Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
516                showDialogInner(DLG_PACKAGE_ERROR);
517                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
518                return;
519            }
520            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
521                    PackageManager.GET_PERMISSIONS, 0, 0, null,
522                    new PackageUserState());
523            as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
524        }
525
526        //set view
527        setContentView(R.layout.install_start);
528        mInstallConfirm = findViewById(R.id.install_confirm_panel);
529        mInstallConfirm.setVisibility(View.INVISIBLE);
530        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
531
532        mOriginatingUid = getOriginatingUid(intent);
533
534        // Deal with install source.
535        String callerPackage = getCallingPackage();
536        if (callerPackage != null && intent.getBooleanExtra(
537                Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
538            try {
539                mSourceInfo = mPm.getApplicationInfo(callerPackage, 0);
540                if (mSourceInfo != null) {
541                    if ((mSourceInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
542                        // System apps don't need to be approved.
543                        initiateInstall();
544                        return;
545                    }
546                    /* for now this is disabled, since the user would need to
547                     * have enabled the global "unknown sources" setting in the
548                     * first place in order to get here.
549                    SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES,
550                            Context.MODE_PRIVATE);
551                    if (prefs.getBoolean(mSourceInfo.packageName, false)) {
552                        // User has already allowed this one.
553                        initiateInstall();
554                        return;
555                    }
556                    //ask user to enable setting first
557                    showDialogInner(DLG_ALLOW_SOURCE);
558                    return;
559                     */
560                }
561            } catch (NameNotFoundException e) {
562            }
563        }
564
565        // Check unknown sources.
566        if (!isInstallingUnknownAppsAllowed()) {
567            //ask user to enable setting first
568            showDialogInner(DLG_UNKNOWN_APPS);
569            return;
570        }
571        initiateInstall();
572    }
573
574    /** Get the ApplicationInfo for the calling package, if available */
575    private ApplicationInfo getSourceInfo() {
576        String callingPackage = getCallingPackage();
577        if (callingPackage != null) {
578            try {
579                return mPm.getApplicationInfo(callingPackage, 0);
580            } catch (NameNotFoundException ex) {
581                // ignore
582            }
583        }
584        return null;
585    }
586
587
588    /** Get the originating uid if possible, or VerificationParams.NO_UID if not available */
589    private int getOriginatingUid(Intent intent) {
590        // The originating uid from the intent. We only trust/use this if it comes from a
591        // system application
592        int uidFromIntent = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
593                VerificationParams.NO_UID);
594
595        // Get the source info from the calling package, if available. This will be the
596        // definitive calling package, but it only works if the intent was started using
597        // startActivityForResult,
598        ApplicationInfo sourceInfo = getSourceInfo();
599        if (sourceInfo != null) {
600            if (uidFromIntent != VerificationParams.NO_UID &&
601                    (mSourceInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
602                return uidFromIntent;
603
604            }
605            // We either didn't get a uid in the intent, or we don't trust it. Use the
606            // uid of the calling package instead.
607            return sourceInfo.uid;
608        }
609
610        // We couldn't get the specific calling package. Let's get the uid instead
611        int callingUid;
612        try {
613            callingUid = ActivityManagerNative.getDefault()
614                    .getLaunchedFromUid(getActivityToken());
615        } catch (android.os.RemoteException ex) {
616            Log.w(TAG, "Could not determine the launching uid.");
617            // nothing else we can do
618            return VerificationParams.NO_UID;
619        }
620
621        // If we got a uid from the intent, we need to verify that the caller is a
622        // system package before we use it
623        if (uidFromIntent != VerificationParams.NO_UID) {
624            String[] callingPackages = mPm.getPackagesForUid(callingUid);
625            if (callingPackages != null) {
626                for (String packageName: callingPackages) {
627                    try {
628                        ApplicationInfo applicationInfo =
629                                mPm.getApplicationInfo(packageName, 0);
630
631                        if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
632                            return uidFromIntent;
633                        }
634                    } catch (NameNotFoundException ex) {
635                        // ignore it, and try the next package
636                    }
637                }
638            }
639        }
640        // We either didn't get a uid from the intent, or we don't trust it. Use the
641        // calling uid instead.
642        return callingUid;
643    }
644
645    // Generic handling when pressing back key
646    public void onCancel(DialogInterface dialog) {
647        finish();
648    }
649
650    public void onClick(View v) {
651        if(v == mOk) {
652            if (mOkCanInstall || mScrollView == null) {
653                // Start subactivity to actually install the application
654                Intent newIntent = new Intent();
655                newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
656                        mPkgInfo.applicationInfo);
657                newIntent.setData(mPackageURI);
658                newIntent.setClass(this, InstallAppProgress.class);
659                String installerPackageName = getIntent().getStringExtra(
660                        Intent.EXTRA_INSTALLER_PACKAGE_NAME);
661                if (mOriginatingURI != null) {
662                    newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
663                }
664                if (mReferrerURI != null) {
665                    newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
666                }
667                if (mOriginatingUid != VerificationParams.NO_UID) {
668                    newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
669                }
670                if (installerPackageName != null) {
671                    newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
672                            installerPackageName);
673                }
674                if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
675                    newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
676                    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
677                }
678                if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
679                startActivity(newIntent);
680                finish();
681            } else {
682                mScrollView.pageScroll(View.FOCUS_DOWN);
683            }
684        } else if(v == mCancel) {
685            // Cancel and finish
686            setResult(RESULT_CANCELED);
687            finish();
688        }
689    }
690}
691