android 7.0 安装器安装过程分析
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); } } };