PMS APK解析数据同步和配置更新

        前面学到PMS构造函数中调用scanDirTraceLI()方法扫描系统app/priv-app/framework等目录下的apk文件,然后调用通过ParallelPackageParser提交(submit())apk给PackageParser解析,PackageParser调用parserPackage()方法解析每一个apk,并将四大组件和其他相关等信息解析出来放入package对象中,然后将所有的package对象放入集合mPackages中。然后PMS的包扫描和解析过程基本上就算是完成了。但扫描和解析完成之后,关于apk的信息都存储在队列中,我们还需要将它们从队列中取出来,同步到PMS的属性中,然后将这些apk的信息在packages.xml和packages.list文件中更新。
        这里我们来学习扫描和解析完成之后,apk数据的同步和配置更新。
        在使用线程池执行所有的apk解析后,所有的解析结果都保存在队列中,系统会循环调用take()方法取出解析的结果。

for (; fileCount > 0; fileCount--) {
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                    currentTime, null);
                }

    1  scanPackageChildLI() 获取扫描到的ParseResult中的Pakcage对象

          取出apk文件解析结果后,调用scanPackageChildLI()扫描获取到的ParseResult中的Pakcage对象,scanPackageChildLI()中直接调用addForInitLI()方法.

//addForInitLI()
synchronized (mPackages) {
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); // 1、获取从配置文件中读取的Settings

final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); // 调用scanPackageNewLI将pkg中的数据保存到PMS的变量中
}

// scanPackageNewLI():创建ScanRequest对象,执行文件扫描修改或更新对应的PackageSetting对象
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
        pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
        originalPkgSetting, realPkgName, parseFlags, scanFlags,
        (pkg == mPlatformPackage), user);
final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime); 
if (result.success) {
    commitScanResultsLocked(request, result); // 调用commitScanResultsLocked()
}

        commitScanResultsLocked()中直接调用commitPackageSettings()处理apk的解析数据 。

commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags,
(parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/); // 提交包解析数据

    2    commitPackageSettings()将package信息保存到PMS内部变量中

         commitPackageSettings()方法主要是将解析得到的Package中的信息保存到PMS内部变量中,并创建程序包所需的各种文件信息。

private void commitPackageSettings(PackageParser.Package pkg,
            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
            final @ScanFlags int scanFlags, boolean chatty) {
final String pkgName = pkg.packageName;
if (pkg.packageName.equals("android")) { // 1、针对系统包,特殊处理属性的初始化
                   mPlatformPackage = pkg;
               pkg.mVersionCode = mSdkVersion;
               mAndroidApplication = pkg.applicationInfo;
                mResolveActivity.applicationInfo = mAndroidApplication;
               mResolveActivity.name = ResolverActivity.class.getName();
......
               mResolveComponentName = new ComponentName(
            mAndroidApplication.packageName, mResolveActivity.name);
}
                int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
                for (int i=0; i<N; i++) {
                String file = mSharedLibraries.get(pkg.usesLibraries.get(i)); 
                    mTmpSharedLibraries[num] = file;   // 遍历所有的user-library标签保存在数组中
                num++;
                }
            if (!verifySignaturesLP(pkgSetting, pkg)) { // 校验签名文件
  ……. 
}
 int N = pkg.providers.size();
            StringBuilder r = null;
            int i;
            for (i=0; i<N; i++) {
            PackageParser.Provider p = pkg.providers.get(i); //遍历提取每个provider
                p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        p.info.processName, pkg.applicationInfo.uid);
                mProvidersByComponent.put(new ComponentName(p.info.packageName,
                        p.info.name), p); // 针对Provider创建ComponentName对象,保存在mProvidersByComponent集合中
               if (p.info.authority != null) {
                    String names[] = p.info.authority.split(";"); // 获取Provider的权限信息
                    p.info.authority = null;
                    for (int j = 0; j < names.length; j++) {
                        if (j == 1 && p.syncable) {
                            p = new PackageParser.Provider(p);
                            p.syncable = false;
                        }
                        if (!mProviders.containsKey(names[j])) {
                            mProviders.put(names[j], p); // 将权限和对应的Provider以键值对保存在 mProviders 集合中
                    }
                }
            }
      }
