美文网首页工作安卓
Android PMS(二)-Apk安装流程

Android PMS(二)-Apk安装流程

作者: Stan_Z | 来源:发表于2019-07-17 20:53 被阅读27次
    一、APK组成

    在APK的安装流程,在此之前先简单了解下APK组成:

    目录/文件 描述
    assert 存放的原生资源文件,通过AssetManager类访问。
    lib 存放库文件。
    META-INF 保存应用的签名信息,签名信息可以验证APK文件的完整性。
    res 存放资源文件。res中除了raw子目录,其他的子目录都参与编译,这些子目录下的资源是通过编译出的R类在代码中访问。
    AndroidManifest.xml 用来声明应用程序的包名称、版本、组件和权限等数据。 apk中的AndroidManifest.xml经过压缩,可以通过AXMLPrinter2工具解开。
    classes.dex Java源码编译后生成的Java字节码文件。
    resources.arsc 编译后的二进制资源文件。
    二、Apk安装方法

    APK的安装场景主要有以下几种:

    • 通过adb命令安装:adb 命令包括adb push/install
    • 用户下载的Apk,通过系统安装器packageinstaller安装该Apk。packageinstaller是系统内置的应用程序,用于安装和卸载应用程序。
    • 系统开机时安装系统应用。
    • 电脑或者手机上的应用商店自动安装。

    这里主要分析下PackageInstaller安装Apk流程:

    按如上流程,挑比较重要的流程分析下:

    packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
    
        private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
            final PackageInstaller packageInstaller = pm.getPackageInstaller();
            PackageInstaller.Session session = null;
            try {
                final String packageLocation = mPackageURI.getPath();
                final File file = new File(packageLocation);
                final int sessionId = packageInstaller.createSession(params);
                final byte[] buffer = new byte[65536];
                session = packageInstaller.openSession(sessionId);
                final InputStream in = new FileInputStream(file);
                final long sizeBytes = file.length();
                final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
                try {
                    int c;
                    while ((c = in.read(buffer)) != -1) {
                        out.write(buffer, 0, c);
                        if (sizeBytes > 0) {
                            final float fraction = ((float) c / (float) sizeBytes);
                            session.addProgress(fraction);
                        }
                    }
                    session.fsync(out);
                } finally {
                    IoUtils.closeQuietly(in);
                    IoUtils.closeQuietly(out);
                }
                // Create a PendingIntent and use it to generate the IntentSender
                Intent broadcastIntent = new Intent(BROADCAST_ACTION + mInstallId);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        InstallAppProgress.this /*context*/,
                        sessionId,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                session.commit(pendingIntent.getIntentSender());
    ...
        }
    

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

    到了PMS这边有如下的关键几步:

    1) PackageHandler处理安装消息

    PMS中doHandleMessage方法用于处理各个类型的消息:

    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
             // 将新的请求加入到mPendingIntalls中,等待MCS_BOUND阶段处理
                break;
           }
            case MCS_BOUND: { 
               //如果mPendingInstalls不为空,调用InstallParams.startCopy函数处理安装请求
                满足条件触发startCopy()
                break;
          }
       }
    }
    

    这里会起一个新的进程DefaultContainerService用于检查和复制可移动文件的服务。它运行在com.android.defcontainer进程,通过IMediaContainerService和PMS进行IPC通信。

    final boolean startCopy() {
        boolean res;
       try {
            if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
           //尝试安装4次,超过次数就放弃这个安装请求
           if (++mRetries > MAX_RETRIES) {
                Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
               mHandler.sendEmptyMessage(MCS_GIVE_UP);
               handleServiceError();
               return false;
           } else {
                handleStartCopy(); //复制APK
               res = true;
           }
        } catch (RemoteException e) {
            if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
           mHandler.sendEmptyMessage(MCS_RECONNECT);
           res = false;
       }
        handleReturnCode(); //安装APK
       return res;
    }
    

    2) 复制APK

    handleStartCopy()主要功能是获取安装位置信息以及复制apk到指定位置。抽象类InstallArgs中的copyApk负责复制APK文件,具体实现在子类FileInstallArgs和SdInstallArgs里面。最终通过IMediaContainerService跨进程调用DefaultContainerService的copyPackage方法,在DefaultContainerService所在的进程中将APK复制到临时存储目录,比如/data/app/vmdl18300388.tmp/base.apk。

    3) 安装APK

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        // Queue up an async operation since the package installation may take a little while.
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
                 // Result object to be returned
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.returnCode = currentStatus;
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = new PackageRemovedInfo();
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    //预安装
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                        //安装
                        installPackageLI(args, res);
                    }
                    //结束安装
                    args.doPostInstall(res.returnCode, res.uid);
                }
                if (!doRestore) {
                    //如果完成安装的msg,package add的广播将在此处发送
                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                    mHandler.sendMessage(msg);
                }
            }
        });
    }
    

    安装的核心方法是:installPackageLI

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
            PackageParser pp = new PackageParser();
               ...
             // 解析我们的package
                pkg = pp.parsePackage(tmpPackageFile, parseFlags);
               …
          //
           mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                                   null /* instructionSets */, false /* checkProfiles */,
                                   getCompilerFilterForReason(REASON_INSTALL),
                                  getOrCreateCompilerPackageStats(pkg),
            mDexManager.isUsedByOtherApps(pkg.packageName));
               ...
           //安装package
            if (replace) {
                  // 更新已经存在的packages
                replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                        installerPackageName, res);
            } else {
                  // 安装新的packages
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res);
            }
        }
                  ...
    }
    

    先看PackageParser的parsePackage:

    public Package parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
    ...
        if (packageFile.isDirectory()) {
           //多apk,内部会通过parseApkLite方法解析每个Mutiple APK,得到每个Mutiple APK对应的ApkLite轻量级APK信息
            parsed = parseClusterPackage(packageFile, flags);
        } else {
           //单apk 
            parsed = parseMonolithicPackage(packageFile, flags);
        }
    ...
        return parsed;
    }
    

    注:
    Android5.0引入了Split APK机制,这是为了解决65536上限以及APK安装包越来越大等问题。Split APK机制可以将一个APK,拆分成多个独立APK。
    在引入了Split APK机制后,APK有两种分类:

    • Single APK:安装文件为一个完整的APK,即base APK。Android称其为Monolithic。
    • Mutiple APK:安装文件在一个文件目录中,其内部有多个被拆分的APK,这些APK由一个 base APK和一个或多个split APK组成。Android称其为Cluster。

    之后调用parseBaseApk 以及 parseBaseApkCommon,主要的xml解析在parseBaseApkCommon:主要用来解>析APK的AndroidManifest中的各个标签,比如application、permission、uses-sdk、feature-group等等。

    PackageParser主要负责解析AndroidManifest

    总结下installPackageLI过程:

    • PackageParser$parsePackage,主要是解析APK的AndroidManifest.xml,将每个标签对应的信息添加到Package的相关列表中,如将下的信息添加到Package的activities列表等。
    • 加载apk证书,获取签名信息。
    • 检查目前安装的APK是否在系统中已存在:
      • 已存在,则调用replacePackageLIF进行替换安装。
      • 不存在,否则调用installNewPackageLIF进行安装。

    后面的replacePackageLIF和installNewPackageLIF最终都会走scanPackageTracedLI。


    参考:

    http://liuwangshu.cn/tags/Android%E5%8C%85%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6/
    https://maoao530.github.io/2017/01/18/package-install/

    相关文章

      网友评论

        本文标题:Android PMS(二)-Apk安装流程

        本文链接:https://www.haomeiwen.com/subject/onzclctx.html