❤️Android 从源码解读 Apk 的安装过程 ❤️(中)

简介: PackageManagerServiceinstallStage()mHandler()HandlerParams.startCopy()InstallParamsFileInstallArgs.copyApk()PackageManagerServiceUtils.copyPackage()

PackageManagerService


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


public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
}


installStage()


installStage 方法就是正式开始 apk 的安装过程。这个过程包括两大步:


       1、拷贝安装包;


       2、装载代码。


    void installStage(ActiveInstallSession activeInstallSession) {
        ...
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        //把之前传入的 sessionParams 安装信息,及其它信息封装成 InstallParams
        final InstallParams params = new InstallParams(activeInstallSession);
        params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));
        mHandler.sendMessage(msg);
    }
    void installStage(List<ActiveInstallSession> children)
            throws PackageManagerException {
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final MultiPackageInstallParams params =
                new MultiPackageInstallParams(UserHandle.ALL, children);
        params.setTraceMethod("installStageMultiPackage")
                .setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));
        mHandler.sendMessage(msg);
    }


MultiPackageInstallParams:多包安装的容器,指的是一起提交的所有安装会话和参数。


 这两个方法很详细就是把之前传入的 sessionParams 安装信息及其它信息封装成 InstallParams(MultiPackageInstallParams)。


       mHandler 发送的消息 INIT_COPY 从名字上就知道是去初始化复制。

   

mHandler()


    class PackageHandler extends Handler {
        PackageHandler(Looper looper) {
            super(looper);
        }
        public void handleMessage(Message msg) {
            try {
                doHandleMessage(msg);
            } finally {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            }
        }
        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    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");
                        params.startCopy();
                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                    }
                    break;
                }
                ...
            }
        }
    }


HandlerParams.startCopy()


    private abstract class HandlerParams {
        /** User handle for the user requesting the information or installation. */
        private final UserHandle mUser;
        String traceMethod;
        int traceCookie;
        ...
        final void startCopy() {
            if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
            handleStartCopy();
            handleReturnCode();
        }
        abstract void handleStartCopy();
        abstract void handleReturnCode();
    }


 startCopy(),里面有两个抽象方法,咱只能去找他的实现类 InstallParams 里面的这两个方法了。


