前言

在本系列上一篇文章Android9.0 PM机制系列(一)PackageInstaller初始化解析中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInstaller是如何安装APK的。本系列文章的源码基于Android9.0。

1.PackageInstaller中的处理

紧接着上一篇的内容,在PackageInstallerActivity调用startInstallConfirm方法初始化安装确认界面后,这个安装确认界面就会呈现给用户,用户如果想要安装这个应用程序就会点击确定按钮,就会调用PackageInstallerActivity的onClick方法,如下所示。
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) { //初始化时mSessionId = -1mInstaller.setPermissionsResult(mSessionId, true);finish();} else {startInstall();//1}} else {mScrollView.pageScroll(View.FOCUS_DOWN);}}} else if (v == mCancel) {...finish();}
}

onClick方法中分别对确定和取消按钮做处理,主要查看对确定按钮的处理,注释1处调用了startInstall方法:

private void startInstall() {Intent newIntent = new Intent();newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,mPkgInfo.applicationInfo);newIntent.setData(mPackageURI);//1newIntent.setClass(this, InstallInstalling.class);String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);if (mOriginatingURI != null) {newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);}...if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);startActivity(newIntent);finish();}

startInstall方法用于跳转到InstallInstalling这个Activity,并关闭掉当前的PackageInstallerActivity。InstallInstalling主要用于向包管理器发送包的信息并处理包管理的回调。 InstallInstalling的onCreate方法如下所示。
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

