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