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.ManifestDigest;
30import android.content.pm.PackageInfo;
31import android.content.pm.PackageInstaller;
32import android.content.pm.PackageManager;
33import android.content.pm.PackageManager.NameNotFoundException;
34import android.content.pm.PackageParser;
35import android.content.pm.PackageUserState;
36import android.content.pm.ResolveInfo;
37import android.content.pm.VerificationParams;
38import android.net.Uri;
39import android.os.Bundle;
40import android.os.SystemClock;
41import android.os.UserManager;
42import android.provider.Settings;
43import android.support.v4.view.ViewPager;
44import android.util.Log;
45import android.view.LayoutInflater;
46import android.view.View;
47import android.view.View.OnClickListener;
48import android.view.ViewGroup;
49import android.widget.AppSecurityPermissions;
50import android.widget.Button;
51import android.widget.TabHost;
52import android.widget.TextView;
53
54import java.io.File;
55import java.util.List;
56
57/*
58 * This activity is launched when a new application is installed via side loading
59 * The package is first parsed and the user is notified of parse errors via a dialog.
60 * If the package is successfully parsed, the user is notified to turn on the install unknown
61 * applications setting. A memory check is made at this point and the user is notified of out
62 * of memory conditions if any. If the package is already existing on the device,
63 * a confirmation dialog (to replace the existing package) is presented to the user.
64 * Based on the user response the package is then installed by launching InstallAppConfirm
65 * sub activity. All state transitions are handled in this activity
66 */
67public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
68    private static final String TAG = "PackageInstaller";
69
70    private int mSessionId = -1;
71    private Uri mPackageURI;
72    private Uri mOriginatingURI;
73    private Uri mReferrerURI;
74    private int mOriginatingUid = VerificationParams.NO_UID;
75    private ManifestDigest mPkgDigest;
76
77    private boolean localLOGV = false;
78    PackageManager mPm;
79    UserManager mUserManager;
80    PackageInstaller mInstaller;
81    PackageInfo mPkgInfo;
82    ApplicationInfo mSourceInfo;
83
84    // ApplicationInfo object primarily used for already existing applications
85    private ApplicationInfo mAppInfo = null;
86
87    private InstallFlowAnalytics mInstallFlowAnalytics;
88
89    // View for install progress
90    View mInstallConfirm;
91    // Buttons to indicate user acceptance
92    private Button mOk;
93    private Button mCancel;
94    CaffeinatedScrollView mScrollView = null;
95    private boolean mOkCanInstall = false;
96
97    static final String PREFS_ALLOWED_SOURCES = "allowed_sources";
98
99    private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
100
101    private static final String TAB_ID_ALL = "all";
102    private static final String TAB_ID_NEW = "new";
103
104    // Dialog identifiers used in showDialog
105    private static final int DLG_BASE = 0;
106    private static final int DLG_UNKNOWN_SOURCES = DLG_BASE + 1;
107    private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
108    private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
109    private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
110    private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;
111    private static final int DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES = DLG_BASE + 6;
112
113    private void startInstallConfirm() {
114        TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
115        tabHost.setup();
116        ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
117        TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
118        adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
119            @Override
120            public void onTabChanged(String tabId) {
121                if (TAB_ID_ALL.equals(tabId)) {
122                    mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
123                } else if (TAB_ID_NEW.equals(tabId)) {
124                    mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
125                }
126            }
127        });
128
129        boolean permVisible = false;
130        mScrollView = null;
131        mOkCanInstall = false;
132        int msg = 0;
133        if (mPkgInfo != null) {
134            AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
135            final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
136            final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
137            if (mAppInfo != null) {
138                msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
139                        ? R.string.install_confirm_question_update_system
140                        : R.string.install_confirm_question_update;
141                mScrollView = new CaffeinatedScrollView(this);
142                mScrollView.setFillViewport(true);
143                boolean newPermissionsFound =
144                        (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
145                mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
146                if (newPermissionsFound) {
147                    permVisible = true;
148                    mScrollView.addView(perms.getPermissionsView(
149                            AppSecurityPermissions.WHICH_NEW));
150                } else {
151                    LayoutInflater inflater = (LayoutInflater)getSystemService(
152                            Context.LAYOUT_INFLATER_SERVICE);
153                    TextView label = (TextView)inflater.inflate(R.layout.label, null);
154                    label.setText(R.string.no_new_perms);
155                    mScrollView.addView(label);
156                }
157                adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
158                        getText(R.string.newPerms)), mScrollView);
159            } else  {
160                findViewById(R.id.tabscontainer).setVisibility(View.GONE);
161                findViewById(R.id.divider).setVisibility(View.VISIBLE);
162            }
163            if (NP > 0 || ND > 0) {
164                permVisible = true;
165                LayoutInflater inflater = (LayoutInflater)getSystemService(
166                        Context.LAYOUT_INFLATER_SERVICE);
167                View root = inflater.inflate(R.layout.permissions_list, null);
168                if (mScrollView == null) {
169                    mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
170                }
171                if (NP > 0) {
172                    ((ViewGroup)root.findViewById(R.id.privacylist)).addView(
173                            perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
174                } else {
175                    root.findViewById(R.id.privacylist).setVisibility(View.GONE);
176                }
177                if (ND > 0) {
178                    ((ViewGroup)root.findViewById(R.id.devicelist)).addView(
179                            perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
180                } else {
181                    root.findViewById(R.id.devicelist).setVisibility(View.GONE);
182                }
183                adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
184                        getText(R.string.allPerms)), root);
185            }
186        }
187        mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
188        if (!permVisible) {
189            if (mAppInfo != null) {
190                // This is an update to an application, but there are no
191                // permissions at all.
192                msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
193                        ? R.string.install_confirm_question_update_system_no_perms
194                        : R.string.install_confirm_question_update_no_perms;
195            } else {
196                // This is a new application with no permissions.
197                msg = R.string.install_confirm_question_no_perms;
198            }
199            tabHost.setVisibility(View.GONE);
200            mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
201            mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
202            findViewById(R.id.filler).setVisibility(View.VISIBLE);
203            findViewById(R.id.divider).setVisibility(View.GONE);
204            mScrollView = null;
205        }
206        if (msg != 0) {
207            ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
208        }
209        mInstallConfirm.setVisibility(View.VISIBLE);
210        mOk = (Button)findViewById(R.id.ok_button);
211        mCancel = (Button)findViewById(R.id.cancel_button);
212        mOk.setOnClickListener(this);
213        mCancel.setOnClickListener(this);
214        if (mScrollView == null) {
215            // There is nothing to scroll view, so the ok button is immediately
216            // set to install.
217            mOk.setText(R.string.install);
218            mOkCanInstall = true;
219        } else {
220            mScrollView.setFullScrollAction(new Runnable() {
221                @Override
222                public void run() {
223                    mOk.setText(R.string.install);
224                    mOkCanInstall = true;
225                }
226            });
227        }
228    }
229
230    private void showDialogInner(int id) {
231        // TODO better fix for this? Remove dialog so that it gets created again
232        removeDialog(id);
233        showDialog(id);
234    }
235
236    @Override
237    public Dialog onCreateDialog(int id, Bundle bundle) {
238        switch (id) {
239        case DLG_UNKNOWN_SOURCES:
240            return new AlertDialog.Builder(this)
241                    .setTitle(R.string.unknown_apps_dlg_title)
242                    .setMessage(R.string.unknown_apps_dlg_text)
243                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
244                        public void onClick(DialogInterface dialog, int which) {
245                            Log.i(TAG, "Finishing off activity so that user can navigate to settings manually");
246                            finish();
247                        }})
248                    .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
249                        public void onClick(DialogInterface dialog, int which) {
250                            Log.i(TAG, "Launching settings");
251                            launchSettingsAppAndFinish();
252                        }
253                    })
254                    .setOnCancelListener(this)
255                    .create();
256        case DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES:
257            return new AlertDialog.Builder(this)
258                    .setTitle(R.string.unknown_apps_dlg_title)
259                    .setMessage(R.string.unknown_apps_admin_dlg_text)
260                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
261                        public void onClick(DialogInterface dialog, int which) {
262                            finish();
263                        }
264                    })
265                    .setOnCancelListener(this)
266                    .create();
267        case DLG_PACKAGE_ERROR :
268            return new AlertDialog.Builder(this)
269                    .setTitle(R.string.Parse_error_dlg_title)
270                    .setMessage(R.string.Parse_error_dlg_text)
271                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
272                        public void onClick(DialogInterface dialog, int which) {
273                            finish();
274                        }
275                    })
276                    .setOnCancelListener(this)
277                    .create();
278        case DLG_OUT_OF_SPACE:
279            // Guaranteed not to be null. will default to package name if not set by app
280            CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
281            String dlgText = getString(R.string.out_of_space_dlg_text,
282                    appTitle.toString());
283            return new AlertDialog.Builder(this)
284                    .setTitle(R.string.out_of_space_dlg_title)
285                    .setMessage(dlgText)
286                    .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
287                        public void onClick(DialogInterface dialog, int which) {
288                            //launch manage applications
289                            Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
290                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
291                            startActivity(intent);
292                            finish();
293                        }
294                    })
295                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
296                        public void onClick(DialogInterface dialog, int which) {
297                            Log.i(TAG, "Canceling installation");
298                            finish();
299                        }
300                  })
301                  .setOnCancelListener(this)
302                  .create();
303        case DLG_INSTALL_ERROR :
304            // Guaranteed not to be null. will default to package name if not set by app
305            CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
306            String dlgText1 = getString(R.string.install_failed_msg,
307                    appTitle1.toString());
308            return new AlertDialog.Builder(this)
309                    .setTitle(R.string.install_failed)
310                    .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
311                        public void onClick(DialogInterface dialog, int which) {
312                            finish();
313                        }
314                    })
315                    .setMessage(dlgText1)
316                    .setOnCancelListener(this)
317                    .create();
318        case DLG_ALLOW_SOURCE:
319            CharSequence appTitle2 = mPm.getApplicationLabel(mSourceInfo);
320            String dlgText2 = getString(R.string.allow_source_dlg_text,
321                    appTitle2.toString());
322            return new AlertDialog.Builder(this)
323                    .setTitle(R.string.allow_source_dlg_title)
324                    .setMessage(dlgText2)
325                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
326                        public void onClick(DialogInterface dialog, int which) {
327                            setResult(RESULT_CANCELED);
328                            finish();
329                        }})
330                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
331                        public void onClick(DialogInterface dialog, int which) {
332                            SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES,
333                                    Context.MODE_PRIVATE);
334                            prefs.edit().putBoolean(mSourceInfo.packageName, true).apply();
335                            startInstallConfirm();
336                        }
337                    })
338                    .setOnCancelListener(this)
339                    .create();
340       }
341       return null;
342   }
343
344    private void launchSettingsAppAndFinish() {
345        // Create an intent to launch SettingsTwo activity
346        Intent launchSettingsIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
347        launchSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
348        startActivity(launchSettingsIntent);
349        finish();
350    }
351
352    private boolean isInstallRequestFromUnknownSource(Intent intent) {
353        String callerPackage = getCallingPackage();
354        if (callerPackage != null && intent.getBooleanExtra(
355                Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
356            try {
357                mSourceInfo = mPm.getApplicationInfo(callerPackage, 0);
358                if (mSourceInfo != null) {
359                    if ((mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
360                        // Privileged apps are not considered an unknown source.
361                        return false;
362                    }
363                }
364            } catch (NameNotFoundException e) {
365            }
366        }
367
368        return true;
369    }
370
371    private boolean isVerifyAppsEnabled() {
372        if (mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS)) {
373            return true;
374        }
375        return Settings.Global.getInt(getContentResolver(),
376                Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) > 0;
377    }
378
379    private boolean isAppVerifierInstalled() {
380        final PackageManager pm = getPackageManager();
381        final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
382        verification.setType(PACKAGE_MIME_TYPE);
383        verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
384        final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0);
385        return (receivers.size() > 0) ? true : false;
386    }
387
388    /**
389     * @return whether unknown sources is enabled by user in Settings
390     */
391    private boolean isUnknownSourcesEnabled() {
392        return Settings.Secure.getInt(getContentResolver(),
393                Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
394    }
395
396    /**
397     * @return whether the device admin restricts installation from unknown sources
398     */
399    private boolean isUnknownSourcesAllowedByAdmin() {
400        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
401    }
402
403    private void initiateInstall() {
404        String pkgName = mPkgInfo.packageName;
405        // Check if there is already a package on the device with this name
406        // but it has been renamed to something else.
407        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
408        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
409            pkgName = oldName[0];
410            mPkgInfo.packageName = pkgName;
411            mPkgInfo.applicationInfo.packageName = pkgName;
412        }
413        // Check if package is already installed. display confirmation dialog if replacing pkg
414        try {
415            // This is a little convoluted because we want to get all uninstalled
416            // apps, but this may include apps with just data, and if it is just
417            // data we still want to count it as "installed".
418            mAppInfo = mPm.getApplicationInfo(pkgName,
419                    PackageManager.GET_UNINSTALLED_PACKAGES);
420            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
421                mAppInfo = null;
422            }
423        } catch (NameNotFoundException e) {
424            mAppInfo = null;
425        }
426
427        mInstallFlowAnalytics.setReplace(mAppInfo != null);
428        mInstallFlowAnalytics.setSystemApp(
429                (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
430
431        startInstallConfirm();
432    }
433
434    void setPmResult(int pmResult) {
435        Intent result = new Intent();
436        result.putExtra(Intent.EXTRA_INSTALL_RESULT, pmResult);
437        setResult(pmResult == PackageManager.INSTALL_SUCCEEDED
438                ? RESULT_OK : RESULT_FIRST_USER, result);
439    }
440
441    @Override
442    protected void onCreate(Bundle icicle) {
443        super.onCreate(icicle);
444
445        mPm = getPackageManager();
446        mInstaller = mPm.getPackageInstaller();
447        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
448
449        final Intent intent = getIntent();
450        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
451            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
452            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
453            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
454                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
455                finish();
456                return;
457            }
458
459            mSessionId = sessionId;
460            mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
461            mOriginatingURI = null;
462            mReferrerURI = null;
463        } else {
464            mSessionId = -1;
465            mPackageURI = intent.getData();
466            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
467            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
468        }
469
470        final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
471        final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
472
473        boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
474        mInstallFlowAnalytics = new InstallFlowAnalytics();
475        mInstallFlowAnalytics.setContext(this);
476        mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
477        mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
478                && unknownSourcesAllowedByUser);
479        mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
480        mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
481        mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
482        mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
483
484        final String scheme = mPackageURI.getScheme();
485        if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
486            Log.w(TAG, "Unsupported scheme " + scheme);
487            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
488            mInstallFlowAnalytics.setFlowFinished(
489                    InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
490            finish();
491            return;
492        }
493
494        final PackageUtil.AppSnippet as;
495        if ("package".equals(mPackageURI.getScheme())) {
496            mInstallFlowAnalytics.setFileUri(false);
497            try {
498                mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
499                        PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
500            } catch (NameNotFoundException e) {
501            }
502            if (mPkgInfo == null) {
503                Log.w(TAG, "Requested package " + mPackageURI.getScheme()
504                        + " not available. Discontinuing installation");
505                showDialogInner(DLG_PACKAGE_ERROR);
506                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
507                mInstallFlowAnalytics.setPackageInfoObtained();
508                mInstallFlowAnalytics.setFlowFinished(
509                        InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
510                return;
511            }
512            as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
513                    mPm.getApplicationIcon(mPkgInfo.applicationInfo));
514        } else {
515            mInstallFlowAnalytics.setFileUri(true);
516            final File sourceFile = new File(mPackageURI.getPath());
517            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
518
519            // Check for parse errors
520            if (parsed == null) {
521                Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
522                showDialogInner(DLG_PACKAGE_ERROR);
523                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
524                mInstallFlowAnalytics.setPackageInfoObtained();
525                mInstallFlowAnalytics.setFlowFinished(
526                        InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
527                return;
528            }
529            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
530                    PackageManager.GET_PERMISSIONS, 0, 0, null,
531                    new PackageUserState());
532            mPkgDigest = parsed.manifestDigest;
533            as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
534        }
535        mInstallFlowAnalytics.setPackageInfoObtained();
536
537        //set view
538        setContentView(R.layout.install_start);
539        mInstallConfirm = findViewById(R.id.install_confirm_panel);
540        mInstallConfirm.setVisibility(View.INVISIBLE);
541        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
542
543        mOriginatingUid = getOriginatingUid(intent);
544
545        // Block the install attempt on the Unknown Sources setting if necessary.
546        if (!requestFromUnknownSource) {
547            initiateInstall();
548            return;
549        }
550
551        // If the admin prohibits it, or we're running in a managed profile, just show error
552        // and exit. Otherwise show an option to take the user to Settings to change the setting.
553        final boolean isManagedProfile = mUserManager.isManagedProfile();
554        if (!unknownSourcesAllowedByAdmin
555                || (!unknownSourcesAllowedByUser && isManagedProfile)) {
556            showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
557            mInstallFlowAnalytics.setFlowFinished(
558                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
559        } else if (!unknownSourcesAllowedByUser) {
560            // Ask user to enable setting first
561            showDialogInner(DLG_UNKNOWN_SOURCES);
562            mInstallFlowAnalytics.setFlowFinished(
563                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
564        } else {
565            initiateInstall();
566        }
567    }
568
569    /** Get the ApplicationInfo for the calling package, if available */
570    private ApplicationInfo getSourceInfo() {
571        String callingPackage = getCallingPackage();
572        if (callingPackage != null) {
573            try {
574                return mPm.getApplicationInfo(callingPackage, 0);
575            } catch (NameNotFoundException ex) {
576                // ignore
577            }
578        }
579        return null;
580    }
581
582
583    /** Get the originating uid if possible, or VerificationParams.NO_UID if not available */
584    private int getOriginatingUid(Intent intent) {
585        // The originating uid from the intent. We only trust/use this if it comes from a
586        // system application
587        int uidFromIntent = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
588                VerificationParams.NO_UID);
589
590        // Get the source info from the calling package, if available. This will be the
591        // definitive calling package, but it only works if the intent was started using
592        // startActivityForResult,
593        ApplicationInfo sourceInfo = getSourceInfo();
594        if (sourceInfo != null) {
595            if (uidFromIntent != VerificationParams.NO_UID &&
596                    (mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
597                return uidFromIntent;
598
599            }
600            // We either didn't get a uid in the intent, or we don't trust it. Use the
601            // uid of the calling package instead.
602            return sourceInfo.uid;
603        }
604
605        // We couldn't get the specific calling package. Let's get the uid instead
606        int callingUid;
607        try {
608            callingUid = ActivityManagerNative.getDefault()
609                    .getLaunchedFromUid(getActivityToken());
610        } catch (android.os.RemoteException ex) {
611            Log.w(TAG, "Could not determine the launching uid.");
612            // nothing else we can do
613            return VerificationParams.NO_UID;
614        }
615
616        // If we got a uid from the intent, we need to verify that the caller is a
617        // privileged system package before we use it
618        if (uidFromIntent != VerificationParams.NO_UID) {
619            String[] callingPackages = mPm.getPackagesForUid(callingUid);
620            if (callingPackages != null) {
621                for (String packageName: callingPackages) {
622                    try {
623                        ApplicationInfo applicationInfo =
624                                mPm.getApplicationInfo(packageName, 0);
625
626                        if ((applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
627                            return uidFromIntent;
628                        }
629                    } catch (NameNotFoundException ex) {
630                        // ignore it, and try the next package
631                    }
632                }
633            }
634        }
635        // We either didn't get a uid from the intent, or we don't trust it. Use the
636        // calling uid instead.
637        return callingUid;
638    }
639
640    @Override
641    public void onBackPressed() {
642        if (mSessionId != -1) {
643            mInstaller.setPermissionsResult(mSessionId, false);
644        }
645        mInstallFlowAnalytics.setFlowFinished(
646                InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
647        super.onBackPressed();
648    }
649
650    // Generic handling when pressing back key
651    public void onCancel(DialogInterface dialog) {
652        finish();
653    }
654
655    public void onClick(View v) {
656        if (v == mOk) {
657            if (mOkCanInstall || mScrollView == null) {
658                mInstallFlowAnalytics.setInstallButtonClicked();
659                if (mSessionId != -1) {
660                    mInstaller.setPermissionsResult(mSessionId, true);
661
662                    // We're only confirming permissions, so we don't really know how the
663                    // story ends; assume success.
664                    mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
665                            PackageManager.INSTALL_SUCCEEDED);
666                } else {
667                    // Start subactivity to actually install the application
668                    Intent newIntent = new Intent();
669                    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
670                            mPkgInfo.applicationInfo);
671                    newIntent.setData(mPackageURI);
672                    newIntent.setClass(this, InstallAppProgress.class);
673                    newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
674                    newIntent.putExtra(
675                            InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
676                    String installerPackageName = getIntent().getStringExtra(
677                            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
678                    if (mOriginatingURI != null) {
679                        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
680                    }
681                    if (mReferrerURI != null) {
682                        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
683                    }
684                    if (mOriginatingUid != VerificationParams.NO_UID) {
685                        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
686                    }
687                    if (installerPackageName != null) {
688                        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
689                                installerPackageName);
690                    }
691                    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
692                        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
693                        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
694                    }
695                    if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
696                    startActivity(newIntent);
697                }
698                finish();
699            } else {
700                mScrollView.pageScroll(View.FOCUS_DOWN);
701            }
702        } else if(v == mCancel) {
703            // Cancel and finish
704            setResult(RESULT_CANCELED);
705            if (mSessionId != -1) {
706                mInstaller.setPermissionsResult(mSessionId, false);
707            }
708            mInstallFlowAnalytics.setFlowFinished(
709                    InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
710            finish();
711        }
712    }
713}
714