@Overrideprotected 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 {//根据mPackageURI创建一个对应的File final File sourceFile = new File(mPackageURI.getPath());PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,sourceFile), R.id.app_snippet);//如果savedInstanceState不为null,获取此前保存的mSessionId和mInstallId       if (savedInstanceState != null) {//1mSessionId = savedInstanceState.getInt(SESSION_ID);mInstallId = savedInstanceState.getInt(INSTALL_ID);//向InstallEventReceiver注册一个观察者try {InstallEventReceiver.addObserver(this, mInstallId,this::launchFinishBasedOnResult);//2} catch (EventResultPersister.OutOfIdsException e) {}} else {PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);//3params.installFlags = PackageManager.INSTALL_FULL_APP; //9.0新增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);//9.0新增File file = new File(mPackageURI.getPath());//4try {PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);//5params.setAppPackageName(pkg.packageName);params.setInstallLocation(pkg.installLocation);params.setSize(PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));} catch (PackageParser.PackageParserException e) {...}try {mInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);//6} catch (EventResultPersister.OutOfIdsException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}try {mSessionId = getPackageManager().getPackageInstaller().createSession(params);//7} catch (IOException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}}...mSessionCallback = new InstallSessionCallback();}}

onCreate方法中会分别对package和content协议的Uri进行处理,我们来看content协议的Uri处理部分。

  1. 注释1处如果savedInstanceState不为null,获取此前保存的mSessionId和mInstallId,其中mSessionId是安装包的会话id,mInstallId是等待的安装事件id。
  2. 注释2处根据mInstallId向InstallEventReceiver注册一个观察者,launchFinishBasedOnResult会接收到安装事件的回调,无论安装成功或者失败都会关闭当前的Activity(InstallInstalling)。如果savedInstanceState为null,代码的逻辑也是类似的。
  3. 注释3处创建SessionParams,它用来代表安装会话的参数。
  4. 注释4、5处根据mPackageUri对包(APK)进行轻量级的解析,并将解析的参数赋值给SessionParams。
  5. 注释6处和注释2处类似向InstallEventReceiver注册一个观察者返回一个新的mInstallId,其中InstallEventReceiver继承自BroadcastReceiver,用于接收安装事件并回调给EventResultPersister。
  6. 注释7处PackageInstaller的createSession方法内部会通过IPackageInstaller与PackageInstallerService进行进程间通信,最终调用的是PackageInstallerService的createSession方法来创建并返回mSessionId。
  7. 只要mInstallId 或者mSessionId 创建失败就会调出安装失败界面。

InstallInstalling的onCreate方法就分析到这,接着查看InstallInstalling的onResume方法:
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

@Overrideprotected void onResume() {super.onResume();if (mInstallingTask == null) {PackageInstaller installer = getPackageManager().getPackageInstaller();PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);//1if (sessionInfo != null && !sessionInfo.isActive()) {//2mInstallingTask = new InstallingAsyncTask();mInstallingTask.execute();} else {mCancelButton.setEnabled(false);setFinishOnTouchOutside(false);}}}
  1. 注释1处根据mSessionId得到SessionInfo,SessionInfo代表安装会话的详细信息。
  2. 注释2处如果sessionInfo不为Null并且不是活动的,就创建并执行InstallingAsyncTask。InstallingAsyncTask的doInBackground方法中会根据包(APK)的Uri,将APK的信息通过IO流的形式写入到PackageInstaller.Session中。InstallingAsyncTask的onPostExecute方法如下所示。

packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

 private final class InstallingAsyncTask extends AsyncTask<Void, Void,
327            PackageInstaller.Session> {328        volatile boolean isDone;
329
330        @Override
331        protected PackageInstaller.Session doInBackground(Void... params) {332            PackageInstaller.Session session;
333            try {334                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
335            } catch (IOException e) {336                return null;
337            }
338
339            session.setStagingProgress(0);
340
341            try {342                File file = new File(mPackageURI.getPath());
343
344                try (InputStream in = new FileInputStream(file)) {345                    long sizeBytes = file.length();
346                    try (OutputStream out = session
347                            .openWrite("PackageInstaller", 0, sizeBytes)) {//8.0 是 byte[] buffer = new byte[4096];
348                        byte[] buffer = new byte[1024 * 1024];
349                        while (true) {350                            int numRead = in.read(buffer);
351
352                            if (numRead == -1) {353                                session.fsync(out);
354                                break;
355                            }
356
357                            if (isCancelled()) {358                                session.close();
359                                break;
360                            }
361
362                            out.write(buffer, 0, numRead);
363                            if (sizeBytes > 0) {364                                float fraction = ((float) numRead / (float) sizeBytes);
365                                session.addProgress(fraction);
366                            }
367                        }
368                    }
369                }
370
371                return session;
372            } catch (IOException | SecurityException e) {373                Log.e(LOG_TAG, "Could not write package", e);
374
375                session.close();
376
377                return null;
378            } finally {379                synchronized (this) {380                    isDone = true;
381                    notifyAll();
382                }
383            }
384        }
385
386        @Override
387        protected void onPostExecute(PackageInstaller.Session session) {388            if (session != null) {389                Intent broadcastIntent = new Intent(BROADCAST_ACTION);/*9.0新增这行,AMS构造方法中构造了两个队列,一个前台队列,一个后台队列。结合其构造方法可知,两者区别在于一个是设置的广播超时时间不同,前台是10s,后台是60s,另外一个是是否要等待后台服务处理完,前台广播是不用等待的,后台广播需要等待。想要让广播能放到前台队列中,只需调用Intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)即可*/
390                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
391                broadcastIntent.setPackage(
392                        getPackageManager().getPermissionControllerPackageName());
393                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
394
395                PendingIntent pendingIntent = PendingIntent.getBroadcast(
396                        InstallInstalling.this,
397                        mInstallId,
398                        broadcastIntent,
399                        PendingIntent.FLAG_UPDATE_CURRENT);
400
401                session.commit(pendingIntent.getIntentSender());
402                mCancelButton.setEnabled(false);
403                setFinishOnTouchOutside(false);
404            } else {405                getPackageManager().getPackageInstaller().abandonSession(mSessionId);
406
407                if (!isCancelled()) {408                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
409                }
410            }
411        }
412    }```
创建了一个PendingIntent,并将该PendingIntent的IntentSender通过注释1处的PackageInstaller.Session的commit方法发送出去,发送去哪了呢?接着查看PackageInstaller.Session的commit方法。
**frameworks/base/core/java/android/content/pm/PackageInstaller.java**```java
public void commit(@NonNull IntentSender statusReceiver) {try {mSession.commit(statusReceiver);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

mSession的类型为IPackageInstallerSession,这说明要通过IPackageInstallerSession来进行进程间的通信,最终会调用PackageInstallerSession的commit方法,这样代码逻辑就到了Java框架层的。

2.Java框架层的处理

frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

@Override
public void commit(IntentSender statusReceiver) {Preconditions.checkNotNull(statusReceiver);...mActiveCount.incrementAndGet();final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);/*8.0 : mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();在Handler处理的时候才对mRemoteObserver进行赋值9.0 直接在这里对mRemoteObserver进行赋值*/mRemoteObserver = adapter.getBinder();if (!mSealed) {try {//验证apk有效性,并给pkgInfo appInfo 赋值等,8.0是放到Handler处理的sealAndValidateLocked();} catch (IOException e) {...}}mHandler.obtainMessage(MSG_COMMIT).sendToTarget();//1
}

commit方法中会将包的信息封装为PackageInstallObserverAdapter ,它在PMS中被定义。在注释1处会向Handler发送一个类型为MSG_COMMIT的消息,其中adapter.getBinder()会得到IPackageInstallObserver2.Stub类型的观察者,从类型就知道这个观察者是可以跨进程进行回调的。处理该消息的代码如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

private final Handler.Callback mHandlerCallback = new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case MSG_COMMIT:synchronized (mLock) {try {commitLocked();//1} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);destroyInternal();dispatchSessionFinished(e.error, completeMsg, null);//2}}break;case MSG_ON_PACKAGE_INSTALLED:final SomeArgs args = (SomeArgs) msg.obj;final String packageName = (String) args.arg1;final String message = (String) args.arg2;final Bundle extras = (Bundle) args.arg3;final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;final int returnCode = args.argi1;args.recycle();try {observer.onPackageInstalled(packageName, returnCode, message, extras);} catch (RemoteException ignored) {}break;}};

注释1处的commitLocked方法如下所示。

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() {@Overridepublic void onUserActionRequired(Intent intent) {throw new IllegalStateException();}@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {destroyInternal();//安装完毕后的回调,很多地方都会调这个方法!然后会一直回调到最开始处,然后发广播给客户端dispatchSessionFinished(returnCode, msg, extras);//2}};mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,installerPackageName, installerUid, user, mCertificates);}

commitLocked方法比较长,这里截取最主要的信息,会调用PMS的installStage方法,这样代码逻辑就进入了PMS中。
在Android 9.0 PM机制系列(三)PMS处理APK的安装文章中说到,如果安装完成,就会调用到注释2处。
同样,回到mHandlerCallback的handleMessage方法,如果commitLocked方法出现PackageManagerException异常,就会调用注释2处的dispatchSessionFinished方法。
同理,它的实现如下所示:
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {1546        final IPackageInstallObserver2 observer;
1547        final String packageName;
1548        synchronized (mLock) {1549            mFinalStatus = returnCode;
1550            mFinalMessage = msg;
1551
1552            observer = mRemoteObserver;
1553            packageName = mPackageName;
1554        }
1555
1556        if (observer != null) {1557            // Execute observer.onPackageInstalled on different tread as we don't want callers
1558            // inside the system server have to worry about catching the callbacks while they are
1559            // calling into the session
1560            final SomeArgs args = SomeArgs.obtain();
1561            args.arg1 = packageName;
1562            args.arg2 = msg;
1563            args.arg3 = extras;
1564            args.arg4 = observer;
1565            args.argi1 = returnCode;
1566
1567            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();//1
1568        }
1569
1570        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
1571
1572        // Send broadcast to default launcher only if it's a new install
1573        final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
1574        if (success && isNewInstall) {1575            mPm.sendSessionCommitBroadcast(generateInfo(), userId);
1576        }
1577
1578        mCallback.onSessionFinished(this, success);
1579    }

注释1处最终会调用IPackageInstallObserver2的onPackageInstalled方法,具体是实现在PackageInstallObserver类中:
frameworks/base/core/java/android/app/PackageInstallObserver.java

public class PackageInstallObserver {private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() {...@Overridepublic void onPackageInstalled(String basePackageName, int returnCode,String msg, Bundle extras) {PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg,extras);//1}};public IPackageInstallObserver2 getBinder() {return mBinder;}
}
  1. 注释1处调用了PackageInstallObserver的onPackageInstalled方法,实现这个方法的类为PackageInstallObserver的子类、前面提到的PackageInstallObserverAdapter。
    /frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
static class PackageInstallObserverAdapter extends PackageInstallObserver {private final Context mContext;private final IntentSender mTarget;private final int mSessionId;private final boolean mShowNotification;private final int mUserId;public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,boolean showNotification, int userId) {mContext = context;mTarget = target;mSessionId = sessionId;mShowNotification = showNotification;mUserId = userId;}
...@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);Notification notification = buildSuccessNotification(mContext,mContext.getResources().getString(update ? R.string.package_updated_device_owner :R.string.package_installed_device_owner),basePackageName,mUserId);if (notification != null) {NotificationManager notificationManager = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);notificationManager.notify(basePackageName,SystemMessage.NOTE_PACKAGE_STATE,notification);}}final Intent fillIn = new Intent();fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);fillIn.putExtra(PackageInstaller.EXTRA_STATUS,PackageManager.installStatusToPublicStatus(returnCode));fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,PackageManager.installStatusToString(returnCode, msg));fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);if (extras != null) {final String existing = extras.getString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);if (!TextUtils.isEmpty(existing)) {fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);}}try {mTarget.sendIntent(mContext, 0, fillIn, null, null); //1} catch (SendIntentException ignored) {}}
}

最终回调到应用层接受广播和安装的结果。

  1. 总结一下就是dispatchSessionFinished方法会通过mRemoteObserver的onPackageInstalled方法,将Complete方法出现的PackageManagerException的异常信息回调给PackageInstallObserverAdapter。

3.总结

本篇文章讲解了PackageInstaller安装APK的过程,简单来说就两步:

  1. 将APK的信息通过IO流的形式写入到PackageInstaller.Session中。
  2. 调用PackageInstaller.Session的commit方法,将APK的信息交由PMS处理。

由于PMS中对APK安装的处理比较复杂,因此关于PMS的处理部分会在本系列的下一篇文章进行讲解。

Android9.0 PM机制系列(二)PackageInstaller安装APK相关推荐

  1. Android 6.0 PM机制系列(四) APK安装需要空间分析

    前言 在Android 9.0 PM机制系列(四) APK安装需要空间分析文章中,我们重点分析了Android9.0需要的最小APK安装存储空间大小.结论就是:只要系统空间小于Math.min(get ...

  2. Android 9.0 PM机制系列(四) APK安装需要空间分析

    前言 在PM机制系列前三篇,我们着重分析了安装的整个流程,没有具体到很多细节问题. 这一篇文章我们就会具体到很多细节问题.本篇主要就是围绕一个问题展开: 安装APK到底需要多少空间不会报错INSTAL ...

  3. com.android.packageinstaller,Android包管理机制(二)PackageInstaller安装APK

    前言 在本系列上一篇文章Android包管理机制(一)PackageInstaller的初始化中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInsta ...

  4. 深入理解 Android 9.0 Crash 机制(二)

    极力推荐Android 开发大总结文章:欢迎收藏 Android 开发技术文章大总结 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 九. AppError ...

  5. 【vSphere系列二】安装 Openfiler 存储

    参考 http://tech.ddvip.com/2013-03/1362159819191307.html 一. Openfiler简介 Openfiler 能把标准x86/64架构的系统变成一个强 ...

  6. [1.2.0新功能系列:二] Apache Doris 1.2.0 JDBC外表 及 Mutil Catalog

    JDBC 外表 JDBC External Table Of Doris 提供了Doris通过数据库访问的标准接口(JDBC)来访问外部表,外部表省去了繁琐的数据导入工作,也省去了之前ODBC繁杂的驱 ...

  7. android 6.0 log,android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

    一.设置保存log文件的路径 在手机刚开机的时候,会有类似如下命令执行 /system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n ...

  8. android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

    这篇博客分析的是logcat是如何获取logd中的log,然后写入文件. 一.设置保存log文件的路径 在手机刚开机的时候,会有类似如下命令执行 /system/bin/logcat -r 5120 ...

  9. android权限机制6.0,Android6.0权限机制(二):封装

    前言 如果项目中用到危险权限的操作很多,那一个个去检查不是累死,当然要封装. 封装权限机制的方法 由于申请权限的回调onRequestPermissionsResult是在Activity或者Frag ...

  10. SAP License:SAP ECC6安装系列二:安装前的准备工作

    安装 Java 1,安装 Java,最好从 SAP 的安装盘上找 Java 版本,对于 SAP 的软件来说,最新往往不是最好的,能够最稳定的配合软件运行的版本才是最好的.请认真阅读官方的 Instal ...

最新文章

  1. SharePoint 2013 处理videoplayerpage.aspx下的个人图片显示有误问题
  2. 全球与中国重型离合器市场运营状况分析与“十四五”发展规划建议2021年版
  3. Android是否会因低价打败iPhone
  4. 【Python】用 Python 来实现PDF 的各种操作(附网站和操作指导)
  5. 导入Excel表里的数据时产生【定义了过多字段】,但有时又是成功的
  6. Nginx的http块自定义服务日志
  7. MySQL5.7 Group Replication (MGR)--Mysql的组复制之多主模式
  8. flatMap()与concatMap()与concatMapEager()– RxJava常见问题解答
  9. [LeetCode]题解(python):062-Unique Paths
  10. 职业人应该“这山望着那山高”
  11. 亲自动手用HTK实现YES NO孤立词识别
  12. 服务器宕机可能的原因以及服务器宕机解决办法
  13. Java程序员常用网站
  14. bundle install 出现 'gem install mysql2 -v '0.3.15' succeeds before bunding '
  15. 基于BeautifulSoup爬取豆瓣网上的电影信息
  16. Django - 应用及分布式路由
  17. 再记公式弱爆了!用ChatGPT处理Excel问题,效率狂升
  18. 【计算机网络】,java基础教程从入门到精通
  19. 电脑自动开机win11设置教程
  20. Android 万能遥控 开源,快速实现WIFI红外遥控器(ESP8266 SoC模式)

热门文章

  1. 计算机技术一直在变吗,计算机软考分数线一直是45吗
  2. 原来这就是公文写作总结类模板和计划类模板
  3. matlab熊,小熊解答win10系统安装matlab10.1的办法
  4. 在Windows 10中使用统一写过滤器(UWF)
  5. 投影仪融合、拼接处理系统
  6. 千兆以太网在国产FPGA(智多晶)上的实现
  7. 概率论-多维随机变量及其分布思维导图
  8. Aspose.word保存PDF时进行授权访问设置
  9. 【宋红康 MySQL数据库 】【高级篇】【09】InnoDB的数据存储结构
  10. 典型相关分析原理(CCA)