美文网首页
Apk安装的源码分析(三)

Apk安装的源码分析(三)

作者: 就叫汉堡吧 | 来源:发表于2023-10-12 20:10 被阅读0次
    • 概述

      前面两篇博文分析了apk安装的流程,我们换个角度,从apk启动来看安装的信息是如何使用的,这个过程也可能会发现安装流程中忽视掉的逻辑,从而使头尾相连,把整个安装原理搞明白。

      整个思路就是根据Activity的启动流程,看看过程中是如何检索包信息来实现启动的,根据已知原理,我们到ActivityStarter的execute中开始寻找,我们发现启动的所有信息,包括ActivityInfo等都是通过ActicityStarter.Request来指定的,显然这是我们需要关注的重点,追踪一下它的信息都是怎么来的。

    • mRequest.resolveActivity(mSupervisor)

      在execute方法中有一句:

      if (mRequest.activityInfo == null) {
          mRequest.resolveActivity(mSupervisor);
      }
      

      因为经查找,我们并没有发现activityInfo被设置了,因此这里会执行mRequest.resolveActivity方法。

      void resolveActivity(ActivityTaskSupervisor supervisor) {
          ...
          resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
                  0 /* matchFlags */,
                  computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
          ...
          activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
                  profilerInfo);
            ...
      }
      

      mSupervisor是ActivityTaskSupervisor,它的resolveActivity方法中:

      mService.getPackageManagerInternalLocked().resolveIntent(
              intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
              filterCallingUid);
      

      mService是ActivityTaskManagerService,它的getPackageManagerInternalLocked方法如下:

      PackageManagerInternal getPackageManagerInternalLocked() {
          if (mPmInternal == null) {
              mPmInternal = LocalServices.getService(PackageManagerInternal.class);
          }
          return mPmInternal;
      }
      

      全局搜索“LocalServices.addService(PackageManagerInternal”会在PackageManagerService的构造方法中找到:

      LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
      

      PackageManagerInternalImpl的resolveIntent方法在其父类PackageManagerInternalBase中定义:

      public final ResolveInfo resolveIntent(Intent intent, String resolvedType,
              @PackageManager.ResolveInfoFlagsBits long flags,
              @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
              boolean resolveForStart, int filterCallingUid) {
          return getResolveIntentHelper().resolveIntentInternal(snapshot(),
                  intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
                  filterCallingUid);
      }
      

      ResolveIntentHelper的resolveIntentInternal方法如下:

      public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
              @PackageManager.ResolveInfoFlagsBits long flags,
              @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
              boolean resolveForStart, int filterCallingUid) {
          try {
              ...
              final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
                      resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
                      resolveForStart, true /*allowDynamicSplits*/);
              ...
              final ResolveInfo bestChoice = chooseBestActivity(computer, intent, resolvedType, flags, privateResolveFlags, query, userId, queryMayBeFiltered);
              ...
              return bestChoice;
          } ...
      }
      

      computer是前面snapshot()方法返回的对象:

      @Override
      public final Computer snapshot() {
          return mService.snapshotComputer();
      }
      

      mService是PackageManagerService,它的snapshotComputer方法会返回一个ComputerEngine对象,computer就是它。

      ComputerEngine的queryIntentActivitiesInternal方法如下:

      public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
              String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
              @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
              int filterCallingUid, int userId, boolean resolveForStart,
              boolean allowDynamicSplits) {
          ...
          ComponentName comp = intent.getComponent();
          ...
          List<ResolveInfo> list = Collections.emptyList();
            //显示启动,指定目标Activity类名形式的跳转
          if (comp != null) {
              final ActivityInfo ai = getActivityInfo(comp, flags, userId);
              if (ai != null) {
                  ...
                  final ResolveInfo ri = new ResolveInfo();
                  ri.activityInfo = ai;
                    //可见这里最终返回只含有一个ResolveInfo的集合
                  list = new ArrayList<>(1);
                  list.add(ri);
                  PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
                          mInjector.getCompatibility(), mComponentResolver,
                          list, false, intent, resolvedType, filterCallingUid);
              }
          } else {
                //隐式启动,匹配action、type、data等形式的跳转,这种方式下list可能有多个匹配的
              ...
              //queryIntentActivitiesInternalBody开始查找匹配项
              //tip:解析匹配uri、action的逻辑在IntentResolver的queryIntent方法中
              QueryIntentActivitiesResult lockedResult =
                      queryIntentActivitiesInternalBody(
                              intent, resolvedType, flags, filterCallingUid, userId,
                              resolveForStart, allowDynamicSplits, pkgName, instantAppPkgName);
              ...
              if (lockedResult.sortResult) {
                    //按照优先级排序,优先级越高越靠前
                  lockedResult.result.sort(RESOLVE_PRIORITY_SORTER);
              }
              list = lockedResult.result;
              }
          }
            ...
          return list;
      }
      

      这里根据Intent的查找方式分成了两种,一种是Intent(this, destActivity.class) 这种指定了具体目标Activity的显示启动,另一种是类似intent.setAction() 这种泛化匹配的隐式启动,对于前者来说只会查到一个ActivityInfo,因此返回的集合中只会有一个ResolveInfo;后者可能会匹配到多个ActivityInfo,返回的集合中可能会有多个ResolveInfo。

      回到resolveIntentInternal方法,接下来会调用chooseBestActivity方法:

      private ResolveInfo chooseBestActivity(Computer computer, Intent intent, String resolvedType,
              @PackageManager.ResolveInfoFlagsBits long flags,
              @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
              List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
          if (query != null) {
              final int n = query.size();
                //对于显示启动来说只是取出list中唯一的ResolveInfo
              if (n == 1) {
                  return query.get(0);
              } else if (n > 1) {
                    //对于隐式启动
                  final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
                  //如果第一个匹配项有着更高且唯一的优先级,也就是说第二个匹配项必须低于第一个(之前排好序了)
                  ResolveInfo r0 = query.get(0);
                  ResolveInfo r1 = query.get(1);
                  ...
                  if (r0.priority != r1.priority
                          || r0.preferredOrder != r1.preferredOrder
                          || r0.isDefault != r1.isDefault) {
                      return query.get(0);
                  }
                  //如果优先级筛选没有决出唯一的一个ResolveInfo则先尝试获取首选项(本地保存记录)
                  ResolveInfo ri = mPreferredActivityHelper.findPreferredActivityNotLocked(computer,
                          intent, resolvedType, flags, query, true, false, debug,
                          userId, queryMayBeFiltered);
                    
                  if (ri != null) {
                      return ri;
                  }
                  ...
                  //mResolveInfoSupplier.get()会获取一个默认的(ResolverActivity)或者自定义的Activity用于让用户选择哪一个Activity
                  ri = new ResolveInfo(mResolveInfoSupplier.get());
                  ri.activityInfo = new ActivityInfo(ri.activityInfo);
                  ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
                  final String intentPackage = intent.getPackage();
                    //如果所有的目标Activity都来自同一个package的话则直接使用第一个的ApplicationInfo
                  if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
                      final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
                      ri.resolvePackageName = intentPackage;
                      if (mUserNeedsBadging.get(userId)) {
                          ri.noResourceId = true;
                      } else {
                          ri.icon = appi.icon;
                      }
                      ri.iconResourceId = appi.icon;
                      ri.labelRes = appi.labelRes;
                  }
                    //如果多个目标Activity来自不同的包则使用mResolveInfoSupplier.get()的ApplicationInfo
                  ri.activityInfo.applicationInfo = new ApplicationInfo(
                          ri.activityInfo.applicationInfo);
                  if (userId != 0) {
                      ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
                              UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
                  }
                  //汽车应用相关
                    // Make sure that the resolver is displayable in car mode
                  if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
                  ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
                  return ri;
              }
          }
        return null;
      }
      

      首选项是用户之前选择过的默认项,mPreferredActivityHelper.findPreferredActivityNotLocked最终是从pm.Settings中的mPreferredActivities中根据userId来获取PreferredIntentResolver实例,如果没取到则使用mResolveInfoSupplier.get()获取,来源逻辑如下:

      //PackageManagerService:
      //倒数第二个参数,所以get()获取的是mResolveInfo
      mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
                      injector.getCompatibility(), mUserManager, mDomainVerificationManager,
                      mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
      

      mResolveInfo有两个途径赋值,一个是在setPlatformPackage方法中:

      void setPlatformPackage(AndroidPackage pkg, PackageSetting pkgSetting) {
          synchronized (mLock) {
              // Set up information for our fall-back user intent resolution activity.
              mPlatformPackage = pkg;
      
              // The instance stored in PackageManagerService is special cased to be non-user
              // specific, so initialize all the needed fields here.
              mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
                      PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
      
              if (!mResolverReplaced) {
                  mResolveActivity.applicationInfo = mAndroidApplication;
                  mResolveActivity.name = ResolverActivity.class.getName();
                  ...//mResolveActivity的其他信息设置
                  mResolveInfo.activityInfo = mResolveActivity;
                  ...
                  mResolveComponentName = new ComponentName(
                          mAndroidApplication.packageName, mResolveActivity.name);
              }
              PackageManagerService.onChanged();
          }
          applyUpdatedSystemOverlayPaths();
      }
      

      setPlatformPackage方法是在InstallPackageHelper的commitPackageSettings方法中调用,而commitPackageSettings方法是在之前的安装流程中通过commitPackagesLocked -> commitReconciledScanResultLocked方法中调用的:

      if (pkg.getPackageName().equals("android")) {
          mPm.setPlatformPackage(pkg, pkgSetting);
      }
      

      可见,这是系统包的默认配置,也就是说对于多个匹配目标的情况下系统默认会提供一个可供用户选择的Activity,而在setPlatformPackage方法中我们可以看到,这个Activity就是ResolverActivity

      mResolveInfo的另一个途径是自定义途径,提供一个可供厂商自定义此Activity的入口,通过setUpCustomResolverActivity方法赋值:

      void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
        synchronized (mLock) {
              ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
                    PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
              // Set up information for custom user intent resolution activity.
              mResolveActivity.applicationInfo = appInfo;
              mResolveActivity.name = mCustomResolverComponentName.getClassName();
              mResolveActivity.packageName = pkg.getPackageName();
              mResolveActivity.processName = pkg.getProcessName();
              mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
              mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
                    | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
                mResolveInfo.activityInfo = mResolveActivity;
              ...
          }
      }
      

      可以看到,mResolveActivity.name的值来自mCustomResolverComponentName。setUpCustomResolverActivity方法同样是在commitPackageSettings方法中调用的:

      final String pkgName = pkg.getPackageName();
      if (mPm.mCustomResolverComponentName != null
              && mPm.mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
          mPm.setUpCustomResolverActivity(pkg, pkgSetting);
      }
      

      mCustomResolverComponentName是在PackageManagerService构造方法中赋值的:

      String customResolverActivityName = Resources.getSystem().getString(
              R.string.config_customResolverActivity);
      if (!TextUtils.isEmpty(customResolverActivityName)) {
          mCustomResolverComponentName = ComponentName.unflattenFromString(
                  customResolverActivityName);
      }
      

      可见,厂商是通过配置config_customResolverActivity属性来设置自定义的“ResolverActivity”。

      因为系统package的安装要优先于其他package,因此setPlatformPackage会优先设置,之后厂商package如果符合条件时setUpCustomResolverActivity才会被调用,他们都是设置的mResolveInfo,所以如果厂商设置了自定义“ResolverActivity”的话则会覆盖掉系统默认的ResolverActivity

      ResolverActivity是如何发挥作用的,因为也没看到把查询到的所有匹配的ActivityInfo数据传递给它啊?我们去ResolverActivity(用的是9.0的源码)中去看看有没有答案。

      通过查看其布局文件resolver_list_with_default.xml(其中之一的情况,无所谓这里只需要了解个大概界面)得知,它含有一个ListView来盛放所有匹配的app项,我们找到了它的adapter设置的内部类ItemClickListener,在其onItemClick方法中会调用startSelected方法:

      public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
          ...
          //从其data中取出TargetInfo
          TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered);
          if (target == null) {
              return;
          }
          if (onTargetSelected(target, always)) {
              ...
              finish();
          }
      }
      

      onTargetSelected方法如下:

      protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
          final ResolveInfo ri = target.getResolveInfo();
          final Intent intent = target != null ? target.getResolvedIntent() : null;
            //保存本次选中的启动首选项
          if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem())
                  && mAdapter.mUnfilteredResolveList != null) {
              IntentFilter filter = new IntentFilter();
              ...//根据intent的action、data、category等生成intentFilter
                //IntentFilter在这里就是用于本次启动的匹配信息的,以便再次启动时复用
              if (filter != null) {
                  ...//一些保存信息的生成        
                  //保存选择记录
                  //tip:IPackageManager的实现类是IPackageManagerBase
                  if (alwaysCheck) {
                        ...
                      //即调用IPackageManager的addPreferredActivity
                      final PackageManager pm = getPackageManager();
                      pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
                        ...
                  } else {
                        //最终也是调用IPackageManager的addPreferredActivity
                        //tip:ResolveListController.setLastChosen方法中调用AppGlobals.getPackageManager().setLastChosenActivity方法,AppGlobals.getPackageManager()获取的是IPackageManager
                      mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
                  }
              }
          }
            //启动本次选中的目标Activity
          if (target != null) {
              safelyStartActivity(target);
          }
          return true;
      }
      

      这个方法里会先保存本次选中的启动首选项,还记得之前chooseBestActivity中的PreferredActivityHelper.findPreferredActivityNotLocked操作吗?这里的PackageManager的addPreferredActivity操作与之呼应上了。最后启动选中的目标Activity。

      流程对上了,我们的重点来到了target是怎么来的,它来自targetInfoForPosition方法,这个方法只是根据index从ResolverListAdapter的mDisplayList中返回,mDisplayList是怎么来的呢?它通过addResolveInfo方法添加,这个方法是由onCreate->configureContentView->mAdapter.rebuildList()->finishRebuildingListWithFilteredResults->processSortedList(currentResolveList)调用的:

      private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
        for (int i = 1; i < sortedComponents.size(); i++) {
           ...
           ResolvedComponentInfo rci = sortedComponents.get(i);
           ...
           processGroup(sortedComponents, start, (i - 1), rci, r0Label);
           ...
           start = i;
        }
      }
      

      processGroup是去除重复的项,并且调用addResolveInfoWithAlternates方法,这个方法里再调用addResolveInfo方法添加到mDisplayList中:

      private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
              CharSequence extraInfo, CharSequence roLabel) {
          ...
          final Intent intent = rci.getIntentAt(0);
          final ResolveInfo add = rci.getResolveInfoAt(0);
          final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
          final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
                  extraInfo, replaceIntent);
          ...
          addResolveInfo(dri);
          ...
      }
      

      继续看上面processSortedList方法的sortedComponents方法的来源,它来自finishRebuildingListWithFilteredResults方法的传入参数。回到ResolverListAdapter的rebuildList方法:

      protected boolean rebuildList(boolean doPostProcessing) {
          mDisplayList.clear();
          List<ResolvedComponentInfo> currentResolveList = getInitialRebuiltResolveList();
            ...
          boolean result =
                  finishRebuildingListWithFilteredResults(currentResolveList, doPostProcessing);
          return result;
      }
      

      可以看到,传入参数是currentResolveList,由getInitialRebuiltResolveList方法返回:

      List<ResolvedComponentInfo> getInitialRebuiltResolveList() {
          if (mBaseResolveList != null) {
              List<ResolvedComponentInfo> currentResolveList = new ArrayList<>();
              mResolverListController.addResolveListDedupe(currentResolveList,
                      mResolverListCommunicator.getTargetIntent(),
                      mBaseResolveList);
              return currentResolveList;
          } else {
              return mResolverListController.getResolversForIntent(
                              /* shouldGetResolvedFilter= */ true,
                              mResolverListCommunicator.shouldGetActivityMetadata(),
                              mIntents);
          }
      }
      

      mResolverListController.getResolversForIntent最底层是调用了mpm.queryIntentActivitiesAsUser方法,mpm查看来源发现是通过ContextImpl的getPackageManager返回的ApplicationPackageManager对象,ApplicationPackageManager的queryIntentActivitiesAsUser方法调用了IPackageManagerBase的queryIntentActivities方法拿到一个ParceledListSlice<ResolveInfo>,然后调用它的getList()把它的mList返回了,所以我们的重点来到了queryIntentActivities方法:

      public final @NonNull
      ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
              String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
          return new ParceledListSlice<>(snapshot().queryIntentActivitiesInternal(intent,
                      resolvedType, flags, userId));
      }
      

      传给ParceledListSlice构造方法的正是赋值给它的mList的,snapshot()我们之前已经知道它是ComputerEngine,它的queryIntentActivitiesInternal方法熟悉吧,正是我们开启这一切的起点,终于真相大白了,之所以chooseBestActivity方法中针对多匹配的情况时没有把所有的匹配项都传给ResolverActivity,是因为在ResolverActivity中会再次调用queryIntentActivitiesInternal查询匹配项。

      Ok!顺带着我们已经搞明白了启动Activity时对于隐式启动的处理逻辑,我们下面要回到主任务中,毕竟我们的目标是分析启动信息是怎么获取的。

    • getActivityInfo

      上面的逻辑只是针对ActivityInfo的处理逻辑,那么ActivityInfo是怎么来的呢?

      ComputerEngine的getActivityInfo方法最终调用了getActivityInfoInternalBody方法:

      protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
              @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
          ParsedActivity a = mComponentResolver.getActivity(component);
          AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
          if (pkg != null && mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {
              PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
              if (ps == null) return null;
              if (shouldFilterApplication(
                      ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
                  return null;
              }
              return PackageInfoUtils.generateActivityInfo(pkg,
                      a, flags, ps.getUserStateOrDefault(userId), userId, ps);
          }
            //如果上面没返回,则看是否是“ResolverActivity”
          if (resolveComponentName().equals(component)) {
              return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity,
                      flags, PackageUserStateInternal.DEFAULT, userId);
          }
            //都不是则返回null
          return null;
      }
      

      mComponentResolver类型是ComponentResolverApi,我们到PackageManagerService的main方法中的injector的构造逻辑中找到了它的实现类ComponentResolver,它的getActivity方法定义在其父类ComponentResolverLocked中,内部又是调用了super的getActivity方法,也就是ComponentResolverBase:

      //ComponentResolverBase:
      @Override
      public ParsedActivity getActivity(@NonNull ComponentName component) {
          return mActivities.mActivities.get(component);
      }
      

      mActivities中的数据是怎么来的?第一个mActivities是ComponentResolver.ActivityIntentResolver,它的mActivities是ArrayMap<ComponentName, ParsedActivity>,这个ArrayMap通过它的addActivity方法添加数据,这个addActivity方法又是被ComponentResolver.ActivityIntentResolver的addActivitiesLocked调用的:

      private void addActivitiesLocked(AndroidPackage pkg,
              List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
          final int activitiesSize = ArrayUtils.size(pkg.getActivities());
          ...
          for (int i = 0; i < activitiesSize; i++) {
              ParsedActivity a = pkg.getActivities().get(i);
              mActivities.addActivity(a, "activity", newIntents);
              ...
          }
          ...
      }
      

      该方法又被ComponentResolver的addAllComponents方法调用,而addAllComponents又在PackageManagerService的commitPackageSettings方法中被调用,这正是我们之前分析的安装流程中的一个必执行方法。

      获取到ParsedActivity之后,再根据它的packageName去mPackages中获取AndroidPackage,而mPackages是在InstallPackageHelper的commitPackageSettings方法中通过mPm.mPackages直接添加数据的:

      mPm.mPackages.put(pkg.getPackageName(), pkg);
      

      AndroidPackage中的activity信息是怎么来的呢?

      那我们得再回到之前安装流程中找一下AndroidPackage是怎么来的,AndroidPackage通过parsedPackage.hideAsFinal()获取,parsedPackage又通过PackageParser2的parsePackage方法获取:

      public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
              List<File> frameworkSplits) throws PackageManagerException {
          ...
          ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
                  frameworkSplits);
          ...
          ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
          return parsed;
      }
      
      public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
              List<File> frameworkSplits) {
          if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
                  && frameworkSplits.size() > 0
                  && packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
              return parseClusterPackage(input, packageFile, frameworkSplits, flags);
          } else if (packageFile.isDirectory()) {
              return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
          } else {
              return parseMonolithicPackage(input, packageFile, flags);
          }
      }
      
      private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
              List<File> frameworkSplits, int flags) {
          ...
          final ParseResult<PackageLite> liteResult =
                  ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits,
                          liteParseFlags);
          ...
          try {
              final File baseApk = new File(lite.getBaseApkPath());
              final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
                      lite.getPath(), assetLoader, flags);
              if (result.isError()) {
                  return input.error(result);
              }
      
              ParsingPackage pkg = result.getResult();
              ...
              return input.success(pkg);
          } ...
      }
      

      parseClusterPackage中会调用ApkLiteParseUtils.parseClusterPackageLite方法,该方法中会遍历apkDirectory下的所有apk文件,对每个apk文件调用parseApkLite方法,最终是调用parseApkLiteInner方法:

      private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
              File apkFile, FileDescriptor fd, String debugPathName, int flags) {
          final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
          XmlResourceParser parser = null;
            //这里会使用ApkAssets加载apk文件
          ApkAssets apkAssets = null;
          try {
              try {
                  apkAssets = fd != null
                          ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
                          : ApkAssets.loadFromPath(apkPath);
              } ...
                    //ANDROID_MANIFEST_FILENAME是AndroidManifest.xml
              parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
                    ...
              return parseApkLite(input, apkPath, parser, signingDetails, flags);
          } ...
      }
      

      调用parseApkLite方法来解析AndroidManifest.xml配置,下面只贴部分代码以明白大概做了什么即可:

      private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
              XmlResourceParser parser, SigningDetails signingDetails, int flags)
              throws IOException, XmlPullParserException {
            //解析manifest标签下的split属性、package属性
          ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
            ...
          Pair<String, String> packageSplit = result.getResult();
            ...
          //安装位置
          int installLocation = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
                  "installLocation", PARSE_DEFAULT_INSTALL_LOCATION);
            //versionCode
          int versionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "versionCode", 0);
          ...
          int type;
          final int searchDepth = parser.getDepth() + 1;
          final List<VerifierInfo> verifiers = new ArrayList<>();
          while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                  && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
              ...
              if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
                  ...
              } else if (TAG_APPLICATION.equals(parser.getName())) {
                  ...//解析application标签
              } else if...
                } else if (TAG_USES_SDK.equals(parser.getName())) {
                  //sdk版本相关
                  String minSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
                          "minSdkVersion");
                  String targetSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
                          "targetSdkVersion");
                            ...
              }
          }
            ...
          return input.success(new ApkLite(codePath, ...));
      }
      

      parseApkLite主要是解析出一些大方面的配置,和系统相关的,比如安装位置、版本号、sdk信息等,而像应用本身的name、label、icon等其他特性信息会在随后的parseBaseApk方法中解析:

      private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
              String codePath, Resources res, XmlResourceParser parser, int flags)
              throws XmlPullParserException, IOException {
          ...
          final String pkgName;
            ...
          final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
          try {
              final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
                      "coreApp",false);
              final ParsingPackage pkg = mCallback.startParsingPackage(
                      pkgName, apkPath, codePath, manifestArray, isCoreApp);
              final ParseResult<ParsingPackage> result =
                      parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
              if (result.isError()) {
                  return result;
              }
      
              return input.success(pkg);
          } finally {
              manifestArray.recycle();
          }
      }
      

      mCallback.startParsingPackage返回的是PackageImpl,是AndroidPackage的子类,这也就是最终要返回的的AndroidPackage对象。parseBaseApkTags方法中会解析出和应用本身相关的信息:

      private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
              TypedArray sa, Resources res, XmlResourceParser parser, int flags)
              throws XmlPullParserException, IOException {
          ...
          final int depth = parser.getDepth();
          int type;
          while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                  && (type != XmlPullParser.END_TAG
                  || parser.getDepth() > depth)) {
              if (type != XmlPullParser.START_TAG) {
                  continue;
              }
              String tagName = parser.getName();
              final ParseResult result;
              if (TAG_APPLICATION.equals(tagName)) {
                    ...
                  //解析<application>信息以及子标签信息,比如注册的四大组件<activity>、<service>等
                  result = parseBaseApplication(input, pkg, res, parser, flags);
                  ...
              } else {
                    //解析和application同级的其他标签,比如<uses-permission>等
                  result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
              }
                ...
          }
            ...
          return input.success(pkg);
      }
      

      parseBaseApplication方法如下:

      private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
              ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
              throws XmlPullParserException, IOException {
          final String pkgName = pkg.getPackageName();
          int targetSdk = pkg.getTargetSdkVersion();
      
          TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
          ...//application标签的信息解析,比如name、label等
      
          //子标签信息的解析
          final int depth = parser.getDepth();
          int type;
          while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                  && (type != XmlPullParser.END_TAG
                  || parser.getDepth() > depth)) {
              if (type != XmlPullParser.START_TAG) {
                  continue;
              }
      
              final ParseResult result;
              String tagName = parser.getName();
              switch (tagName) {
                  case "activity":
                      isActivity = true;
                  case "receiver":
                      ParseResult<ParsedActivity> activityResult =
                              ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                      res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
                                      input);
      
                      if (activityResult.isSuccess()) {
                            //拿到的是ParsedActivityImpl
                          ParsedActivity activity = activityResult.getResult();
                            //添加到ParsingPackageImpl中的activities集合中
                          pkg.addActivity(activity);
                      }
      
                      result = activityResult;
                      break;
                  case "service":
                      ParseResult<ParsedService> serviceResult =
                              ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
                                      flags, sUseRoundIcon, null /*defaultSplitName*/,
                                      input);
                      if (serviceResult.isSuccess()) {
                          ParsedService service = serviceResult.getResult();
                          hasServiceOrder |= (service.getOrder() != 0);
                          pkg.addService(service);
                      }
      
                      result = serviceResult;
                      break;
                  ...
                  default:
                      result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                      break;
              }
                    ...
          }
            ...
          return input.success(pkg);
      }
      

      可以看到,activity和receiver组件都是通过ParsedActivityUtils.parseActivityOrReceiver方法解析的,具体逻辑就不看了,没什么特别的。

      总结一下,在安装过程中会解析apk的AndroidManifest.xml文件,然后把解析的activity信息保存到AndroidPackage(实际类型是其子类PackageImpl)的activities集合(在其父类ParsingPackageImpl中定义)中,然后在随后的commitPackageSettings方法流程中调用addAllComponents方法将所有的activity信息交给PackageManagerService中的mComponentResolver(ComponentResolver),之后在启动Activity时调用getActivity方法就会使用mComponentResolver获取AndroidPackage(也就是PackageImpl),最后调用PackageInfoUtils.generateActivityInfo方法生成ActivityInfo。

    相关文章

      网友评论

          本文标题:Apk安装的源码分析(三)

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