Android PMS应用安装流程源码分析下篇-安装包校验及安装

注:

  • PMS应用安装流程系统源码以Android12为基础
  • 文章中PackageManagerService简称为PMS
  • 测试机为pixel 3

一、引言

  上篇Android PMS应用安装流程源码分析-安装包的写入源码分析了Installer将待安装应用apk通过system_server写入到指定临时文件目录下,并在写入成功之后触发应用apk的后续校验安装流程即走到了PackageInstallerSession.java类的handleInstall函数。因此该篇文章继续上篇针对PMS后续安装包校验安装流程源码进行分析。

二、安装包校验

1、handleInstall

  代码比较简单,最终的目标就是调用到安装apk校验流程中,在调用apk校验流程之前做如下几件事情:解析apk、将apk中的so库copy到存储apk的临时文件目录下、创建安装包校验完成回调函数等。

    private void handleInstall() {
        //安装器为设备管理器,这里不是
        if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
            DevicePolicyEventLogger
                    .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
                    .setAdmin(mInstallSource.installerPackageName)
                    .write();
        }

        //apex应用相关,这不涉及所以省略
        if (isApexSession()) {
           ......
        }

        //isStaged为false
        if (params.isStaged) {
            ......
        }

        verify();
    }
   private void verify() {
        try {
            verifyNonStaged();
        } catch (PackageManagerException e) {
            ......
        }

    }

    private void verifyNonStaged() throws PackageManagerException {
        final PackageManagerService.VerificationParams verifyingSession = prepareForVerification();
        if (verifyingSession == null) {
            return;
        }

        //这里也为false
        if (isMultiPackage()) {
            ......
        } else {
            //进入到PMS中开始安装包合法性校验流程
            mPm.verifyStage(verifyingSession);
        }
    }
   private PackageManagerService.VerificationParams prepareForVerification()
            throws PackageManagerException {
        assertNotLocked("makeSessionActive");


        int userActionRequirement = USER_ACTION_NOT_NEEDED;
        //判断是否需要用户点击确认才能够继续安装,如果需要则弹窗给用户
        if (!params.isMultiPackage) {
            userActionRequirement = computeUserActionRequirement();
            if (userActionRequirement == USER_ACTION_REQUIRED) {
                sendPendingUserActionIntent();
                return null;
            } 
        }

        boolean silentUpdatePolicyEnforceable = false;
        synchronized (mLock) {
            ......
            //解析apk中的AndroidManifest.xml文件
            PackageLite result = parseApkLite();
            if (result != null) {
                mPackageLite = result;
                ......
                //将apk lib目录下的so库拷贝到当前存储apk的临时文件夹/lib/arm64下
                extractNativeLibraries(mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
                ......
            }
        }

        //静默安装相关,暂时省略
        ......
        synchronized (mLock) {
            return makeVerificationParamsLocked();
        }
    }
    //创建安装包校验流程相关类对象,并创建校验完成之后的回调函数
    private PackageManagerService.VerificationParams makeVerificationParamsLocked() {
        final IPackageInstallObserver2 localObserver;
        //这里肯定返回false,在上述的createSession函数中对应的parentSessionId为-1
        if (!hasParentSessionId()) {
            //这里是重点,apk校验完成之后,会通过该observer进行apk安装流程
            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) {
                    if (returnCode == INSTALL_SUCCEEDED) {
                        onVerificationComplete();
                    } else {
                        onSessionVerificationFailure(returnCode, msg);
                    }
                }
            };
        } else {
            localObserver = null;
        }
        ......
        return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite);
    }    

2、安装包校验

  该步骤主要是针对包的完整性进行校验,以确保应用在安装过程中不会被被篡改或破坏。等待安装包校验完成之后则回调到上述observer中进行安装包的安装流程。

2.1 verifyStage

  该函数最终会调用到其父类HandlerParamsstartCopy函数中,并在startCopy函数中再次回调到子类的相关函数(Java中的多态)。