N = pkg.services.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Service s = pkg.services.get(i);
                s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        s.info.processName, pkg.applicationInfo.uid);
                mServices.addService(s);
N = pkg.receivers.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.receivers.get(i);
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        a.info.processName, pkg.applicationInfo.uid);
                mReceivers.addActivity(a, "receiver");
            }

 N = pkg.activities.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.activities.get(i);
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        a.info.processName, pkg.applicationInfo.uid);
                mActivities.addActivity(a, "activity");
}

        在commitPackageSettings中,主要是将每个apk文件获得的Package对象中保存的四大组件信息分别提取保存在PMS内部对应的属性中,在PMS内部有4个专门储存四大组件的属性:

final ActivityIntentResolver mActivities = new ActivityIntentResolver();
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
final ServiceIntentResolver mServices = new ServiceIntentResolver();
final ProviderIntentResolver mProviders = new ProviderIntentResolver();

1.ActivityIntentResolver.addActivity():处理Activity和Receiver分别保存在各自的ArrayMap中 

public final void addActivity(PackageParser.Activity a, String type) {
    mActivities.put(a.getComponentName(), a); // 1、获取内部的Component对象,在Activity会自动创建Component对象
    final int NI = a.intents.size();
    for (int j=0; j<NI; j++) {
        PackageParser.ActivityIntentInfo intent = a.intents.get(j); // 获取Activity中设置的intent
        addFilter(intent); // 添加Intent过滤
    }
}

2.ServiceIntentResolver.addActivity():将Package中极细获取的Service对象,保存在ArrayMap中

public final void addService(PackageParser.Service s) {
    mServices.put(s.getComponentName(), s); //保存service
    final int NI = s.intents.size();
    int j;
    for (j=0; j<NI; j++) {
        PackageParser.ServiceIntentInfo intent = s.intents.get(j);
        addFilter(intent);
    }
}

 3.ProviderIntentResolver.addActivity():将Package中极细获取的Provider对象,保存在ArrayMap中。

public final void addProvider(PackageParser.Provider p) {
    if (mProviders.containsKey(p.getComponentName())) {
        return;
    }
    mProviders.put(p.getComponentName(), p); // 保存provider
  
    final int NI = p.intents.size();
    int j;
    for (j = 0; j < NI; j++) {
        PackageParser.ProviderIntentInfo intent = p.intents.get(j);
        addFilter(intent); // 添加查找过滤的intent
    }
}

3  updateAllSharedLibrariesLPw同步共享Library

        接着是调用updateAllSharedLibrariesLPw更新所有package的usesLibraryFiles数据,为什么要做更新?因为package在AndroidManifest里头使用use-library描述要引用的library,在系统mSharedLibraries里可能是未定义的,所以,需要做数据同步。app可以在Manifest里头声明自身运行需要额外的library,但是,这里仅仅只是声明,最终还是要看系统有没有配置这个shareLibrary,系统配置的shareLibrary可在初始化SystemConfig时知道,如果app的uses-library在系统中不存在,apk会安装失败。

private ArrayList<PackageParser.Package> updateAllSharedLibrariesLPw(
            PackageParser.Package changingPkg) {
        ArrayList<PackageParser.Package> res = null;
        for (PackageParser.Package pkg : mPackages.values()) {
            if (changingPkg != null
                    && !hasString(pkg.usesLibraries, changingPkg.libraryNames)
                    && !hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)
                    && !ArrayUtils.contains(pkg.usesStaticLibraries,
                            changingPkg.staticSharedLibName)) {
                return null;
            }
            if (res == null) {
                res = new ArrayList<>();
            }
            res.add(pkg);
            try {
                updateSharedLibrariesLPr(pkg, changingPkg);
            } catch (PackageManagerException e) {
                // If a system app update or an app and a required lib missing we
                // delete the package and for updated system apps keep the data as
                // it is better for the user to reinstall than to be in an limbo
                // state. Also libs disappearing under an app should never happen
                // - just in case.
                if (!pkg.isSystem() || pkg.isUpdatedSystemApp()) {
                    final int flags = pkg.isUpdatedSystemApp()
                            ? PackageManager.DELETE_KEEP_DATA : 0;
                    deletePackageLIF(pkg.packageName, null, true, sUserManager.getUserIds(),
                            flags , null, true, null);
                }
                Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
            }
        }
        return res;
    }

     4   updateAllPermissions权限处理

        调用updateAllPermissions更新系统permission trees和permission 列表

