Apk安装之——点击安装确认页面的确认按钮,到将安装过程交给PMS的过程分析

mac2024-03-27  32

上篇文章Apk安装之——安装确认页面的初始化,讲解了apk包安装的一个小的阶段,展示安装确认页面的过程。这篇文章,继续分析,点击这个页面的确定按钮,开始安装apk的过程。下面是触发点击事件的具体代码:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java public void onClick(View v) { if (v == mOk) { if (mOk.isEnabled()) { if (mOkCanInstall || mScrollView == null) { if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, true); finish(); } 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); } finish(); } }

点击确认按钮,只要mSessionId的值不是-1,就会执行,startInstall()方法进行apk的安装。

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); //关键代码 newIntent.setClass(this, InstallInstalling.class); ... newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); finish(); }

这个方法内部,会跳转到InstallInstalling这个Activity中,并关闭当前的PackageInstallerActivity页面。下面看看InstallInstalling这个类的onCreate方法:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.install_installing); ApplicationInfo appInfo = getIntent() .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = getIntent().getData(); if ("package".equals(mPackageURI.getScheme())) { try { getPackageManager().installExistingPackage(appInfo.packageName); launchSuccess(); } catch (PackageManager.NameNotFoundException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } else { //关键代码1 final File sourceFile = new File(mPackageURI.getPath()); PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo, sourceFile), R.id.app_snippet); //关键代码2 if (savedInstanceState != null) { mSessionId = savedInstanceState.getInt(SESSION_ID); mInstallId = savedInstanceState.getInt(INSTALL_ID); // Reregister for result; might instantly call back if result was delivered while // activity was destroyed try { //关键代码3 InstallEventReceiver.addObserver(this, mInstallId, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { // Does not happen } } else { //关键代码4 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.installFlags = PackageManager.INSTALL_FULL_APP; 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); params.installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME); File file = new File(mPackageURI.getPath()); try { //关键代码5 PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0); params.setAppPackageName(pkg.packageName); params.setInstallLocation(pkg.installLocation); params.setSize( PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride)); } catch (PackageParser.PackageParserException e) { Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults."); Log.e(LOG_TAG, "Cannot calculate installed size " + file + ". Try only apk size."); params.setSize(file.length()); } catch (IOException e) { Log.e(LOG_TAG, "Cannot calculate installed size " + file + ". Try only apk size."); params.setSize(file.length()); } try { //关键代码6 mInstallId = InstallEventReceiver .addObserver(this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } try { //关键代码7 mSessionId = getPackageManager().getPackageInstaller().createSession(params); } catch (IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } mCancelButton = (Button) findViewById(R.id.cancel_button); mCancelButton.setOnClickListener(view -> { if (mInstallingTask != null) { mInstallingTask.cancel(true); } if (mSessionId > 0) { getPackageManager().getPackageInstaller().abandonSession(mSessionId); mSessionId = 0; } setResult(RESULT_CANCELED); finish(); }); mSessionCallback = new InstallSessionCallback(); } }

这个方法内部,首先根据是content协议还是package协议,来进行不同的处理,本案例是已content协议启动apk的安装流程的,所以会执行关键代码1处的代码,这里会判断savedInstanceState如果不为null,则会取出保存的sessionId和installId,sessionId表示此次安装的会话,installId表示等待安装事件id,在关键代码3处,并给InstallEventReceiver添加一个观察者,回调时,观察者的launchFinishBasedOnResult方法会执行。如果savedInstanceState为null,在关键代码4处,会创建PackageInstaller.SessionParams对象,并给这个对象的各个字段赋值。在关键代码5处,调用PackageParser.parsePackageLite方法,解析apk文件,这里是对apk进行轻量解析,主要是获取apk的包名,apk的位置,以及apk的大小等信息,保存到PackageParser.PackageLite类型的对象pkg中。关键代码6处,也是给InstallEventReceiver添加一个观察者,并返回一个installId,关键代码7处,会创建一个sessionId。以上便是InstallInstalling的onCreate方法的分析。下面接着看这个类的onResume方法:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @Override protected void onResume() { super.onResume(); // This is the first onResume in a single life of the activity if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId); if (sessionInfo != null && !sessionInfo.isActive()) { mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } else { // we will receive a broadcast when the install is finished mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } }

这个方法中,如果sessionInfo不会null,并且sessionInfo是非活动的,则会创建一个InstallingAsyncTask(),并执行这个asynTask。下面看看这个InstallingAsyncTask()具体做了什么:

http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java private final class InstallingAsyncTask extends AsyncTask<Void, Void, PackageInstaller.Session> { volatile boolean isDone; @Override protected PackageInstaller.Session doInBackground(Void... params) { PackageInstaller.Session session; try { session = getPackageManager().getPackageInstaller().openSession(mSessionId); } catch (IOException e) { return null; } session.setStagingProgress(0); try { File file = new File(mPackageURI.getPath()); try (InputStream in = new FileInputStream(file)) { long sizeBytes = file.length(); try (OutputStream out = session .openWrite("PackageInstaller", 0, sizeBytes)) { byte[] buffer = new byte[1024 * 1024]; while (true) { int numRead = in.read(buffer); if (numRead == -1) { session.fsync(out); break; } if (isCancelled()) { session.close(); break; } out.write(buffer, 0, numRead); if (sizeBytes > 0) { float fraction = ((float) numRead / (float) sizeBytes); session.addProgress(fraction); } } } } return session; } catch (IOException | SecurityException e) { Log.e(LOG_TAG, "Could not write package", e); session.close(); return null; } finally { synchronized (this) { isDone = true; notifyAll(); } } } @Override protected void onPostExecute(PackageInstaller.Session session) { if (session != null) { Intent broadcastIntent = new Intent(BROADCAST_ACTION); broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntent.setPackage( getPackageManager().getPermissionControllerPackageName()); broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallInstalling.this, mInstallId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); //关键代码 session.commit(pendingIntent.getIntentSender()); mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } else { getPackageManager().getPackageInstaller().abandonSession(mSessionId); if (!isCancelled()) { launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null); } } } }

这个InstallingAsyncTask的doInBackground方法内部,将apk文件写入到了PackageInstaller.Session类型的对象session中,在onPostExecute方法中,执行PackageInstaller.Session的commit方法。

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageInstaller.java public void commit(@NonNull IntentSender statusReceiver) { try { mSession.commit(statusReceiver, false); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }

这个方法内部会调用mSession的commit方法,mSession是IPackageInstallerSession类型的,IPackageInstallerSession其实是system_servier进程的PackageInstallerSession在客户端的一个代理对象。mSession。commit方法,其实就是发起了进程间的通信,system_servier进程的PackageInstallerSession的commit方法会执行这个请求。下面看看这个方法的具体实现:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java @Override public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { Preconditions.checkNotNull(statusReceiver); final boolean wasSealed; synchronized (mLock) { ... mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } ... } private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { ... case MSG_COMMIT: synchronized (mLock) { try { //关键代码 commitLocked(); } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); destroyInternal(); dispatchSessionFinished(e.error, completeMsg, null); } } break; case MSG_ON_PACKAGE_INSTALLED: ... break; } return true; } }

这个方法内部,会通过handler发送一个MSG_COMMIT消息,使得handler中的commitLocked()方法得到执行。下面看看这个方法:

@GuardedBy("mLock") private void commitLocked()throws PackageManagerException { ... // We've reached point of no return; call into PMS to install the stage. // Regardless of success or failure we always destroy session. final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) { throw new IllegalStateException(); } @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { destroyInternal(); dispatchSessionFinished(returnCode, msg, extras); } }; final UserHandle user; if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(userId); } mRelinquished = true; //关键代码 mPm.installStage(mPackageName, stageDir, localObserver, params, mInstallerPackageName, mInstallerUid, user, mSigningDetails); }

这个方法内部,主要是执行mPm的installStage方法,前面的很多代码都是为这个方法的创建很多的参数。这个mPm是PackageManagerService类型的,其实这里的安装,交给了PackageManagerService来完成。

总结:点击安装确认页面的 确定按钮后,会创建安装会话id,和准备安装的事件id,接着会将apk文件读取到PackageInstaller.Session类型的对象中,在调用这PackageInstaller.Session类型的对象的commit方法,将具体的安装交给PMS。

参考:Android包管理机制(二)PackageInstaller安装APK

最新回复(0)