void verifyStage(VerificationParams params) {
    mHandler.post(()-> {
        params.startCopy();
    });
}
final void startCopy() {
    //开始安装包完整性校验流程
    handleStartCopy();
    //回调到上述observer中
    handleReturnCode();
}

2.2 handleStartCopy

  该流程主要是针对包的完整性、所要求的特定版本应用是否存在、以及判断应用是否是降级安装进行判断,如果某一项不满足要求则安装失败。

    public void handleStartCopy() {
        //apex相关
        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
            mRet = INSTALL_SUCCEEDED;
            return;
        }
        PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,mPackageLite,origin.resolvedPath, installFlags, packageAbiOverride);
        //是否要求安装包对应应用已经安装或者降级等判断
        mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);
       if (mRet != INSTALL_SUCCEEDED) {
           return;
        }

       if (!origin.existing) {
            //安装包完成性校验,发送广播给到对应的校验器以对安装包进行校验
            sendApkVerificationRequest(pkgLite);
            //安装包安装完成之后,是否允许回滚,这里为false
            if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
               sendEnableRollbackRequest();
            }
        }
    }
   private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
            long requiredInstalledVersionCode, int installFlags) {
        String packageName = pkgLite.packageName;
        synchronized (mLock) {
            //获取当前正在安装应用保存在系统中的数据(新安装应用该数据一般为空)
            AndroidPackage dataOwnerPkg = mPackages.get(packageName);
            if (dataOwnerPkg  == null) {
                PackageSetting ps = mSettings.getPackageLPr(packageName);
                if (ps != null) {
                    dataOwnerPkg = ps.pkg;
                }
            }
            //如果要求安装包对应包名应用之前安装过的版本不为-1,但是dataOwnerPkg为空,说明没有安装过;因此不满足安装要求
            if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
                if (dataOwnerPkg == null) {
                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
                }
                //此前安装过的版本与要求的版本不一致,也不满足要求
                if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
                }
            }

            //是否允许降级
            if (dataOwnerPkg != null) {
                if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,dataOwnerPkg.isDebuggable())) {
                    try {
                        checkDowngrade(dataOwnerPkg, pkgLite);
                    } catch (PackageManagerException e) {
                        return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                    }
                }
            }
        }


        return PackageManager.INSTALL_SUCCEEDED;
    }

2.3 handleReturnCode

  在等待上述安装包校验完成之后就会回调到上述observer的onPackageInstalled函数中进行后续的安装流程。

     //省略安装包校验完成回调函数,最终都会调用到下面的handleReturnCode
     ......
    void handleReturnCode() {
         //等待安装包校验完成,再进行后续的安装流程
        if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
                || mWaitForEnableRollbackToComplete) {
            return;
        }

        sendVerificationCompleteNotification();
    }


   private void sendVerificationCompleteNotification() {
        //这里为null
        if (mParentVerificationParams != null) {
            mParentVerificationParams.trySendVerificationCompleteNotification(this, mRet);
        } else {
            try {
                //回调到上述observer中进行后续的安装流程
                observer.onPackageInstalled(null, mRet, "Package Verification Result",new Bundle());
            } catch (RemoteException e) {}
        }

    }

三、安装包安装

1、onVerificationComplete

  最终调用到安装流程与上述调用到校验流程类似,最终的处理也是是PMS对应的内部类中。

    private void onVerificationComplete() {
        ......

        install();
    }
    private void install() {
        try {
            installNonStaged();
        } catch (PackageManagerException e) { }
    }
    private void installNonStaged() throws PackageManagerException {
        final PackageManagerService.InstallParams installingSession = makeInstallParams();
        if (installingSession == null) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session should contain at least one apk session for installation");
        }
        //google分包相关,此处不涉及,代码省略
        if (isMultiPackage()) {
            ......
            mPm.installStage(installingSession, installingChildSessions);
        } else {
            //所以最终的安装流程实现仍然在PMS中
            mPm.installStage(installingSession);
        }
    }
    //创建安装包安装相关类对象以及最终的回调函数
    private PackageManagerService.InstallParams makeInstallParams() throws PackageManagerException {
        ......
        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) {
                if (isStaged()) {
                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
                } else {
                    destroyInternal();
                    dispatchSessionFinished(returnCode, msg, extras);
                }
            }

        };
        ......
        if (!isMultiPackage() && !isApexSession()) {
            synchronized (mLock) {
                if (mPackageLite == null) {
                    Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite.");
                }
                mPackageLite = getOrParsePackageLiteLocked(stageDir,0);
            }
        }
        synchronized (mLock) {
            return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,mSigningDetails, mInstallerUid, mPackageLite);
        }


    }

