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.Build; 40import android.os.Bundle; 41import android.os.SystemClock; 42import android.os.UserManager; 43import android.provider.Settings; 44import android.support.v4.view.ViewPager; 45import android.util.Log; 46import android.view.LayoutInflater; 47import android.view.View; 48import android.view.View.OnClickListener; 49import android.view.ViewGroup; 50import android.widget.AppSecurityPermissions; 51import android.widget.Button; 52import android.widget.TabHost; 53import android.widget.TextView; 54 55import java.io.File; 56import 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 */ 68public 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 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 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 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