Android应用安装流程分析

addaddc 8年前
   <p>这段时间在研究插件化相关的技术,追根溯源,所以干脆把Apk的安装流程梳理了一遍,与大家共享,望指正!</p>    <p>本文基于Android 5.1的源码,分析Apk安装流程。</p>    <p>Apk是Android Pakage的缩写,即Android安装包,Apk文件其实是zip格式,一般包含一个或多个dex文件、resources.arsc、AndroidManifest.xml、res目录、META-INF目录及包含so库的lib目录,这里就不在啰嗦。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a2935f9dbf734d6fb119f2264a4adb8b.jpg"></p>    <h2><strong>1、安装流程图</strong></h2>    <p>先来看一张整体的流程图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/590b645b0a05dab5a7044cf87cd5e89f.png"></p>    <p>安装过程:复制apk安装包到/data/app目录下,解压并扫描安装包,向资源管理器注入apk资源,解析AndroidManifest文件,并在/data/data目录下创建对应的应用数据目录,然后针对dalvik/art环境优化dex文件,保存到dalvik-cache目录,将AndroidManifest文件解析出的组件、权限注册到PackageManagerService,完成后发送广播。</p>    <h2><strong>2、安装时序图</strong></h2>    <p>上图太过笼统,不利于了解细节,这里整理出一张时序图,以便于分析。</p>    <p><img src="https://simg.open-open.com/show/1f60e4344199415f8ec1ca0059eac2d2.png"></p>    <p>说明:时序图中划分为三个部分:PackageInstaller进程、System进程、DefaultContainerService进程,重点关注System进程中的PackageManagerService。</p>    <p>PackageManagerService:PMS是Android中最核心的服务之一,主要负责对系统的apk进行管理,以及对四大组件的管理。</p>    <h2><strong>3、流程分析</strong></h2>    <p>用户安装Apk时,如从厂商官方应用市场下载,一般无安装页面,这里以用户安装第三方安装包为例进行分析,PackageInstaller应用负责安装及卸载过程与用户交互(见时序图PackageInstaller进程)。这里着重介绍System进程PMS安装流程。</p>    <p><strong>3.1 将apk文件复制至/data/app目录</strong></p>    <p>此流程在时序图中过程还是挺繁复的,中间涉及到比较多的跳转,我们挑重点看:</p>    <pre>  <code class="language-java">public class PackageManagerService extends IPackageManager.Stub {      @Override      public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,              int installFlags, String installerPackageName, VerificationParams verificationParams,              String packageAbiOverride, int userId) {          ...          final Message msg = mHandler.obtainMessage(INIT_COPY);          msg.obj = new InstallParams(origin, observer, installFlags,                  installerPackageName, verificationParams, user, packageAbiOverride);          mHandler.sendMessage(msg);      }  }  </code></pre>    <p>此处,通过PackageHandler发送INIT_COPY消息准备复制apk,我们再来接着看,</p>    <pre>  <code class="language-java">class PackageHandler extends Handler {      ...      void doHandleMessage(Message msg) {          switch (msg.what) {               case INIT_COPY: {                      HandlerParams params = (HandlerParams) msg.obj;                      int idx = mPendingInstalls.size();                      if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);                      // If a bind was already initiated we dont really                      // need to do anything. The pending install                      // will be processed later on.                      if (!mBound) {                          // If this is the only one pending we might                          // have to bind to the service again.                          if (!connectToService()) {                              Slog.e(TAG, "Failed to bind to media container service");                              params.serviceError();                              return;                          } else {                              // Once we bind to the service, the first                              // pending request will be processed.                              mPendingInstalls.add(idx, params);                          }                      } else {                          mPendingInstalls.add(idx, params);                          // Already bound to the service. Just make                          // sure we trigger off processing the first request.                          if (idx == 0) {                              mHandler.sendEmptyMessage(MCS_BOUND);                          }                      }                      break;               }               case MCS_BOUND: {                      if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");                      if (msg.obj != null) {                          mContainerService = (IMediaContainerService) msg.obj;                      }                      if (mContainerService == null) {                          ...                      } else if (mPendingInstalls.size() > 0) {                          HandlerParams params = mPendingInstalls.get(0);                          if (params != null) {                              if (params.startCopy()) {                                  // We are done...  look for more work or to                                  // go idle.                                                                    // Delete pending install                                  if (mPendingInstalls.size() > 0) {                                      mPendingInstalls.remove(0);                                  }                                  if (mPendingInstalls.size() == 0) {                                      if (mBound) {                                          if (DEBUG_SD_INSTALL) Log.i(TAG,                                                  "Posting delayed MCS_UNBIND");                                          removeMessages(MCS_UNBIND);                                          Message ubmsg = obtainMessage(MCS_UNBIND);                                          // Unbind after a little delay, to avoid                                          // continual thrashing.                                          sendMessageDelayed(ubmsg, 10000);                                      }                                  } else {                                      // There are more pending requests in queue.                                      // Just post MCS_BOUND message to trigger processing                                      // of next pending install.                                      if (DEBUG_SD_INSTALL) Log.i(TAG,                                              "Posting MCS_BOUND for next work");                                      mHandler.sendEmptyMessage(MCS_BOUND);                                  }                              }                          }                      } else {                          // Should never happen ideally.                          Slog.w(TAG, "Empty queue");                      }                      break;               }          }          ...      }      ...  }  </code></pre>    <p>INIT_COPY这条case主要是确保 DefaultContainerService 已经绑定,DefaultContainerService是一个单独的apk进程,主要提供检查和复制设备上的文件的服务。MCS_BOUND这条case中最关键的是执行 params.startCopy() 开始拷贝工作。</p>    <pre>  <code class="language-java">class InstallParams extends HandlerParams {      ...      public void handleStartCopy() throws RemoteException {          ...          if (onInt && onSd) {              ...          } else {              pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);          ...          }                  final InstallArgs args = createInstallArgs(this);          mArgs = args;            if (ret == PackageManager.INSTALL_SUCCEEDED) {              ...              if (!origin.existing && requiredUid != -1                          && isVerificationEnabled(userIdentifier, installFlags)) {                  ...                  if (ret == PackageManager.INSTALL_SUCCEEDED                              && mRequiredVerifierPackage != null) {                          /*                           * Send the intent to the required verification agent,                           * but only start the verification timeout after the                           * target BroadcastReceivers have run.                           */                          verification.setComponent(requiredVerifierComponent);                          mContext.sendOrderedBroadcastAsUser(verification, getUser(),                                  android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,                                  new BroadcastReceiver() {                                      @Override                                      public void onReceive(Context context, Intent intent) {                                          final Message msg = mHandler                                                  .obtainMessage(CHECK_PENDING_VERIFICATION);                                          msg.arg1 = verificationId;                                          mHandler.sendMessageDelayed(msg, getVerificationTimeout());                                      }                                  }, null, 0, null, null);                            /*                           * We don't want the copy to proceed until verification                           * succeeds, so null out this field.                           */                          mArgs = null;                      }              } else {                  /*                   * No package verification is enabled, so immediately start                   * the remote call to initiate copy using temporary file.                   */                  ret = args.copyApk(mContainerService, true);              }          }      }      ...  }  </code></pre>    <p>这部分主要是检查存储空间,权限等,若已有软件包验证程序,则需要等待验证程序检验安装包,否则可直接安装。这里我们直接来看 args.copyApk 。这里需要提到的是 createInstallArgs(this) 会根据InstallParams来判断安装位置,这里以内部存储安装为例。</p>    <pre>  <code class="language-java">class FileInstallArgs extends InstallArgs {      ...      int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {              ...              try {                  final File tempDir = mInstallerService.allocateInternalStageDirLegacy();                  codeFile = tempDir;                  resourceFile = tempDir;              } catch (IOException e) {                  Slog.w(TAG, "Failed to create copy file: " + e);                  return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;              }                final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {                  @Override                  public ParcelFileDescriptor open(String name, int mode) throws RemoteException {                      ...                  }              };                int ret = PackageManager.INSTALL_SUCCEEDED;              ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);              if (ret != PackageManager.INSTALL_SUCCEEDED) {                  Slog.e(TAG, "Failed to copy package");                  return ret;              }                final File libraryRoot = new File(codeFile, LIB_DIR_NAME);              NativeLibraryHelper.Handle handle = null;              try {                  handle = NativeLibraryHelper.Handle.create(codeFile);                  ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,                          abiOverride);              } catch (IOException e) {                  Slog.e(TAG, "Copying native libraries failed", e);                  ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;              } finally {                  IoUtils.closeQuietly(handle);              }                return ret;          }      ...  }  </code></pre>    <p>这里完成了apk拷贝及so库的拷贝。</p>    <p><strong>3.2 解析安装包</strong></p>    <pre>  <code class="language-java">public class PackageManagerService extends IPackageManager.Stub {      ...      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() {                  ...                  if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {                      args.doPreInstall(res.returnCode);                      synchronized (mInstallLock) {                          installPackageLI(args, res);                      }                      args.doPostInstall(res.returnCode, res.uid);                  }                  ...              }          }      }            private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {          ...          PackageParser pp = new PackageParser();          ...                    final PackageParser.Package pkg;          try {              pkg = pp.parsePackage(tmpPackageFile, parseFlags);          } catch (PackageParserException e) {              res.setError("Failed parse during installPackageLI", e);              return;          }          ...          if (replace) {              replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,                      installerPackageName, res);          } else {              installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,                      args.user, installerPackageName, res);          }          ...      }      ...  }  </code></pre>    <p>此处对于安装包进行解析,包括解析 AndroidManifest 版本、权限、组件等,详见 PackageParser::parsePackage(tmpPackageFile, parseFlags) ,这部分代码量较大但流程清晰,这里简单看一下</p>    <pre>  <code class="language-java">public class PackageParser {      ...      public Package parsePackage(File packageFile, int flags) throws PackageParserException {          if (packageFile.isDirectory()) {              return parseClusterPackage(packageFile, flags);          } else {              return parseMonolithicPackage(packageFile, flags);          }      }      ...      public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {          ...            final AssetManager assets = new AssetManager();          try {              final Package pkg = parseBaseApk(apkFile, assets, flags);              pkg.codePath = apkFile.getAbsolutePath();              return pkg;          } finally {              IoUtils.closeQuietly(assets);          }      }      ...      private Package parseBaseApk(File apkFile, AssetManager assets, int flags)              throws PackageParserException {          ...          //将资源添加进资源管理          final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);          ...          try {              res = new Resources(assets, mMetrics, null);              ...              final String[] outError = new String[1];              //这里解析manifest文件,具体就不在展开,详看请移步源码              final Package pkg = parseBaseApk(res, parser, flags, outError);              ...                return pkg;            } catch (PackageParserException e) {              throw e;          } catch (Exception e) {              throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,                      "Failed to read manifest from " + apkPath, e);          } finally {              IoUtils.closeQuietly(parser);          }      }      ...  }  </code></pre>    <p><strong>3.3 检测权限、注册组件</strong></p>    <p>接下来看执行 installNewPackageLI 函数,这部分代码核心为 scanPackageLI 、 updateSettingsLI :</p>    <pre>  <code class="language-java">public class PackageManagerService extends IPackageManager.Stub {      ...      private void installNewPackageLI(PackageParser.Package pkg,              int parseFlags, int scanFlags, UserHandle user,              String installerPackageName, PackageInstalledInfo res) {          ...            try {              PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,                      System.currentTimeMillis(), user);                updateSettingsLI(newPackage, installerPackageName, null, null, res);              // delete the partially installed application. the data directory will have to be              // restored if it was already existing              if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {                  // remove package from internal structures.  Note that we want deletePackageX to                  // delete the package data and cache directories that it created in                  // scanPackageLocked, unless those directories existed before we even tried to                  // install.                  deletePackageLI(pkgName, UserHandle.ALL, false, null, null,                          dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,                                  res.removedInfo, true);              }            } catch (PackageManagerException e) {              res.setError("Package couldn't be installed in " + pkg.codePath, e);          }      }      ...  }  </code></pre>    <p>scanPackageLI负责安装,而updateSettingLI则是完成安装后的设置信息更新。如果安装失败则会删除安装包。</p>    <p>我们来看 scanPackageLI 这部分代码</p>    <pre>  <code class="language-java">public class PackageManagerService extends IPackageManager.Stub {      ...      private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,              int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {          boolean success = false;          try {              final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,                      currentTime, user);              success = true;              return res;          } finally {              if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {                  removeDataDirsLI(pkg.packageName);              }          }      }      ...      private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,              int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {          ...          // writer          synchronized (mPackages) {              // 验证已注册的ContentProvider是否有其他同名              if ((scanFlags & SCAN_NEW_INSTALL) != 0) {                  final int N = pkg.providers.size();                  int i;                  for (i=0; i<N; i++) {                      PackageParser.Provider p = pkg.providers.get(i);                      if (p.info.authority != null) {                          String names[] = p.info.authority.split(";");                          for (int j = 0; j < names.length; j++) {                              if (mProvidersByAuthority.containsKey(names[j])) {                                  PackageParser.Provider other = mProvidersByAuthority.get(names[j]);                                  final String otherPackageName =                                          ((other != null && other.getComponentName() != null) ?                                                  other.getComponentName().getPackageName() : "?");                                  throw new PackageManagerException(                                          INSTALL_FAILED_CONFLICTING_PROVIDER,                                                  "Can't install because provider name " + names[j]                                                  + " (in package " + pkg.applicationInfo.packageName                                                  + ") is already used by " + otherPackageName);                              }                          }                      }                  }              }          }                  if (mPlatformPackage == pkg) {              ...          } else {              // This is a normal package, need to make its data directory.              dataPath = getDataPathForPackage(pkg.packageName, 0);              if (dataPath.exists()) {                  ...              } else {                  //invoke installer to do the actual installation                  //这里创建了应用数据目录,用于存放用户数据                  int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,                                             pkg.applicationInfo.seinfo);                  ...              }                      }                  // We also need to dexopt any apps that are dependent on this library.  Note that          // if these fail, we should abort the install since installing the library will          // result in some apps being broken.          if (clientLibPkgs != null) {              if ((scanFlags & SCAN_NO_DEX) == 0) {                  for (int i = 0; i < clientLibPkgs.size(); i++) {                      PackageParser.Package clientPkg = clientLibPkgs.get(i);                      if (performDexOptLI(clientPkg, null /* instruction sets */, forceDex,                              (scanFlags & SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) {                          throw new PackageManagerException(INSTALL_FAILED_DEXOPT,                                  "scanPackageLI failed to dexopt clientLibPkgs");                      }                  }              }          }                  // writer          synchronized (mPackages) {              ...              // 以下对四大组件进行注册              int N = pkg.providers.size();              StringBuilder r = null;              int i;              for (i=0; i<N; i++) {                  PackageParser.Provider p = pkg.providers.get(i);                  p.info.processName = fixProcessName(pkg.applicationInfo.processName,                          p.info.processName, pkg.applicationInfo.uid);                  mProviders.addProvider(p);                  ...              }              ...          }      }      ...  }  </code></pre>    <p>scanPackageLI()方法主要逻辑是由 scanPackageDirtyLI() 实现。这里主要对provider冲突检测,创建应用数据目录,dexopt操作,四大组件注册,权限注册等。</p>    <p><strong>3.4 安装完成</strong></p>    <p>继续看 processPendingInstall ,安装成功后如需要备份则会通过 BackupManagerService 进行备份:</p>    <pre>  <code class="language-java">public class PackageManagerService extends IPackageManager.Stub {      ...      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() {                  ...                  if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {                      IBackupManager bm = IBackupManager.Stub.asInterface(                              ServiceManager.getService(Context.BACKUP_SERVICE));                      if (bm != null) {                          if (DEBUG_INSTALL) Log.v(TAG, "token " + token                                  + " to BM for possible restore");                          try {                              if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {                                  bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);                              } else {                                  doRestore = false;                              }                          } catch (RemoteException e) {                              // can't happen; the backup manager is local                          } catch (Exception e) {                              Slog.e(TAG, "Exception trying to enqueue restore", e);                              doRestore = false;                          }                      } else {                          Slog.e(TAG, "Backup Manager not found!");                          doRestore = false;                      }                  }                  if (!doRestore) {                      // No restore possible, or the Backup Manager was mysteriously not                      // available -- just fire the post-install work request directly.                      if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);                      Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);                      mHandler.sendMessage(msg);                  }                  ...              }          }      }      ...  }  </code></pre>    <p>无论备份与否,最终则会通过 PackageHandler 发送POST_INSTALL消息,最终通过发送 Intent.ACTION_PACKAGE_ADDED 广播,apk的安装流程就到此结束了。</p>    <p> </p>    <p>来自:http://solart.cc/2016/10/30/install_apk/</p>    <p> </p>