mPermissionManager.updateAllPermissions(
                    StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
                    mPermissionCallback);
            ver.sdkVersion = mSdkVersion;

在PMS构造时,会调用SystemConfig.getPermissions()获取系统builtin权限数据 

 

 // Propagate permission configuration in to package manager.
            ArrayMap<String, SystemConfig.PermissionEntry> permConfig
                    = systemConfig.getPermissions();
            for (int i=0; i<permConfig.size(); i++) {
                SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
                BasePermission bp = mSettings.mPermissions.get(perm.name);
                if (bp == null) {
                    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
                    mSettings.mPermissions.put(perm.name, bp);
                }
                if (perm.gids != null) {
                    bp.gids = appendInts(bp.gids, perm.gids);
                }
            }

        遍历systemConfig定义的permission时,接着判断mSettings.mPermissions是否已经存在该权限的定义,如果不存在,新建一个BasePermission,权限source package为android,type为builtin,接着更新权限对应的supplementary gids;

扫描apk时权限数据的初始化:

//PackageParser.parsePermission
private Permission parsePermission(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, String[] outError)
        throws XmlPullParserException, IOException {
        Permission perm = new Permission(owner);
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestPermission);
        if (!parsePackageItemInfo(owner, perm.info, outError,
                "<permission>", sa,
                com.android.internal.R.styleable.AndroidManifestPermission_name,
                com.android.internal.R.styleable.AndroidManifestPermission_label
                com.android.internal.R.styleable.AndroidManifestPermission_icon,
                com.android.internal.R.styleable.AndroidManifestPermission_logo,
                com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
            sa.recycle();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return null;
        }
...
       owner.permissions.add(perm);
        return perm;
}

        permission和permission tree都保存到Package.permissions列表中,通过字段perm.tree是true还是false来区分是否是permissiontree
在app扫描结束后,permission数据已经被全部拿到,但是现在数据都还只是保存在Package内部,所以还需要将permission数据添加到PMS permission Map。

        对大部分app来说,如果要使用某权限,必须要在manifest进行声明, 比如:
<uses-permissionandroid:name="android.permission.INTERNET" />
        然后PackageParser. parseBaseApk时,会调用parseUsesPermission对声明解析使用的权限数据。

5  mSettings.writeLPr()配置更新
        mSettings.writeLPr():将mPackages中的数据分别写入package.xml和package.list文件中

void writeLPr() {
        if (mSettingsFilename.exists()) {
            if (!mBackupSettingsFilename.exists()) { // 重命名文件
                if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
                    Slog.wtf(PackageManagerService.TAG,
                            "Unable to backup package manager settings, "
                            + " current changes will be lost at reboot");
                    return;
                }
            } else {
                mSettingsFilename.delete(); //删除package文件
            }
        }
FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
BufferedOutputStream str = new BufferedOutputStream(fstr);
 ......
 for (final PackageSetting pkg : mPackages.values()) {
 writePackageLPr(serializer, pkg); // 写入配置信息
 }
 ......
}
writePackageListLPr(); // 更新package.list文件

主要是以下几件事:
1.先判断packages.xml和backup.xml文件是否存在,如果两个都存在则删除packages.xml
2.如果backup.xml文件不存在,则将packages.xml重命名为backup.xml
3.创建新的packages.xml文件,并将mSetting中的内容写入文件夹
4.删除backup文件,并重新生成packages.list文件

        至此apk的扫描和解析的数据同步以及配置更新就完成了,关于权限的处理,Android源码的更新比较快,我上述写的方法是Android9的源码中的逻辑,在Android的早些版本的源码中调用的是updatePermissionsLPw这个方法循环遍历mSettings.mPackages集合来将所有的应用赋予权限,而最后的writeLPr方法则是将一些应用信息保存到packages.xml文件中。

参考链接:https://blog.csdn.net/Alexwll/article/details/102777742

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值