1 安裝入口PackageInstallerActivity,這個類只是在安裝前做准備。通過各種校驗,然后彈出被安裝應用的權限框,等待用戶安裝。具體的流程如下
1.1 求mSessionId 如果是已經存在的則判斷對應的SessinInfo是否存在,否則默認一個-1
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) { //可能是系統級別的應用安裝時,需要授權走這個流程 final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1); final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); if (info == null || !info.sealed || info.resolvedBaseCodePath == null) { Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring"); finish(); return; } //如果有SessInfo則證明傳過來的sessionId是有效的,並且獲取packageUri mSessionId = sessionId; packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath)); mOriginatingURI = null; mReferrerURI = null; } else { //如果是用戶自己拉起來的安裝,則默認sessionId為-1 病且獲取 packageUri mSessionId = -1; packageUri = intent.getData(); mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); }
1.2 開始做校驗
// 返回URI解析錯誤 -3 if (packageUri == null) { Log.w(TAG, "Unspecified source"); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); finish(); return; } //如果是手表就不支持 if (DeviceUtils.isWear(this)) { showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR); return; }
1. 3 如果是系統應用拉起安裝則直接進入包分析階段
final boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent); if (!requestFromUnknownSource) { //進入packageUri處理階段 processPackageUri(packageUri); return; } //安裝請求是否來自於一個未知的源。 private boolean isInstallRequestFromUnknownSource(Intent intent) { String callerPackage = getCallingPackage(); if (callerPackage != null && intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) { try { mSourceInfo = mPm.getApplicationInfo(callerPackage, 0); if (mSourceInfo != null) { if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { // Privileged apps are not considered an unknown source. //如果安裝請求是來自於一個系統應用,則可以明確源是已知的 return false; } } } catch (NameNotFoundException e) { } } return true;//否則源是未知 }
1. 4 針對管理員賬戶做不同的處理
// If the admin prohibits it, or we're running in a managed profile, just show error // and exit. Otherwise show an option to take the user to Settings to change the setting. // 是否是管理員用戶 final boolean isManagedProfile = mUserManager.isManagedProfile(); if (isUnknownSourcesDisallowed()) { //如果有用戶限制了未知來源應用的安裝 if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) { //如果不是system用戶限制當前用戶安裝未知來源app,啟動設置,使用戶在設置里面修改 showDialogInner(DLG_UNKNOWN_SOURCES); } else { //如果是system用戶限制的,則直接退出 startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)); clearCachedApkIfNeededAndFinish(); } } else if (!isUnknownSourcesEnabled() && isManagedProfile) { //如果不允許安裝未知市場的應用,並且當前是管理員用戶,則彈出"您的管理員不允許安裝來源不明的應用"的對話框 showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES); } else if (!isUnknownSourcesEnabled()) { // Ask user to enable setting first //如果不允許安裝未知市場的應用,則彈出這個對話框修改設置 showDialogInner(DLG_UNKNOWN_SOURCES); } else { //進入packageUri處理階段 processPackageUri(packageUri); }
1. 5 處理文件的uri
//處理包的uri private void processPackageUri(final Uri packageUri) { mPackageURI = packageUri; final String scheme = packageUri.getScheme(); final PackageUtil.AppSnippet as; switch (scheme) { case SCHEME_PACKAGE: try { mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(), PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES); } catch (NameNotFoundException e) { } if (mPkgInfo == null) { Log.w(TAG, "Requested package " + packageUri.getScheme() + " not available. Discontinuing installation"); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK); return; } as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo), mPm.getApplicationIcon(mPkgInfo.applicationInfo)); break; case SCHEME_FILE: File sourceFile = new File(packageUri.getPath()); PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile); // Check for parse errors if (parsed == null) { Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation"); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK); return; } mPkgInfo = PackageParser.generatePackageInfo(parsed, null, PackageManager.GET_PERMISSIONS, 0, 0, null, new PackageUserState()); as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile); break; case SCHEME_CONTENT: //重新復制一個安裝包在解析,返回一個SCHEME_FILE類型的uri重新解析包uri mStagingAsynTask = new StagingAsyncTask(); mStagingAsynTask.execute(packageUri); return; default: Log.w(TAG, "Unsupported scheme " + scheme); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); clearCachedApkIfNeededAndFinish(); return; } PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet); //啟動安裝 initiateInstall(); }
1.6 啟動安裝
//啟動安裝 private void initiateInstall() { String pkgName = mPkgInfo.packageName; // Check if there is already a package on the device with this name // but it has been renamed to something else. 是否有同名應用已經安裝上去了。在此安裝則被認為是替換安裝 String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName }); if (oldName != null && oldName.length > 0 && oldName[0] != null) { pkgName = oldName[0]; mPkgInfo.packageName = pkgName; mPkgInfo.applicationInfo.packageName = pkgName; } // Check if package is already installed. display confirmation dialog if replacing pkg // 檢查這個包是否真的被安裝,如果要替換,則顯示替換對話框 try { // This is a little convoluted because we want to get all uninstalled // apps, but this may include apps with just data, and if it is just // data we still want to count it as "installed". // 獲取設備上有殘存數據,並且標記為“installed”的,實際上已經被卸載的應用。 mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.GET_UNINSTALLED_PACKAGES); if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { ////如果應用是被卸載的,但是又是被標識成安裝過的,則認為是新安裝 mAppInfo = null; } } catch (NameNotFoundException e) { mAppInfo = null; } //列出權限列表,等待用戶確認安裝 startInstallConfirm(); }
1.7 確認安裝權限
private void startInstallConfirm() { TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost); tabHost.setup(); tabHost.setVisibility(View.VISIBLE); ViewPager viewPager = (ViewPager)findViewById(R.id.pager); TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager); // If the app supports runtime permissions the new permissions will // be requested at runtime, hence we do not show them at install. // 如果app支持運行時權限,這里會顯示新的運行時權限 // 根據版本判斷app是否有可能有運行時權限 boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M; boolean permVisible = false; mScrollView = null; mOkCanInstall = false; int msg = 0; //perms這個對象包括了該應用的用戶的uid以及相應的一些權限,以及權限組信息。 AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo); //所有的權限數量 final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL); if (mAppInfo != null) { //如果是替換應用 msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_question_update_system : R.string.install_confirm_question_update; mScrollView = new CaffeinatedScrollView(this); mScrollView.setFillViewport(true); boolean newPermissionsFound = false; if (!supportsRuntimePermissions) { newPermissionsFound = (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0); if (newPermissionsFound) { permVisible = true; //顯示新添加的權限項(這個view竟然是系統的view) mScrollView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_NEW)); } } if (!supportsRuntimePermissions && !newPermissionsFound) { //如果既不支持可運行權限項也沒有新權限發現,則提示沒有新權限 LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); TextView label = (TextView)inflater.inflate(R.layout.label, null); label.setText(R.string.no_new_perms); mScrollView.addView(label); } adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(getText(R.string.newPerms)), mScrollView); } else { findViewById(R.id.tabscontainer).setVisibility(View.GONE); findViewById(R.id.spacer).setVisibility(View.VISIBLE); } if (!supportsRuntimePermissions && N > 0) { permVisible = true; LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); View root = inflater.inflate(R.layout.permissions_list, null); if (mScrollView == null) { mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview); } //如果沒有運行時權限並且有權限,則列出所有權限 ((ViewGroup)root.findViewById(R.id.permission_list)).addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL)); adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(getText(R.string.allPerms)), root); } if (!permVisible) { //如果不需要任何權限。更新的不需要新的權限以及運行時權限 if (mAppInfo != null) { // This is an update to an application, but there are no // permissions at all. msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_question_update_system_no_perms: R.string.install_confirm_question_update_no_perms; findViewById(R.id.spacer).setVisibility(View.VISIBLE); } else { // This is a new application with no permissions. msg = R.string.install_confirm_question_no_perms; } tabHost.setVisibility(View.INVISIBLE); mScrollView = null; } if (msg != 0) { ((TextView)findViewById(R.id.install_confirm_question)).setText(msg); } mInstallConfirm.setVisibility(View.VISIBLE); mOk.setEnabled(true); if (mScrollView == null) { // There is nothing to scroll view, so the ok button is immediately // set to install. mOk.setText(R.string.install); mOkCanInstall = true; } else { mScrollView.setFullScrollAction(new Runnable() { @Override public void run() { mOk.setText(R.string.install); mOkCanInstall = true; } }); } }
1. 8 點擊安裝
public void onClick(View v) { if (v == mOk) { if (mOkCanInstall || mScrollView == null) { if (mSessionId != -1) { //如果原來是確認權限請求則賦予安裝權限退出 mInstaller.setPermissionsResult(mSessionId, true); clearCachedApkIfNeededAndFinish(); } else { //開始安裝 startInstall(); } } else {mScrollView.pageScroll(View.FOCUS_DOWN);} } else if (v == mCancel) { // Cancel and finish 取消安裝 setResult(RESULT_CANCELED); if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, false); } clearCachedApkIfNeededAndFinish(); } }
1.9 進入安裝
private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); //帶走安裝包的applicationInfo newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); //帶走安裝包的applicationInfo newIntent.setClass(this, InstallAppProgress.class); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (mOriginatingURI != null) { //帶走安裝包的mOriginatingURI newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); } if (mReferrerURI != null) {//帶走安裝包的mReferrerURI newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI); } if (mOriginatingUid != VerificationParams.NO_UID) {//帶走安裝包的mOriginatingUid,這個uid如果不是拉安裝的應用的uid newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid); } if (installerPackageName != null) {//帶走安裝包的installerPackageName newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName); } if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); finish(); }
2 安裝過程InstallAppProgress
2.1 注冊安裝監聽
IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BROADCAST_ACTION); registerReceiver(mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
2.2 正式安裝
替換現存的包標示 final int installFlags = getInstallFlags(mAppInfo.packageName);
if ("package".equals(mPackageURI.getScheme())) { try { //安裝與該應用同名的應用,應該比較快,否則會拋出異常 pm.installExistingPackage(mAppInfo.packageName); onPackageInstalled(PackageInstaller.STATUS_SUCCESS); } catch (PackageManager.NameNotFoundException e) { onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID); } } else { final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER); params.originatingUri = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, UID_UNKNOWN); File file = new File(mPackageURI.getPath()); try { //解析安裝包,設置安裝位置。這個安裝位置是從AndroidManifest文件獲取的,至於怎么獲取,最后指向native 層。沒有繼續跟蹤 params.setInstallLocation(PackageParser.parsePackageLite(file, 0).installLocation); } catch (PackageParser.PackageParserException e) { Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults."); } mInstallHandler.post(new Runnable() { @Override public void run() { doPackageStage(pm, params); } }); }
2.3 后台安裝
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) { //初始化安裝器 final PackageInstaller packageInstaller = pm.getPackageInstaller(); PackageInstaller.Session session = null; try { final String packageLocation = mPackageURI.getPath(); final File file = new File(packageLocation); //獲取sessionId final int sessionId = packageInstaller.createSession(params); final byte[] buffer = new byte[65536]; //獲取session session = packageInstaller.openSession(sessionId); final InputStream in = new FileInputStream(file); final long sizeBytes = file.length(); final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes); try { int c; //安裝中.............. while ((c = in.read(buffer)) != -1) { out.write(buffer, 0, c); if (sizeBytes > 0) { final float fraction = ((float) c / (float) sizeBytes); session.addProgress(fraction); } } session.fsync(out); } finally { IoUtils.closeQuietly(in); IoUtils.closeQuietly(out); } // Create a PendingIntent and use it to generate the IntentSender //發起安裝完成提交通知 Intent broadcastIntent = new Intent(BROADCAST_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallAppProgress.this /*context*/, sessionId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); session.commit(pendingIntent.getIntentSender()); } catch (IOException e) { onPackageInstalled(PackageInstaller.STATUS_FAILURE); } finally { IoUtils.closeQuietly(session); } }
2.4 接受安裝結果
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final int statusCode = intent.getIntExtra( PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); //等待安裝 if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) { context.startActivity((Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT)); } else { //返回安裝結果 onPackageInstalled(statusCode); } } };