2、安装包安装

2.1 installStage

  同上述校验流程一样,installStage函数会调用到其父类HandlerParamsstartCopy函数中,并在startCopy函数再次调用到具体的实现类InstallParams

    void installStage(InstallParams params) {
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = params;
        ......


        mHandler.sendMessage(msg);
    }
   class PackageHandler extends Handler {
        PackageHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg) {
            doHandleMessage(msg);
        }

        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    if (params != null) {
                        ......
                        //调用到父类中
                        params.startCopy();
                    }
                    break;
                ......
                }
            }


        }
    }  
    final void startCopy() {
        //开始安装包完整性校验流程
        handleStartCopy();
        //回调到上述observer中
        handleReturnCode();
    }    

2.2 handleStartCopy

  该函数实现比较简单,主要就是为了确保当前能够安装当前安装包,比如判断安装包安装位置是否有效、设备安装空间是否充足以及安装包路径是否有效等。

    public void handleStartCopy() {
        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
            mRet = INSTALL_SUCCEEDED;
            return;
        }
        //获取安装包应用信息(包名、版本等)
        PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
        //Staged指应用程序已经成功安装到设备上,但还没有对用户可见或可用。这种安装状态通常用于支持应用程序的增量安装和回滚功能,这里为false
        boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
        if (isStaged) {
            ......
        }

        //确定应用安装位置是否有效
        mRet = overrideInstallLocation(pkgLite);
    }
    
   private int overrideInstallLocation(PackageInfoLite pkgLite) {
        final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
        //这里为true,在InstallParams的第二个构造函数中被创建的时候赋值为true
        if (origin.staged) {
            if (origin.file != null) {
                installFlags |= PackageManager.INSTALL_INTERNAL;
            } else {
                throw new IllegalStateException("Invalid stage location");
            }


        } else if (pkgLite.recommendedInstallLocation==PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
           ......
        }

        int ret = INSTALL_SUCCEEDED;
        int loc = pkgLite.recommendedInstallLocation;
        //表示安装包安装安装位置无效
        if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        //表示安装包已经被安装
        } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
            ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
        //设备安装空间不足
        } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
            ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        //安装包无效
        } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
            ret = PackageManager.INSTALL_FAILED_INVALID_APK;
        //安装包路径无效
        } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
            ret = PackageManager.INSTALL_FAILED_INVALID_URI;
        //设备外部存储器不可用,无法安装应用
        } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
            ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
        } else {
            //判断是否是覆盖安装,以返回不同的flag
            loc = installLocationPolicy(pkgLite);
            //安装包是否安装到内部存储目录
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
            //如果不是安装在内部存储目录,则将对应flag去掉
            if (!onInt) {
                if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                    installFlags &= ~PackageManager.INSTALL_INTERNAL;
                } else {
                    installFlags |= PackageManager.INSTALL_INTERNAL;
                }
            }
        }
        return ret;
    }

