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