InstallParams


    class InstallParams extends HandlerParams {
        ...
            /*
         * 调用远程方法来获取包信息和安装位置值。
         * 如果需要,根据默认策略覆盖安装位置,然后根据安装位置创建安装参数。
         */
        public void handleStartCopy() {
            int ret = PackageManager.INSTALL_SUCCEEDED;
            // 表示文件已下载,不需要再次下载。
            if (origin.staged) {
                 // 设置安装标志位,决定是安装在手机内部存储空间还是 sdcard 中
                if (origin.file != null) {
                    installFlags |= PackageManager.INSTALL_INTERNAL;
                } else {
                    throw new IllegalStateException("Invalid stage location");
                }
            }
            // 判断安装位置
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
            final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
            PackageInfoLite pkgLite = null;
            //解析给定的包并返回最少的细节。
            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                    origin.resolvedPath, installFlags, packageAbiOverride);
            if (DEBUG_INSTANT && ephemeral) {
                Slog.v(TAG, "pkgLite for install: " + pkgLite);
            }
            /*
             * 如果我们的可用空间太少,请在放弃之前尝试释放缓存。
             */
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                // TODO: 释放目标设备上的磁盘空间
                final StorageManager storage = StorageManager.from(mContext);
                //返回给定路径被(Environment.getDataDirectory())认为存储空间不足的可用字节数。
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());
                //返回:-1 计算安装包大小有错误
                //返回其他:安装包大小。
                final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
                        origin.resolvedPath, packageAbiOverride);
                if (sizeBytes >= 0) {
                    try {
                        //用于权限提升。
                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                                origin.resolvedPath, installFlags, packageAbiOverride);
                    } catch (InstallerException e) {
                        Slog.w(TAG, "Failed to free cache", e);
                    }
                }
                /*
                 * 缓存删除了我们下载安装的文件。
                 * 用于存储错误
                 */
                if (pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                }
            }
            //安装返回码
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                //指定推荐的安装位置。 可以是其中之一
                //RECOMMEND_INSTALL_INTERNAL 安装在内部存储上,
                //RECOMMEND_INSTALL_EXTERNAL 安装在外部媒体上,
                //RECOMMEND_FAILED_INSUFFICIENT_STORAGE 用于存储错误,
                //或 RECOMMEND_FAILED_INVALID_APK 解析错误。
                int loc = pkgLite.recommendedInstallLocation;
                ...
            }
            //创建安装参数
            final InstallArgs args = createInstallArgs(this);
            mVerificationCompleted = true;
            mIntegrityVerificationCompleted = true;
            mEnableRollbackCompleted = true;
            mArgs = args;
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                final int verificationId = mPendingVerificationToken++;
                // 执行安装包验证(除非我们只是简单地移动装包)。
                if (!origin.existing) {
                    ...
                }
                //INSTALL_ENABLE_ROLLBACK 的标志参数以指示应为此安装启用回滚。
                if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                    // TODO(ruhler) b/112431924: 在“move”的情况下不要这样做?
                    final int enableRollbackToken = mPendingEnableRollbackToken++;
                    Trace.asyncTraceBegin(
                            TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
                    mPendingEnableRollback.append(enableRollbackToken, this);
                    Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
                    enableRollbackIntent.putExtra(
                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
                            enableRollbackToken);
                    enableRollbackIntent.putExtra(
                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
                            mSessionId);
                    enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
                    enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    //允许在启动完成前发送广播。 在早期启动中提交分阶段会话的 apk 部分时需要这样做。 回滚管理器在启动过程中足够早地注册其接收器,以免错过广播。
                    // 在早期启动中提交分阶段会话的 apk 部分时需要这样做。
                    // 回滚管理器在启动过程中足够早地注册其接收器,以免错过广播。
                    enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                    mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
                            android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
                            new BroadcastReceiver() {
                                @Override
                                public void onReceive(Context context, Intent intent) {
                                    // 等待回滚启用的持续时间,以毫秒为单位
                                    long rollbackTimeout = DeviceConfig.getLong(
                                            DeviceConfig.NAMESPACE_ROLLBACK,
                                            PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
                                            DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
                                    if (rollbackTimeout < 0) {
                                        rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
                                    }
                                    final Message msg = mHandler.obtainMessage(
                                            ENABLE_ROLLBACK_TIMEOUT);
                                    msg.arg1 = enableRollbackToken;
                                    msg.arg2 = mSessionId;
                                    mHandler.sendMessageDelayed(msg, rollbackTimeout);
                                }
                            }, null, 0, null, null);
                    mEnableRollbackCompleted = false;
                }
            }
            mRet = ret;
        }
        @Override
        void handleReturnCode() {
            if (mVerificationCompleted
                    && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                //INSTALL_DRY_RUN :installPackage 的标志参数,指示只应验证包而不应安装包。
                if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
                    ...
                }
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    //注释1
                    mRet = mArgs.copyApk();
                }
                //注释2
                processPendingInstall(mArgs, mRet);
            }
        }
    }
    }


注释1:mArgs 其实就是 InstallArgs ,而 InstallArgs 则是通过 createInstallArgs(this); 创建如下代码: createInstallArgs(this)


1.    private InstallArgs createInstallArgs(InstallParams params) {
        if (params.move != null) {
            //处理现有已安装应用程序移动的逻辑。
            return new MoveInstallArgs(params);
        } else {
            //处理新应用程序安装的逻辑,包括复制和重命名逻辑。
            return new FileInstallArgs(params);
        }
    }



这里createInstallArgs 返回的是 FileInstallArgs 对象。


FileInstallArgs.copyApk()


拷贝部分


    class FileInstallArgs extends InstallArgs {
        private File codeFile;
        private File resourceFile;
        // Example topology:
        // /data/app/com.example/base.apk
        // /data/app/com.example/split_foo.apk
        // /data/app/com.example/lib/arm/libfoo.so
        // /data/app/com.example/lib/arm64/libfoo.so
        // /data/app/com.example/dalvik/arm/base.apk@classes.dex
        /** New install */
        FileInstallArgs(InstallParams params) {
            super(params);
        }
        /** Existing install */
        FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
            super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                    null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
                    PackageParser.SigningDetails.UNKNOWN,
                    PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
                    false, null /* parent */, DataLoaderType.NONE);
            this.codeFile = (codePath != null) ? new File(codePath) : null;
            this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
        }
        //执行此处方法
        int copyApk() {
            try {
                //就调用个doCopyApk(),接着往下看
                return doCopyApk();
            } finally {
            }
        }
        private int doCopyApk() {
            //表示文件已下载
            if (origin.staged) {
                ...
                if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                //获取安装包路径
                codeFile = origin.file;
                resourceFile = origin.file;
                return PackageManager.INSTALL_SUCCEEDED;
            }
            try {
                final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
                //创建路径
                final File tempDir =
                        mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
                codeFile = tempDir;
                resourceFile = tempDir;
            } catch (IOException e) {
                Slog.w(TAG, "Failed to create copy file: " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }
            //将包复制到目标位置(origin.file.getAbsolutePath())。
            //返回状态码。注释3
            int ret = PackageManagerServiceUtils.copyPackage(
                    origin.file.getAbsolutePath(), codeFile);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "无法复制安装包");
                return ret;
            }
            final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
            final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            try {
                handle = NativeLibraryHelper.Handle.create(codeFile);
                // 将 apk 中的动态库 .so 文件也拷贝到目标路径中。
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        abiOverride, isIncremental);
            } catch (IOException e) {
                Slog.e(TAG, "Copying native libraries failed", e);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }
            return ret;
        }
    }


 注释3:PackageManagerServiceUtils.copyPackage() 这个看名字 就是调用 工具类 的 copyPackage 方法拷贝安装包。具体咱们往下看。

   