2.3 handleReturnCode

  该函数所涉及到了安装包合法性判断就稍显多且复杂,比如安装包中权限定义是否合乎要求、provider定义authorities是否与系统中其他应用冲突、安装包签名是否有效等并对上述apk存放对临时文件路径进行重命名。如果该函数所有校验都符合系统要求,并且文件路径重命名也成功,那么应用也就安装成功了。

    void handleReturnCode() {
        processPendingInstall();
    }
    private void processPendingInstall() {
        //将InstallParams转换为FileInstallArgs对象
        InstallArgs args = createInstallArgs(this);
        if (mRet == PackageManager.INSTALL_SUCCEEDED) {
            //将apk以及apk中的so库写入到指定的临时文件路径下,不过之前已经写入了,所以不必重复写入
            mRet = args.copyApk();
        }

        //释放安装包安装过程中可回收的压缩块
        if (mRet == PackageManager.INSTALL_SUCCEEDED) {
            F2fsUtils.releaseCompressedBlocks(mContext.getContentResolver(), new File(args.getCodePath()));
        }
        //mParentInstallParams为null
        if (mParentInstallParams != null) {
            mParentInstallParams.tryProcessInstallRequest(args, mRet);
        } else {
            PackageInstalledInfo res = createPackageInstalledInfo(mRet);
            processInstallRequestsAsync(res.returnCode == PackageManager.INSTALL_SUCCEEDED,Collections.singletonList(new InstallRequest(args, res)));
        }

    }

    /**
    * success:true,表示apk安装前置条件均满足
    * installRequests:安装包安装请求,里面包含了每个需要安装的安装包基本信息
    /**
    private void processInstallRequestsAsync(boolean success, List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            List<InstallRequest> apexInstallRequests = new ArrayList<>();
            List<InstallRequest> apkInstallRequests = new ArrayList<>();
            for (InstallRequest request : installRequests) {
                //apex相关
                if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
                    apexInstallRequests.add(request);
                } else {
                    apkInstallRequests.add(request);
                }
            }

            //apex应用与普通应用安装不可能同时发生
            if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
                throw new IllegalStateException(
                        "Attempted to do a multi package install of both APEXes and APKs");
            }

            //apex应用安装相关
            if (!apexInstallRequests.isEmpty()) {
                ......
                return;
            }
            if (success) {
                //如果前置条件不满足,则移除临时apk相关文件目录
                for (InstallRequest request : apkInstallRequests) {
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    installPackagesTracedLI(apkInstallRequests);
                }
                //如果应用安装失败则移除临时apk相关文件及文件目录
                for (InstallRequest request : apkInstallRequests) {
                    request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);
                }

            }

            for (InstallRequest request : apkInstallRequests) {
                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,new PostInstallData(request.args, request.installResult, null));
            }
        });
    }

2.4 installPackagesLI

  installPackagesTracedLI最终会调用到该函数,该函数会对安装包中所定义的权限、权限组、签名、包名、provider声明等进行校验以及对存放临时apk文件路径进行重命名,如果其中一步不满足则应用会安装失败。如下只保留了大致的函数调用流程。

    private void installPackagesLI(List<InstallRequest> requests) {
        ......

        boolean success = false;
        try {
            for (InstallRequest request : requests) {
                final PrepareResult prepareResult;
                try {
                    prepareResult = preparePackageLI(request.args, request.installResult);
                } catch (PrepareFailure prepareFailure) {
                	......
                    return;
                } finally {
                }
                ......
                try {
                    final ScanResult result = scanPackageTracedLI(prepareResult.packageToScan, prepareResult.parseFlags,prepareResult.scanFlags, System.currentTimeMillis(),request.args.user, request.args.abiOverride);
                    ......
                } catch (PackageManagerException e) {
                    return;
                }
            }

            ......
            synchronized (mLock) {
                Map<String, ReconciledPackage> reconciledPackages;
                try {
                    reconciledPackages = reconcilePackagesLocked(reconcileRequest, mSettings.getKeySetManagerService(), mInjector);
                } catch (ReconcileFailure e) {
                    ......
                    return;
                } finally {
                }
                try {
                    commitRequest = new CommitRequest(reconciledPackages,mUserManager.getUserIds());
                    //如果是覆盖安装非系统应用,则卸载旧的应用(移除系统中存储的相关信息)
                    commitPackagesLocked(commitRequest);
                    //到这一步说明应用已经安装成功了
                    success = true;
                } finally {
                }
            }
            executePostCommitSteps(commitRequest);
        } finally {
            ......
        }
    }

2.5 preparePackageLI

  • 安装包解析;
  • 安装包中所定义的权限、权限组是否合法判断;
  • 重命名apk临时文件目录;
  • 安装包完整性校验等;
  private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
            throws PrepareFailure {
        //一些变量初始化
        ......


        //scanFlags相关赋值
        ......
        //其他相关代码
        ......
        //解析安装包
        final ParsedPackage parsedPackage;
        try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
             parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
            //验证APK包中的Dex Metadata有效性
            //Dex Metadata位于APK包的Dex文件中,包含了一些关键信息,例如应用的类结构、方法和字段的信息,以及其他与Dalvik虚拟机相关的元数据。系统可以使用Dex Metadata 提前加载类或方法,以加快应用的启动速度和执行性能
            AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
        } catch (PackageParserException e) {
        } finally {
        }
        //临时应用相关
        ......
        //安装包是共享库相关
        ......
        //安装包是否在AndroidManifest.xml中配置了仅仅作为测试
        ......
        //安装包签名设置
        ......
        //instantApp相关
        ......
        synchronized (mLock) {
            //覆盖安装相关
            ......
            //权限相关判断:不允许定义的权限、权限组已经存在于不同签名的应用包内
            ......
        }
        //系统应用相关
        ......
        //重命名apk临时文件路径
        if (!args.doRename(res.returnCode, parsedPackage)) {
            throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
        }
        try {
            //也属于安装包完成性校验相关,使用的是fs-verity。当启用 fs-verity后,文件系统会对特定文件进行加密和校验,只有在校验通过的情况下,才能读取和执行该文件
            setUpFsVerityIfPossible(parsedPackage);
        } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {
        }

        //对应用进行冻结
        final PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI");
        boolean shouldCloseFreezerBeforeReturn = true;
        try {
            ......
            if (replace) {
                //覆盖安装相关
                ......
            } else { 
                ......
                String pkgName1 = parsedPackage.getPackageName();
                synchronized (mLock) {
                    //判断是否是重复安装
                    ......
                }

            }

            shouldCloseFreezerBeforeReturn = false;
            return new PrepareResult(replace, targetScanFlags, targetParseFlags,existingPackage, parsedPackage, replace, sysPkg,ps, disabledPs);
        } finally {
            ......
        }

    }

2.6 doRename

  该函数会将存放临时apk文件路径重命名为特定的路径名,比如:data/app/~~AKJbp1qvUKuNjzi_UMpsqQ==/包名-Mc2kon90VNr66M5RUQ9R_w==/

    boolean doRename(int status, ParsedPackage parsedPackage) {
        //判断前置条件是否满足,不满足则清除临时apk相关文件路径
        ......
        //获取临时apk文件夹父级目录(data/app)
        final File targetDir = resolveTargetDir();
        //临时apk文件夹目录
        final File beforeCodeFile = codeFile;
        //获取最终存储apk的文件路径
        final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());
        //apk对应文件路径是否为增量安装文件路径
        final boolean onIncremental = mIncrementalManager != null && isIncrementalPath(beforeCodeFile.getAbsolutePath());
        try {
            //目标文件路径创建
            makeDirRecursive(afterCodeFile.getParentFile(), 0775);
            //如果是增量安装文件路径,那么只做文件link操作
            if (onIncremental) {
                mIncrementalManager.linkCodePath(beforeCodeFile, afterCodeFile);
            //将此前的apk文件路径重命名为新的文件路径名
            } else {
                Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
            }

        } catch (IOException | ErrnoException e) {
            return false;
        }


        if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
            return false;
        }
        //parsedPackage中apk路径设置为最新的
        ......
      return true;
    }
   //获取存储apk的真正文件路径
   private File getNextCodePath(File targetDir, String packageName) {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[16];
        File firstLevelDir;
        do {
            random.nextBytes(bytes);
            //最终名类似于:~~AKJbp1qvUKuNjzi_UMpsqQ==
            String dirName = RANDOM_DIR_PREFIX + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
            //即data/app/~~AKJbp1qvUKuNjzi_UMpsqQ==
            firstLevelDir = new File(targetDir, dirName);
        } while (firstLevelDir.exists());
        random.nextBytes(bytes);
        String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
        //文件路径类似于:data/app/~~AKJbp1qvUKuNjzi_UMpsqQ==/包名-Mc2kon90VNr66M5RUQ9R_w==
        return new File(firstLevelDir, packageName + "-" + suffix);
    }

2.7 scanPackageNewLI

  • 对解析的安装包对象中部分属性进行设置,比如非系统应用无法声明受保护广播、非系统应用不能声明为系统核心应用、将未声明exported值的Activity声明为未导出状态等
  • 判断应用签名是否有效
  • 判断安装包包名是否与其他应用冲突
  • 判断安装包中声明的provider是否与其他应用冲突
  • 应用进程名合法性判断
  • 如果应用是非Privileged类型应用,但其共享uid与某个Privileged类型应用一致,则要求其签名与Android平台签名一致。
    private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
        final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
        @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
        ......


        scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
        synchronized (mLock) {
            ......
            applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);
            assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
            ......
        }
    }
   private static void applyPolicy(ParsedPackage parsedPackage, final int parseFlags,final int scanFlags, AndroidPackage platformPkg, boolean isUpdatedSystemApp) {
        //安装包扫描为系统应用类型,这里当然不是
        if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
            ......
        } else {
            parsedPackage
                    // 非系统应用无法声明受保护广播
                    .clearProtectedBroadcasts()
                    //非系统应用不能声明为系统核心应用
                    .setCoreApp(false)
                    //非系统应用不能声明常驻
                    .setPersistent(false)
                    //非系统应用无法设置应用的默认安装路径为设备受保护存储
                    .setDefaultToDeviceProtectedStorage(false)
                   	//非系统应用无法设置允许应用在设备处于未解锁状态启动
                    .setDirectBootAware(false)
                    //非系统应用无法设置权限的优先级
                    .capPermissionPriorities();
        }

        //将未声明exported值的Activity声明为未导出状态
        if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
            parsedPackage.markNotActivitiesAsNotExportedIfSingleUser();
        }
        //设置应用是否是如下几种类型
        parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
                .setOem((scanFlags & SCAN_AS_OEM) != 0)
                .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
                .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
                .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
                .setOdm((scanFlags & SCAN_AS_ODM) != 0);


        //校验应用签名是否于Android签名一致
        parsedPackage.setSignedWithPlatformKey(
                (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
                        || (platformPkg != null && compareSignatures(
                        platformPkg.getSigningDetails().signatures,
                        parsedPackage.getSigningDetails().signatures
                ) == PackageManager.SIGNATURE_MATCH))
        );
        //如果当前是非系统应用则移除如下几个值
        if (!parsedPackage.isSystem()) {
            parsedPackage.clearOriginalPackages()
                    .setRealPackage(null)
                    .clearAdoptPermissions();
        }
        PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
    }
    private void assertPackageIsValid(AndroidPackage pkg, final int parseFlags,final int scanFlags)throws PackageManagerException {
        //其他一些合法性判断            
        ......
        //apex不能通过apk的形式进行安装
        ......
        //判断安装包签名是否有效
        final KeySetManagerService ksms = mSettings.getKeySetManagerService();
        ksms.assertScannedPackageValid(pkg);
        synchronized (mLock) {
            //判断安装包包名是否是android,如果是则抛出异常
            ......
            //如果是新安装(非覆盖安装)的应用,但是系统中存在相同包名的应用,则抛出异常
            ......
            //当前安装包是共享库相关
            ......
            //判断是否安装现有的安装包,如果是则要求现有的安装包路径与待安装包路径相同,不然则抛出异常
            ......
            //检测安装包中声明的provider是否与系统即系统中应用已经声明的provider冲突
            if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
                mComponentResolver.assertProvidersNotDefined(pkg);
            }
            //校验安装包中是否包含主进程,以及其他子进程命名是否符合要求
            ......
            //一个应用不是Privileged类型应用,但其共享uid与某个Privileged类型应用一致,但是其签名又与Android平台签名不一致,则会直接抛出异常
            ......
            //其他代码
            ......
        }
    }

2.8 reconcilePackagesLocked

  • 将当前正在安装应用信息合并到存储所有应用基本信息的map中;
  • 如果当前正在覆盖安装非系统应用则需要删除原有的应用,这里只是构造了对应的action对象;
  • 如果是覆盖安装,则判断新安装的应用签名与原有应用签名是否一致;如果不是覆盖安装且如果当前应用与其他应用共享uid则合并签名;
    private static Map<String, ReconciledPackage> reconcilePackagesLocked(
            final ReconcileRequest request, KeySetManagerService ksms, Injector injector)
            throws ReconcileFailure {
         //当前正在安装应用信息,key为对应包名
        final Map<String, ScanResult> scannedPackages = request.scannedPackages;
        final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
        //request.allPackages表示当前系统中已经安装的应用(key为包名),这里包含了当前正在安装包
        final ArrayMap<String, AndroidPackage> combinedPackages = new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
        combinedPackages.putAll(request.allPackages);
        final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries = new ArrayMap<>();


        for (String installPackageName : scannedPackages.keySet()) {
            final ScanResult scanResult = scannedPackages.get(installPackageName);
            //将正在安装的应用信息替换map中的原有信息
            combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
            //当前正在安装的应用是否存在被共享的so库,只有系统应用才有
            ......
            //获取前面应用安装过程中存储的信息
            final InstallArgs installArgs = request.installArgs.get(installPackageName);
            final PackageInstalledInfo res = request.installResults.get(installPackageName);
            final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
            final boolean isInstall = installArgs != null;
            //如果当前正在安装应用,但是存储信息为空,则说明某个步骤存在问题
            if (isInstall && (res == null || prepareResult == null)) {
                throw new ReconcileFailure("Reconcile arguments are not balanced for " + installPackageName + "!");
            }


            final DeletePackageAction deletePackageAction;
            // 如果是覆盖安装并且是非系统应用,则需要卸载原有的应用
            if (isInstall && prepareResult.replace && !prepareResult.system) {
                final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
                final int deleteFlags = PackageManager.DELETE_KEEP_DATA | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
                deletePackageAction = mayDeletePackageLocked(res.removedInfo, prepareResult.originalPs, prepareResult.disabledPs,deleteFlags, null);
                if (deletePackageAction == null) {
                    throw new ReconcileFailure(PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,"May not delete " + installPackageName + " to replace");
                }
            } else {
                deletePackageAction = null;
            }

            //临时变量赋值
            ......
            //如果是应用升级判断新安装应用签名是与原有的应用签名一致
            if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
                } else {
                    //签名不一致则抛出异常
                    ......
                }
                signingDetails = parsedPackage.getSigningDetails();
            } else {
                 //如果正在安装应用签名与某些应用共享uid,则合并他们的签名信息
                ......
            }
            result.put(installPackageName, new ReconciledPackage(request, installArgs, scanResult.pkgSetting, res, request.preparedPackages.get(installPackageName), scanResult, deletePackageAction, allowedSharedLibInfos, signingDetails, sharedUserSignaturesChanged, removeAppKeySetData));
        }


        //共享库相关
        ......
        return result;
    }

2.9 executePostCommitSteps

  当调用到该函数,说明应用已经安装成功,后续则需要为该应用创建私有目录,以及进行dex优化。

   private void executePostCommitSteps(CommitRequest commitRequest) {
        final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
        for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
            final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
            final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
            final String packageName = pkg.getPackageName();
            final String codePath = pkg.getPath();
            final boolean onIncremental = mIncrementalManager != null && isIncrementalPath(codePath);
            if (onIncremental) {
                ......
                incrementalStorages.add(storage);
            }
            //为应用创建私有目录,data/data(data/user/0)目录下创建
            prepareAppDataAfterInstallLIF(pkg);
            //判断是否需要清除应用数据
            if (reconciledPkg.prepareResult.clearCodeCache) {
                clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
            }
            //覆盖安装相关
            if (reconciledPkg.prepareResult.replace) {
                mDexManager.notifyPackageUpdated(pkg.getPackageName(),pkg.getBaseApkPath(), pkg.getSplitCodePaths());
            }
            //准备正在安装应用配置文件,用于后续dex优化
            mArtManagerService.prepareAppProfiles(pkg,resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),true);
            //根据安装场景选择dex优化策略,比如单个应用安装、多个应用同时安装场景
            final int compilationReason = mDexManager.getCompilationReasonForInstallScenario(reconciledPkg.installArgs.mInstallScenario);
            //判断应用是否是通过其他设备迁移或者备份恢复进行安装
            final boolean isBackupOrRestore = reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE || reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP;
            //dex优化相关flag
            final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0);
            DexoptOptions dexoptOptions = new DexoptOptions(packageName, compilationReason, dexoptFlags);
       	 //判断是否需要做dex优化,需要满足如下条件
            //(1)非instant类型应用或者instant类型应用dex优化开关开启
       	 //(2)非debug类型应用
       	 //(3)非增量安装
       	 //(4)系统支持dex优化
            final boolean performDexopt = (!instantApp || Global.getInt(mContext.getContentResolver(),Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) && !pkg.isDebuggable() && (!onIncremental) && dexoptOptions.isCompilationEnabled();
            if (performDexopt) {
                //开始dex优化
                ......
            }

            //如果前面该包dex优化失败,则从失败列表中移除,以再次进行dex优化
            BackgroundDexOptService.notifyPackageChanged(packageName);
            notifyPackageChangeObserversOnUpdate(reconciledPkg);
        }

        //增量安装相关
        waitForNativeBinariesExtraction(incrementalStorages);
    }

2.10 restoreAndPostInstall

  如下代码判断是否需要对应用数据进行备份恢复(之前系统备份过该应用数据),如果需要则首先进行备份恢复;如果不需要则进行最后的资源回收、安装成功广播发送等操作。

   private void restoreAndPostInstall(int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
        final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
        boolean doRestore = !update && res.pkg != null;
        int token;
        if (mNextInstallToken < 0) mNextInstallToken = 1;
        token = mNextInstallToken++;
        if (data != null) {
            mRunningInstalls.put(token, data);
        }
        //备份恢复
        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
            if (res.freezer != null) {
                res.freezer.close();
            }
            doRestore = performBackupManagerRestore(userId, token, res);
        }

        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
            doRestore = performRollbackManagerRestore(userId, token, res, data);
        }
        //如果没有备份恢复则直接发送安装成功msg进行各种资源回收、安装成功广播发送、observer回调等操作
        //如果存在备份恢复,在恢复成功之后会回调到finishPackageInstall函数做上述的处理
        if (!doRestore) {
            Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
            mHandler.sendMessage(msg);
        }
    }

三、总结

  如上源码分析结合上篇Android PMS应用安装流程源码分析-安装包的写入即为PMS应用安装整体流程了。整个流程分析下来能够知道应用的安装实质其实就是文件的写入以及判断安装包是否符合系统要求。整个可总结为如下流程:

  • Installer调用到system_server将待安装的应用包写入到特定临时文件目录;
  • system_server判断包的完整性,防止安装过程中安装包被篡改;
  • system_server判断安装包中签名、所定义的权限、权限组、provider等是否符合要求;
  • system_server将存储apk的临时文件路径重命名为特定格式名称的文件路径;
  • system_server针对应用做dex优化以及备份恢复;
  • system_server发送安装成功广播。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYaOSOYW' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片