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);
            }
        }
    };

 

posted @ 2017-06-30 21:10  欧颜柳  阅读(2524)  评论(0编辑  收藏  举报