美文网首页工作安卓
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