侧边栏壁纸
博主头像
慢行的骑兵博主等级

贪多嚼不烂,欲速则不达

  • 累计撰写 29 篇文章
  • 累计创建 27 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

PMS服务启动原理

慢行的骑兵
2021-09-24 / 0 评论 / 0 点赞 / 352 阅读 / 4,003 字
  • 学习目标:从开机到APP启动PMS服务处理机制与流程(即PMS服务启动原理);
  • 笔记总结方式:pms的作用,以及带着问题对源码进行分析;

一.PMS(PackageManagerService)简介

  • 作用

  • PMS用来管理所有的package信息,包括安装、卸载、更新以及解析AndroidManifest.xml以组织相应的数据结构被缓存(为了方便AMS快速定位)到PMS

  • 问题

    • Apk安装是怎么进行的,PMS是如何分析APK压缩文件
    • 开机时PMS做了什么,data/app目录下的扫描与分析
    • PakageParse类源码解读
    • AndroidManifest为什么要按照google设计的写(为什么要写AndroidManifest)
      • 因为AndroidManifest可以描述四大组件,缓存xml比缓存直接的类要好
  • 补充

    • 系统应用安装在system目录(system/app/pakage)下,其它应用是安装在data目录(data/app/pakage)下;
    • PMS发生在开机的时候,AMS发生在用户点击的时候;
    • 设计原则:PMS提前解析,AMS在后面去启动对应的Activity;
    • AMS和PMS的相同点:AMS会去应用PMS解析的数据,然后再去启动对应的Activity对象;

二.源码

  • 先看问题(带着问题看源码)

    • 如何去扫描app的
    • PMS是如何启动的
    • AndroidManifest是如何解析的
    • AndroidManifest解析的信息是如何缓存的
    • Android 8.0、9.0、10.0不同版本的差异
  • 源码分析

    • 从SystemServer(开机的时候就会启动-在initrc配置信息启动该类-孵化器进程启动了SystemServer-调用run方法)类开始分析源码
    //SystemServer类
    private void run() {
        //...
    	startBootstrapServices();
        startCoreServices();
        startOtherServices();
    	//...
    }
    
  • startBootstrapServices

private void startBootstrapServices() {
    //...
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);   
	//...
}

//PackageManagerService类
public static PackageManagerService main(Context context, Installer installer,
    //...
    //构造函数内容剧多,略                                     
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    //...
}
                                         
//PackageManagerService类 成员变量 
File sAppInstallDir;
File sAppLib32InstallDir;                                        
  • PMS的构造函数-非常复杂,超过1000行, 整个开机启动pms最耗时。当电源开机的时候
    • 1.扫描data/app/pakage/base.apk和system/app/pakage/base.apk
    • 2.使用工具类解析AndroidManifest(内部本质的东西是四大组件)的查找
    • 3.缓存:每个app对应的缓存是pakage(一个app对应一个,pakage是存在PMS进程-单独的进程)
  • 接下来分析 pms 对 路径sAppInstallDir和sAppLib32InstallDir做了啥事情
  • 在构造函数内调用了scanTraceDirLI方法(记录性能)--->scanDirLI
  • 分析scanDirLI
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
    //...
    try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
            mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
            mParallelPackageParserCallback)) {
        // Submit files for parsing in parallel
        int fileCount = 0;
        //遍历所有的文件
        for (File file : files) {
            //isApkFile:判断是否以.apk结尾
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            //分析(ParallelPackageParser类)submit
            //使用了线程池(9.0和10.0)  6.0、7.0、8.0未使用线程池
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }
		
        //...
    }
}
  • ParallelPackageParser类submit
public void submit(File scanFile, int parseFlags) {
    mService.submit(() -> {
        ParseResult pr = new ParseResult();
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
        //...
            pr.pkg = parsePackage(pp, scanFile, parseFlags);
        //...
    });
}

protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
       int parseFlags) throws PackageParser.PackageParserException {
   return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}

//PackageParser
public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    //缓存机制
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
        return parsed;
    }
    
    //下方操作非常耗时

    long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
    if (packageFile.isDirectory()) {
        parsed = parseClusterPackage(packageFile, flags);
    } else {
        parsed = parseMonolithicPackage(packageFile, flags);
    }

    long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
    cacheResult(packageFile, flags, parsed);
    if (LOG_PARSE_TIMINGS) {
        parseTime = cacheTime - parseTime;
        cacheTime = SystemClock.uptimeMillis() - cacheTime;
        if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
            Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
                    + "ms, update_cache=" + cacheTime + " ms");
        }
    }
    return parsed;
}

