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