PackageManagerServiceUtils.copyPackage()

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


    /**
     * 将包复制到目标位置。
     * @param packagePath 要复制的包的绝对路径。 
     * 可以是单个单一的 APK 文件,也可以是包含一个或多个 APK 的集群目录。
     * @return 根据PackageManager 中的状态返回状态码
     */
    public static int copyPackage(String packagePath, File targetDir) {
        if (packagePath == null) {
            return PackageManager.INSTALL_FAILED_INVALID_URI;
        }
        try {
            final File packageFile = new File(packagePath);
            final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
            //下面有copyFile的源码,咱一起看
            copyFile(pkg.baseCodePath, targetDir, "base.apk");
            if (!ArrayUtils.isEmpty(pkg.splitNames)) {
                for (int i = 0; i < pkg.splitNames.length; i++) {
                    copyFile(pkg.splitCodePaths[i], targetDir,
                            "split_" + pkg.splitNames[i] + ".apk");
                }
            }
            return PackageManager.INSTALL_SUCCEEDED;
        } catch (PackageParserException | IOException | ErrnoException e) {
            Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
    }
    private static void copyFile(String sourcePath, File targetDir, String targetName)
            throws ErrnoException, IOException {
        if (!FileUtils.isValidExtFilename(targetName)) {
            throw new IllegalArgumentException("Invalid filename: " + targetName);
        }
        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
        final File targetFile = new File(targetDir, targetName);
        final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
                O_RDWR | O_CREAT, 0644);
        Os.chmod(targetFile.getAbsolutePath(), 0644);
        FileInputStream source = null;
        try {
            source = new FileInputStream(sourcePath);
            //将 source.getFD() 的内容复制到targetFd。
            //FileUtils:frameworks/base/core/java/android/os/FileUtils.java
            FileUtils.copy(source.getFD(), targetFd);
        } finally {
            IoUtils.closeQuietly(source);
        }
    }


看到这里 最终安装包在 data/app 目录下以 base.apk 的方式保存,至此安装包拷贝工作就已经完成。


       例:/data/app/com.example/base.apk


相关文章
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
223 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
3月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
124 1
|
3月前
|
Android开发 Docker 容器
docker中编译android aosp源码,出现Build sandboxing disabled due to nsjail error
在使用Docker编译Android AOSP源码时,如果遇到"Build sandboxing disabled due to nsjail error"的错误,可以通过在docker run命令中添加`--privileged`参数来解决权限不足的问题。
611 1
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
432 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
Java Android开发 Windows
使用keytool查看Android APK签名
本文介绍了如何使用Windows命令行工具和keytool查看APK的签名信息,并提供了使用AOSP环境中的signapk.jar工具对APK进行系统签名的方法。
353 0
使用keytool查看Android APK签名
|
3月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
132 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
3月前
|
Android开发
我的Android 进阶修炼(1): AOSP源码根目录结构
本文介绍了AOSP源码的根目录结构,提供了基于MTK9269 Android 9.0源码的目录说明,帮助读者了解AOSP源码的组织方式和各目录的功能。
173 0
我的Android 进阶修炼(1): AOSP源码根目录结构
|
3月前
|
API 开发工具 Android开发
Android源码下载
Android源码下载
453 0
|
3月前
|
Android开发
将AAB(Android App Bundle)转换为APK
将AAB(Android App Bundle)转换为APK
239 1
|
3月前
|
Android开发 开发者
Android、Flutter为不同的CPU架构包打包APK(v7a、v8a、x86)
Android、Flutter为不同的CPU架构包打包APK(v7a、v8a、x86)
262 1