public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
    final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
    if (mOnlyCoreApps) {
        if (!lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + apkFile);
        }
    }

    final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
    try {
        //分析parseBaseApk
        final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
        pkg.setCodePath(apkFile.getCanonicalPath());
        pkg.setUse32bitAbi(lite.use32bitAbi);
        return pkg;
    } catch (IOException e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to get path: " + apkFile, e);
    } finally {
        IoUtils.closeQuietly(assetLoader);
    }
}

//扫描apk文件的AndroidManifest文件
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        throws PackageParserException {
    final String apkPath = apkFile.getAbsolutePath();

    String volumeUuid = null;
    if (apkPath.startsWith(MNT_EXPAND)) {
        final int end = apkPath.indexOf('/', MNT_EXPAND.length());
        volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
    }

    mParseError = PackageManager.INSTALL_SUCCEEDED;
    mArchiveSourcePath = apkFile.getAbsolutePath();

    if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

    XmlResourceParser parser = null;
    try {
        final int cookie = assets.findCookieForPath(apkPath);
        if (cookie == 0) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Failed adding asset path: " + apkPath);
        }
        
        //解析xml信息
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        final Resources res = new Resources(assets, mMetrics, null);

        final String[] outError = new String[1];
        //分析重载的parseBaseApk方法
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
        if (pkg == null) {
            throw new PackageParserException(mParseError,
                    apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
        }

        pkg.setVolumeUuid(volumeUuid);
        pkg.setApplicationVolumeUuid(volumeUuid);
        pkg.setBaseCodePath(apkPath);
        pkg.setSigningDetails(SigningDetails.UNKNOWN);

        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);
    }
}
  • parseBaseApk
