• APK安装过程解析


    应用端发起安装APK的代码一般如下:

     Intent installintent = new Intent();
     installintent.setAction(Intent.ACTION_VIEW);
     installintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     installintent.setDataAndType(xxx,"application/vnd.android.package-archive");
     context.startActivity(installintent);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    安装apk,本质上是通过系统的应用packageInstaller.apk来完成的。因此,我们需要查看的是packageInstaller的源码。打开 PackageInstaller 的AndroidManifest.xml文件,我们会发现跟上面Intent要启动的Activity匹配的是InstallStart,这也是PackageInstaller应用的入口:

    
            
                
                
                
                
                
            
                             
            
                
                
                
                
            
            
            
                
                
            
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    InstallStart.java

    /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
    
    • 1

    跳转InstallStart .java

    主要工作:

    1. 判断是否需要展示一个确认安装的对话框,如果是,则跳转PackageInstallerActivity;如果否则跳转3
    final boolean isSessionInstall =PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
    
    • 1
    1. 判断是否勾选“信任未知来源”选项,若未勾选,则判断版本是否小于Android 8.0,是则取消安装;否则判断版本是否大于Android 8.0且没有设置REQUEST_INSTALL_PACKAGES权限,是则取消安装
    isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
    
    • 1
    1. 判断Uri的Scheme协议,若是content://则跳转 InstallStaging, 若是 package://则跳转 PackageInstallerActivity,但是实际上 InstallStaging中的 StagingAsyncTask 会将content协议的Uri转换为File协议,然后跳转到PackageInstallerActivity

    2. finish当前InstallStart界面

    PackageInstallerActivity.java

    主要工作:

    1. 构建确认安装对话框
    protected void onCreate(Bundle icicle) {
    	...
    	bindUi();
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 初始化安装需要用的各种对象,比如 PackageManager、IPackageManager、AppOpsManager、UserManager、PackageInstaller 等等

    2. 根据传过来的scheme协议(package/file)做不同处理,主要就是获取PackageInfo对象,PackageInfo包含了Android 应用的信息,例如应用名称,权限列表等,通过PackageInfo创建AppSnippet对象,里面包含待安装应用的图标和标题

     boolean wasSetUp = processPackageUri(packageUri);
    
    • 1
    1. 使用 checkIfAllowedAndInitiateInstall() 来检查APK来源,展示"未知来源APK安装"的对话框,当点击"settings"按钮后跳转到设置页
      Intent settingsIntent = new Intent();
        settingsIntent.setAction(
        Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        Uri packageUri = Uri.parse("package:" + argument);
        settingsIntent.setData(packageUri);
        try{
            getActivity().startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
        } catch(ActivityNotFoundException exc){
            Log.e(TAG, "Settings activity not found for action: " + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 打开“允许未知来源安装”设置选项后回到 PackageInstallerActivity,在 onActivityResult()中,调用initiatenstall()检查应用列表判断该应用是否已安装,若已安装则提示该应用已安装,由用户决定是否替换,然后显示确认安装界面

    2. 在安装界面,点击 OK 按钮确认安装后,会调用 startInstall 开始安装工作

    3. startInstall 方法用来跳转到 InstallInstalling,并关闭掉当前的 PackageInstallerActivity

    InstallInstalling.java

    主要工作:

    1. 获取PackageManager,然后获取其中的PackageInstaller对象

    2. PackageInstaller中创建用于传输APK安装包以及安装信息的Session,并返回SessionId

    3. 创建异步任务InstallingAsyncTask,并在异步任务中根据之前返回的SessionId打开PackageInstallerSession

    4. 通过Session将APK安装包以及相关信息传递

    5. 在异步任务中onPostExecute方法中执行session.commit(pendingIntent.getIntentSender())

    6. 通过注册观察者 InstallEventReceiver监听安装成功和失败的回调,跳转到对应结果页面

         
                
                    
                
         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    PackageInstallerSession

    session.commit()方法通过binder跨进程调到了 PackageInstallerSession 服务中。

    通过aidl文件定义的接口为:

    /frameworks/base/core/java/android/content/pm/IPackageInstallerSession.aidl
    
    • 1

    IPackageInstallerSession 的实现类为:

    /frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
    
    • 1
    //PackageInstallerSession.java
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        ...
        dispatchStreamValidateAndCommit();
    }
    
    private void dispatchStreamValidateAndCommit() {
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    主要工作

    1. 发送了 MSG_STREAM_VALIDATE_AND_COMMIT 消息到 mHandler
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit(); //
                    break;
                case MSG_INSTALL:
                    handleInstall(); //
                    break;
                ...
            }
            return true;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 最后执行在 handleStreamValidateAndCommit()中,然后里面又发送了消息MSG_INSTALL,这个执行在handleInstall()
    //PackageInstallerSession.java
        public void handleStreamValidateAndCommit() {
            ...
             mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
        }
        rivate final Handler.Callback mHandlerCallback = new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_STREAM_VALIDATE_AND_COMMIT:
                        handleStreamValidateAndCommit(); //
                        break;
                    case MSG_INSTALL:
                        handleInstall(); //
                        break;
                    ...
                }
                return true;
            }
        };
    
        private void handleInstall() {
            ...
            List childSessions = getChildSessionsNotLocked();
    
            installNonStagedLocked(childSessions);
            ...
        }
    
        private void installNonStagedLocked(List childSessions) {
            final PackageManagerService.ActiveInstallSession installingSession = makeSessionActiveLocked();
             ...
             //安装走到了 PMS 中
             mPm.installStage(installingSession);
          ...
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    1. 最后,安装过程走到了 PMSinstallStage()

    目前为止,只是通过 PackageInstaller 维持了安装 Session,把安装包写入到 Session中,真正的安装过程是 PMS 来执行。

    PackageManagerService.java

    主要工作

    1. installStage()创建 InstallParams 对象,传入message消息;发送了 INIT_COPY 消息到mHandler
    //PMS
    void installStage(ActiveInstallSession activeInstallSession) {
        ...
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final InstallParams params = new InstallParams(activeInstallSession);
        msg.obj = params;
        mHandler.sendMessage(msg);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    mHandlerPackageHandler,这是在 PackageManagerService 构造方法中创建的。
    mHandler 处理INIT_COPYInstallParams.startCopy():

    void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    // 得到安装参数  HandlerParams 
                    HandlerParams params = (HandlerParams) msg.obj;
                    if (params != null) {
                        if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                System.identityHashCode(params));
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                        // 2 调用  startCopy() 方法
                        params.startCopy();
                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                    }
                    break;
                }
                ...
            } 
    }          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    里面调用了 HandlerParamsstartCopy() 方法。HandlerParamsPMS的内部抽象类。PMS的内部类 InstallParamsMultiPackageInstallParams 是其实现类。

    //HandlerParams.java
    final void startCopy() {
        handleStartCopy();
        handleReturnCode();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    handleStartCopy()、handleReturnCode() 都是是抽象的方法。因此,这里的对象是哪一个呢? 经过之前的分析我们知道是 InstallParams

    1. handleStartCopy()扫描了apk的轻量信息、安装空间、apk完整性的校验等。创建 FileInstallArgs 对象,赋值返回code。

    2. handleReturnCode()先是调用了 InstallArgs 的 copyApk()方法执行APK拷贝,然后执行安装APK;

      void handleReturnCode() {
                if (mVerificationCompleted && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                    ...
                    //如果前面校验ok,这里执行apk拷贝
                    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                        mRet = mArgs.copyApk();
                    }
                    //执行安装
                    processPendingInstall(mArgs, mRet);
                }
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    FileInstallArgs 继承了 InstallArgs类。 copyApk() 内部调用了 doCopyApk();

    doCopyApk() 先获取了拷贝文件路径:/data/app,使用PackageManagerServiceUtils.copyPackage()进行APK拷贝,接着是 .so文件的拷贝。也就是说,把发送到 Session暂存目录 data/app-staging 的APK 拷贝到了 /data/app。

    /data/app-stagging 临时目录把apk拷贝到/data/app目录。

    //拷贝apk
        int ret = PackageManagerServiceUtils.copyPackage(
                origin.file.getAbsolutePath(), codeFile);
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            Slog.e(TAG, "Failed to copy package");
            return ret;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接着看执行安装processPendingInstall(mArgs, mRet)

    1. processPendingInstall()调用了processInstallRequestsAsync(),processInstallRequestsAsync()进行安装前校验,开始安装APK,发送通知安装结果的广播
      private void processInstallRequestsAsync(boolean success, ListinstallRequests) {
            mHandler.post(() -> {
                if (success) {
                    for (InstallRequest request : installRequests) {
                        //安装前检验:returnCode不为INSTALL_SUCCEEDED就移除拷贝的apk等
                        request.args.doPreInstall(request.installResult.returnCode);
                    }
                    synchronized (mInstallLock) {
                        //安装:解析apk
                        installPackagesTracedLI(installRequests);
                    }
                    for (InstallRequest request : installRequests) {
                        //同安装前检验
                        request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);
                    }
                }
                for (InstallRequest request : installRequests) {
                    //安装后续:备份、可能的回滚、发送安装完成先关广播
                    restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, new PostInstallData(request.args, request.installResult, null));
                }
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里我们先重点看安装过程,installPackagesTracedLI() 又走到 installPackagesLI():

    安装过程分为四个阶段:

    1. 准备,分析当前安装状态,解析包 并初始校验:在 preparePackageLI() 内使用 PackageParser2.parsePackage() 解析AndroidManifest.xml,获取四大组件等信息;使用ParsingPackageUtils.getSigningDetails() 解析签名信息;重命名包最终路径 等。
      prepareResult = preparePackageLI(request.args, request.installResult);
    1. 扫描,根据准备阶段解析的包信息上下文 进一步解析:确认包名真实;根据解析出的信息校验包有效性(是否有签名信息等);搜集apk信息——PackageSetting、apk的静态库/动态库信息等。
      final List scanResults = scanPackageTracedLI( prepareResult.packageToScan, prepareResult.parseFlags, prepareResult.scanFlags, System.currentTimeMillis(), request.args.user);
    1. 核对,验证扫描后的包信息,确保安装成功:主要就是覆盖安装的签名匹配验证。
      reconciledPackages = reconcilePackagesLocked( reconcileRequest, mSettings.mKeySetManagerService);

    4. 提交,提交扫描的包、更新系统状态:添加 PackageSetting 到 PMS 的 mSettings、添加 AndroidPackage 到 PMS 的 mPackages 、添加 秘钥集 到系统、应用的权限添加到 mPermissionManager、四大组件信息添加到 mComponentResolver 。这是唯一可以修改系统状态的地方,并且要对所有可预测的错误进行检测。
    commitRequest = new CommitRequest(reconciledPackages, mUserManager.getUserIds()); commitPackagesLocked(commitRequest);

    安装完成后,调用了 executePostCommitSteps() 准备app数据、执行dex优化:

    • prepareAppDataAfterInstallLIF():提供目录结构/data/user/用户ID/包名/cache(/data/user/用户ID/包名/code_cache)
    • mPackageDexOptimizer.performDexOpt():dexopt 是对 dex 文件 进行 verification 和 optimization 的操作,其对 dex 文件的优化结果变成了 odex 文件,这个文件和 dex 文件很像,只是使用了一些优化操作码(譬如优化调用虚拟指令等)。

    这两个操作最终都是使用 Installer 对应的方法来操作。前面介绍 PMS 创建时传入了 Installer 的实例,而 Installer 继承自 SystemService 也是一个系统服务。这里来看下:

    • 在 Installer的 onStart()方法中 通过 installd 的Binder对象获取了 mInstalld 实例。可见这里是IPC操作,即 System_Server 进程中的 Installer IPC 到 具有root权限的守护进程。像目录 /data/user 的创建 必须要有root权限。
    • PMS中使用了Installer的很多方法,Installer是Java层提供的Java API接口,Installd 则是在init进程启动的具有root权限的Daemon进程。

    到这里安装完成,再回到 PMS 的 processInstallRequestsAsync(),最后调用restoreAndPostInstall()进行 备份、可能的回滚、发送安装完成先关广播:

    private void restoreAndPostInstall(
            int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
        ...
        Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
        mHandler.sendMessage(msg);
        ...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    mHandler还是 PackageHandler对象。

    mHandler 使用 PMS的 handlePackagePostInstall()方法处理 POST_INSTALL

    • 根据安装结果 发送 Intent.ACTION_PACKAGE_ADDED 等广播,桌面Launcher 收到广播后就会在桌上增加App的Icon
    • 调用 PackageInstallSession 中保存的IPackageInstallObserver2实例的onPackageInstalled()方法,最后发送安装成功的通知显示在通知栏,通过 IntentSender 发送 在 InstallInstalling 中就定义好的广播,最后 InstallInstalling页面 根据结果展示 安装成功/安装失败 。

    到这里,APK的安装过程 就梳理完毕了,来回顾下:

    • APK用写入Session且包信息和APK安装操作 都提交到了PMS;
    • PMS中先把APK拷贝到 /data/app,然后使用PackageParser2解析APK 获取 四大组件、搜集签名、PackageSetting等信息,并进行校验确保安装成功;
    • 接着提交信息包更新系统状态及PMS的内存数据;
    • 然后使用 Installer 准备用户目录/data/user、进行 dexOpt;
    • 最后发送安装结果通知UI层。

  • 相关阅读:
    ST‐LINK V2 使用说明(安装,调试,烧录)
    基于R语言绘制GGE双标图2
    《CTF特训营》——古典密码学
    义乌再次位列第一档!2022年跨境电商综试区评估结果揭晓!
    flink1.13.2版本的对应的hive的Hcatalog的使用记录
    C++基础——new和delete动态开辟
    【OpenCV入门】第五部分——图像运算
    【Leetcode】 17. 电话号码的字母组合
    C++ STL 【priority_queue】
    文本情感倾向查询易语言代码
  • 原文地址:https://blog.csdn.net/jxq1994/article/details/132875496