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

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

<activity android:name=".InstallStart"
        android:theme="@android:style/Theme.Translucent.NoTitleBar"
        android:exported="true"
        android:excludeFromRecents="true">
        <intent-filter android:priority="1">
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.INSTALL_PACKAGE"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="content"/>
            <data android:mimeType="application/vnd.android.package-archive"/>
        </intent-filter>
                         
        <intent-filter android:priority="1">
            <action android:name="android.intent.action.INSTALL_PACKAGE"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="package"/>
            <data android:scheme="content"/>
        </intent-filter>
        
        <intent-filter android:priority="1">
            <action android:name="android.content.pm.action.CONFIRM_INSTALL"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

InstallStart.java

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

跳转InstallStart .java

主要工作:

  1. 判断是否需要展示一个确认安装的对话框,如果是,则跳转PackageInstallerActivity;如果否则跳转3

    final boolean isSessionInstall =PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());

  2. 判断是否勾选"信任未知来源"选项,若未勾选,则判断版本是否小于Android 8.0,是则取消安装;否则判断版本是否大于Android 8.0且没有设置REQUEST_INSTALL_PACKAGES权限,是则取消安装

    isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);

  3. 判断Uri的Scheme协议,若是content://则跳转 InstallStaging, 若是 package://则跳转 PackageInstallerActivity,但是实际上 InstallStaging中的 StagingAsyncTask 会将content协议的Uri转换为File协议,然后跳转到PackageInstallerActivity

  4. finish当前InstallStart界面

PackageInstallerActivity.java

主要工作:

  1. 构建确认安装对话框

    protected void onCreate(Bundle icicle) {
    ...
    bindUi();
    }

  2. 初始化安装需要用的各种对象,比如 PackageManager、IPackageManager、AppOpsManager、UserManager、PackageInstaller 等等

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

    boolean wasSetUp = processPackageUri(packageUri);

  4. 使用 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);
    }

  5. 打开"允许未知来源安装"设置选项后回到 PackageInstallerActivity,在 onActivityResult()中,调用initiatenstall()检查应用列表判断该应用是否已安装,若已安装则提示该应用已安装,由用户决定是否替换,然后显示确认安装界面

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

  7. 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监听安装成功和失败的回调,跳转到对应结果页面

      <receiver android:name=".InstallEventReceiver"
         android:permission="android.permission.INSTALL_PACKAGES"
         android:exported="true">
             <intent-filter android:priority="1">
                 <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
             </intent-filter>
      </receiver>
    

PackageInstallerSession

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

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

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

IPackageInstallerSession 的实现类为:

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

//PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    dispatchStreamValidateAndCommit();
}

private void dispatchStreamValidateAndCommit() {
    mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}

主要工作

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

  2. 最后执行在 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<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
    
         installNonStagedLocked(childSessions);
         ...
     }
    
     private void installNonStagedLocked(List<PackageInstallerSession> childSessions) {
         final PackageManagerService.ActiveInstallSession installingSession = makeSessionActiveLocked();
          ...
          //安装走到了 PMS 中
          mPm.installStage(installingSession);
       ...
     }
    
  3. 最后,安装过程走到了 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);
    }

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

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

//HandlerParams.java
final void startCopy() {
    handleStartCopy();
    handleReturnCode();
}

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

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

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

  1. processPendingInstall()调用了processInstallRequestsAsync(),processInstallRequestsAsync()进行安装前校验,开始安装APK,发送通知安装结果的广播

    private void processInstallRequestsAsync(boolean success, List<InstallRequest>installRequests) {
    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));
    }
    });
    }

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

安装过程分为四个阶段:

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

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层。
相关推荐
Aileen_0v01 小时前
【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】
android·java·人工智能·云计算·ocr·腾讯云·玩转腾讯云ocr
江上清风山间明月4 小时前
Flutter DragTarget拖拽控件详解
android·flutter·ios·拖拽·dragtarget
debug_cat7 小时前
AndroidStudio Ladybug中编译完成apk之后定制名字kts复制到指定目录
android·android studio
编程洪同学11 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
氤氲息13 小时前
Android 底部tab,使用recycleview实现
android
Clockwiseee13 小时前
PHP之伪协议
android·开发语言·php
小林爱13 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
小何开发14 小时前
Android Studio 安装教程
android·ide·android studio
开发者阿伟15 小时前
Android Jetpack LiveData源码解析
android·android jetpack
weixin_4381509915 小时前
广州大彩串口屏安卓/linux触摸屏四路CVBS输入实现同时显示!
android·单片机