//获取版本号
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
    final String splitName;
    final String pkgName;

    try {
        Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
		//找到对应的包名
        pkgName = packageSplit.first;
        splitName = packageSplit.second;

        if (!TextUtils.isEmpty(splitName)) {
            outError[0] = "Expected base APK, but found split " + splitName;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }
    } catch (PackageParserException e) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
        return null;
    }

    if (mCallback != null) {
        String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
        if (overlayPaths != null && overlayPaths.length > 0) {
            for (String overlayPath : overlayPaths) {
                res.getAssets().addOverlayPath(overlayPath);
            }
        }
    }

    final Package pkg = new Package(pkgName);

    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);

    pkg.mVersionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
    pkg.mVersionCodeMajor = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
    pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
    pkg.baseRevisionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
    pkg.mVersionName = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_versionName, 0);
    if (pkg.mVersionName != null) {
        pkg.mVersionName = pkg.mVersionName.intern();
    }

    pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);

    pkg.mCompileSdkVersion = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
    pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
    pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
    if (pkg.mCompileSdkVersionCodename != null) {
        pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
    }
    pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;

    sa.recycle();

	//解析apk的公共信息
    return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
  • parseBaseApkCommon
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
        XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
        IOException {
    mParseInstrumentationArgs = null;

    int type;
    boolean foundApp = false;

    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);

    String str = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
    if (str != null && str.length() > 0) {
        String nameError = validateName(str, true, true);
        if (nameError != null && !"android".equals(pkg.packageName)) {
            outError[0] = "<manifest> specifies bad sharedUserId name \""
                + str + "\": " + nameError;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
            return null;
        }
        pkg.mSharedUserId = str.intern();
        pkg.mSharedUserLabel = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
    }

    pkg.installLocation = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_installLocation,
            PARSE_DEFAULT_INSTALL_LOCATION);
    pkg.applicationInfo.installLocation = pkg.installLocation;

    final int targetSandboxVersion = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_targetSandboxVersion,
            PARSE_DEFAULT_TARGET_SANDBOX);
    pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;

    /* Set the global "on SD card" flag */
    if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
    }

    if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) {
        pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
    }

    // Resource boolean are -1, so 1 means we don't know the value.
    int supportsSmallScreens = 1;
    int supportsNormalScreens = 1;
    int supportsLargeScreens = 1;
    int supportsXLargeScreens = 1;
    int resizeable = 1;
    int anyDensity = 1;

    int outerDepth = parser.getDepth();
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
            continue;
        }

        String tagName = parser.getName();

        if (acceptedTags != null && !acceptedTags.contains(tagName)) {
            Slog.w(TAG, "Skipping unsupported element under <manifest>: "
                    + tagName + " at " + mArchiveSourcePath + " "
                    + parser.getPositionDescription());
            XmlUtils.skipCurrentTag(parser);
            continue;
        }

        if (tagName.equals(TAG_APPLICATION)) {
            if (foundApp) {
                if (RIGID_PARSER) {
                    outError[0] = "<manifest> has more than one <application>";
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                } else {
                    Slog.w(TAG, "<manifest> has more than one <application>");
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }

            foundApp = true;
            if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                return null;
            }
        } else if (tagName.equals(TAG_OVERLAY)) {
            sa = res.obtainAttributes(parser,
                    com.android.internal.R.styleable.AndroidManifestResourceOverlay);
            pkg.mOverlayTarget = sa.getString(
                    com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
            pkg.mOverlayTargetName = sa.getString(
                    com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetName);
            pkg.mOverlayCategory = sa.getString(
                    com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
            pkg.mOverlayPriority = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
                    0);
            pkg.mOverlayIsStatic = sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic,
                    false);
            final String propName = sa.getString(
                    com.android.internal.R.styleable
                    .AndroidManifestResourceOverlay_requiredSystemPropertyName);
            final String propValue = sa.getString(
                    com.android.internal.R.styleable
                    .AndroidManifestResourceOverlay_requiredSystemPropertyValue);
            sa.recycle();

            if (pkg.mOverlayTarget == null) {
                outError[0] = "<overlay> does not specify a target package";
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return null;
            }

            if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
                outError[0] = "<overlay> priority must be between 0 and 9999";
                mParseError =
                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return null;
            }

            // check to see if overlay should be excluded based on system property condition
            if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
                Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
                    + pkg.baseCodePath+ ": overlay ignored due to required system property: "
                    + propName + " with value: " + propValue);
                return null;
            }

            pkg.applicationInfo.privateFlags |=
                ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY;

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_KEY_SETS)) {
            if (!parseKeySets(pkg, res, parser, outError)) {
                return null;
            }
        } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
            if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
                return null;
            }
        } else if (tagName.equals(TAG_PERMISSION)) {
            if (!parsePermission(pkg, res, parser, outError)) {
                return null;
            }
        } else if (tagName.equals(TAG_PERMISSION_TREE)) {
            if (!parsePermissionTree(pkg, res, parser, outError)) {
                return null;
            }
        } else if (tagName.equals(TAG_USES_PERMISSION)) {
            if (!parseUsesPermission(pkg, res, parser)) {
                return null;
            }
        } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
                || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
            if (!parseUsesPermission(pkg, res, parser)) {
                return null;
            }
        } else if (tagName.equals(TAG_USES_CONFIGURATION)) {
            ConfigurationInfo cPref = new ConfigurationInfo();
            sa = res.obtainAttributes(parser,
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
            cPref.reqTouchScreen = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
                    Configuration.TOUCHSCREEN_UNDEFINED);
            cPref.reqKeyboardType = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
                    Configuration.KEYBOARD_UNDEFINED);
            if (sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
                    false)) {
                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
            }
            cPref.reqNavigation = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
                    Configuration.NAVIGATION_UNDEFINED);
            if (sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
                    false)) {
                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
            }
            sa.recycle();
            pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_USES_FEATURE)) {
            FeatureInfo fi = parseUsesFeature(res, parser);
            pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);

            if (fi.name == null) {
                ConfigurationInfo cPref = new ConfigurationInfo();
                cPref.reqGlEsVersion = fi.reqGlEsVersion;
                pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_FEATURE_GROUP)) {
            FeatureGroupInfo group = new FeatureGroupInfo();
            ArrayList<FeatureInfo> features = null;
            final int innerDepth = parser.getDepth();
            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                    continue;
                }

                final String innerTagName = parser.getName();
                if (innerTagName.equals("uses-feature")) {
                    FeatureInfo featureInfo = parseUsesFeature(res, parser);
                    // FeatureGroups are stricter and mandate that
                    // any <uses-feature> declared are mandatory.
                    featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
                    features = ArrayUtils.add(features, featureInfo);
                } else {
                    Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
                            " at " + mArchiveSourcePath + " " +
                            parser.getPositionDescription());
                }
                XmlUtils.skipCurrentTag(parser);
            }

            if (features != null) {
                group.features = new FeatureInfo[features.size()];
                group.features = features.toArray(group.features);
            }
            pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);

        } else if (tagName.equals(TAG_USES_SDK)) {
            if (SDK_VERSION > 0) {
                sa = res.obtainAttributes(parser,
                        com.android.internal.R.styleable.AndroidManifestUsesSdk);

                int minVers = 1;
                String minCode = null;
                int targetVers = 0;
                String targetCode = null;

                TypedValue val = sa.peekValue(
                        com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
                if (val != null) {
                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                        minCode = val.string.toString();
                    } else {
                        // If it's not a string, it's an integer.
                        minVers = val.data;
                    }
                }

                val = sa.peekValue(
                        com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
                if (val != null) {
                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                        targetCode = val.string.toString();
                        if (minCode == null) {
                            minCode = targetCode;
                        }
                    } else {
                        // If it's not a string, it's an integer.
                        targetVers = val.data;
                    }
                } else {
                    targetVers = minVers;
                    targetCode = minCode;
                }

                sa.recycle();

                final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode,
                        SDK_VERSION, SDK_CODENAMES, outError);
                if (minSdkVersion < 0) {
                    mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                    return null;
                }

                final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
                        targetCode, SDK_CODENAMES, outError);
                if (targetSdkVersion < 0) {
                    mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                    return null;
                }

                pkg.applicationInfo.minSdkVersion = minSdkVersion;
                pkg.applicationInfo.targetSdkVersion = targetSdkVersion;
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
            sa = res.obtainAttributes(parser,
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens);

            pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
                    0);
            pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
                    0);
            pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
                    0);

            // This is a trick to get a boolean and still able to detect
            // if a value was actually set.
            supportsSmallScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
                    supportsSmallScreens);
            supportsNormalScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
                    supportsNormalScreens);
            supportsLargeScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
                    supportsLargeScreens);
            supportsXLargeScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
                    supportsXLargeScreens);
            resizeable = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
                    resizeable);
            anyDensity = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
                    anyDensity);

            sa.recycle();

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
            sa = res.obtainAttributes(parser,
                    com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

            // Note: don't allow this value to be a reference to a resource
            // that may change.
            String name = sa.getNonResourceString(
                    com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

            sa.recycle();

            if (name != null) {
                if (pkg.protectedBroadcasts == null) {
                    pkg.protectedBroadcasts = new ArrayList<String>();
                }
                if (!pkg.protectedBroadcasts.contains(name)) {
                    pkg.protectedBroadcasts.add(name.intern());
                }
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_INSTRUMENTATION)) {
            if (parseInstrumentation(pkg, res, parser, outError) == null) {
                return null;
            }
        } else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {
            sa = res.obtainAttributes(parser,
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage);

            String orig =sa.getNonConfigurationString(
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
            if (!pkg.packageName.equals(orig)) {
                if (pkg.mOriginalPackages == null) {
                    pkg.mOriginalPackages = new ArrayList<String>();
                    pkg.mRealPackage = pkg.packageName;
                }
                pkg.mOriginalPackages.add(orig);
            }

            sa.recycle();

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
            sa = res.obtainAttributes(parser,
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage);

            String name = sa.getNonConfigurationString(
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);

            sa.recycle();

            if (name != null) {
                if (pkg.mAdoptPermissions == null) {
                    pkg.mAdoptPermissions = new ArrayList<String>();
                }
                pkg.mAdoptPermissions.add(name);
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals(TAG_USES_GL_TEXTURE)) {
            // Just skip this tag
            XmlUtils.skipCurrentTag(parser);
            continue;

        } else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {
            // Just skip this tag
            XmlUtils.skipCurrentTag(parser);
            continue;
        } else if (tagName.equals(TAG_SUPPORTS_INPUT)) {//
            XmlUtils.skipCurrentTag(parser);
            continue;

        } else if (tagName.equals(TAG_EAT_COMMENT)) {
            // Just skip this tag
            XmlUtils.skipCurrentTag(parser);
            continue;

        } else if (tagName.equals(TAG_PACKAGE)) {
            if (!MULTI_PACKAGE_APK_ENABLED) {
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
            if (!parseBaseApkChild(pkg, res, parser, flags, outError)) {
                // If parsing a child failed the error is already set
                return null;
            }

        } else if (tagName.equals(TAG_RESTRICT_UPDATE)) {
            if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
                sa = res.obtainAttributes(parser,
                        com.android.internal.R.styleable.AndroidManifestRestrictUpdate);
                final String hash = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestRestrictUpdate_hash, 0);
                sa.recycle();

                pkg.restrictUpdateHash = null;
                if (hash != null) {
                    final int hashLength = hash.length();
                    final byte[] hashBytes = new byte[hashLength / 2];
                    for (int i = 0; i < hashLength; i += 2){
                        hashBytes[i/2] = (byte) ((Character.digit(hash.charAt(i), 16) << 4)
                                + Character.digit(hash.charAt(i + 1), 16));
                    }
                    pkg.restrictUpdateHash = hashBytes;
                }
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (RIGID_PARSER) {
            outError[0] = "Bad element under <manifest>: "
                + parser.getName();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return null;

        } else {
            Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                    + " at " + mArchiveSourcePath + " "
                    + parser.getPositionDescription());
            XmlUtils.skipCurrentTag(parser);
            continue;
        }
    }

    if (!foundApp && pkg.instrumentation.size() == 0) {
        outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
    }

    final int NP = PackageParser.NEW_PERMISSIONS.length;
    StringBuilder newPermsMsg = null;
    for (int ip=0; ip<NP; ip++) {
        final PackageParser.NewPermissionInfo npi
                = PackageParser.NEW_PERMISSIONS[ip];
        if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
            break;
        }
        if (!pkg.requestedPermissions.contains(npi.name)) {
            if (newPermsMsg == null) {
                newPermsMsg = new StringBuilder(128);
                newPermsMsg.append(pkg.packageName);
                newPermsMsg.append(": compat added ");
            } else {
                newPermsMsg.append(' ');
            }
            newPermsMsg.append(npi.name);
            pkg.requestedPermissions.add(npi.name);
            pkg.implicitPermissions.add(npi.name);
        }
    }
    if (newPermsMsg != null) {
        Slog.i(TAG, newPermsMsg.toString());
    }


    final int NS = PermissionManager.SPLIT_PERMISSIONS.size();
    for (int is=0; is<NS; is++) {
        final PermissionManager.SplitPermissionInfo spi =
                PermissionManager.SPLIT_PERMISSIONS.get(is);
        if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk()
                || !pkg.requestedPermissions.contains(spi.getSplitPermission())) {
            continue;
        }
        final List<String> newPerms = spi.getNewPermissions();
        for (int in = 0; in < newPerms.size(); in++) {
            final String perm = newPerms.get(in);
            if (!pkg.requestedPermissions.contains(perm)) {
                pkg.requestedPermissions.add(perm);
                pkg.implicitPermissions.add(perm);
            }
        }
    }

    if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
    }
    if (supportsNormalScreens != 0) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
    }
    if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
    }
    if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
    }
    if (resizeable < 0 || (resizeable > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
    }
    if (anyDensity < 0 || (anyDensity > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
    }

    // At this point we can check if an application is not supporting densities and hence
    // cannot be windowed / resized. Note that an SDK version of 0 is common for
    // pre-Doughnut applications.
    if (pkg.applicationInfo.usesCompatibilityMode()) {
        adjustPackageToBeUnresizeableAndUnpipable(pkg);
    }

    return pkg;
}

三.总结

  • PMS, 由SystemServer启动,PMS运行在单独的系统进程中,启动的时候通过ServiceManager.addService存入PMS对象,应用层通过ServiceManager.getService获取对象由于PMS是单独进程,获取的对象是一个IBinder接口;

  • PMS的作用时期主要是集中在手机开机时期,会扫描主要/data/app 和/systema/app两个目录扫描比较耗费时间,Android10.0 、9.0 通过线程,缓存机制进行优化,9.0以下没有扫描所有的apk文件,只是读取每一个AndroidManifest.xml文件信息,通过dom解析,判断tagname,解析成对应四大组件然后统一缓存在一个Package对象中;

四.Pms实现apk解析

  • 开机的时候,PMS会去扫描所有的apk文件,读取AndroidManifest的内容(四大组件),将读取的内容按照JavaBean的格式封装,然后将其缓存起来,方便后面查找和定位;
  • 在知晓其原理的情况下,就能够实现
    • 只能通过DexClassLoader去反射,而不能通过Class.forName(只能加载主工程的类)去反射
  • 注意事项
    • 反射系统api的时候,(兼容问题):其参数可能发生了变化;
    • 插件apk无法开启调试、;插件apk通过私有目录的方式实现不被恶意替换;
  • 细节:getField与getDxxField的区别
  • 插件化、热修复、换肤可以整合成一个专题进行学习;
0

评论区