Android值Intent匹配规则挖掘(PMS获取系统apk信息过程)

Intent的查找与匹配

App信息表的构建

  • 在Android开发中,Intent是极其重要的一个类,他是个个组件,进程之间通信的纽带,那么系统是如何通过Intent来查找对应的组件的呢?
  • 在Android中,系统启动之后就会注册各种系统服务,关于注册的这些内容我的前几篇博客都有说到,这里就不再絮叨,注册的服务很多,如:WindowManagerService,ActivityMAnagerService,等,其中有一个就是PackagerManagerService(后面简称PMS),PMS启动之后,会扫描系统中已经安装的APK目录,例如系统App的安装目录为/system/app,第三方应用安装目录为/data/app,PMS会解析apk包下的AndroidManifest.xml文件得到App相关信息,而整个AndroidManifest.xml又包含了Activity,Service等各大组件的注册信息,当PMS解析完这些信息之后就构建好了整个apk的信息树,大概流程如下
    《Android值Intent匹配规则挖掘(PMS获取系统apk信息过程)》
  • 下面我们来看看PMS解析已安装apk信息的过程
  • 直接 去PMS的构造方法
  • 额,点进去看了看,实在太长,这里就不贴了,看到哪里我就贴哪里的代码,具体的可以去PMS的java文件之下的四个参数的构造方法里面从上到下找
  • 先看这段代码
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
  • 在这段代码的后面我们看到他会得到/data目录,并访问其中的一系列文件,接着往下找
final int packageSettingCount = mSettings.mPackages.size();
for (int i = packageSettingCount - 1; i >= 0; i--) {
    PackageSetting ps = mSettings.mPackages.valueAt(i);
    if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
            && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
        mSettings.mPackages.removeAt(i);
        mSettings.enableSystemPackageLPw(ps.name);
    }
}
  • 这段代码好像是对系统包的某一部分进行清理的,继续
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
if (TextUtils.isEmpty(customResolverActivity)) {
    customResolverActivity = null;
} else {
    mCustomResolverComponentName = ComponentName.unflattenFromString(
            customResolverActivity);
}
  • 这段代码看起来好像是对当前情况下外部(非系统程序)未销毁的Activity,的组件名做个设置
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
  • 得到FrameWork目录,继续
if (mPromoteSystemApps) {
 Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
    while (pkgSettingIter.hasNext()) {
        PackageSetting ps = pkgSettingIter.next();
        if (isSystemApp(ps)) {
            mExistingSystemPackages.add(ps.name);
        }
    }
}
  • 这段代码是对已经存在的系统程序的包名做收集,存在mExistingSystemPackages他是一个ArraySet
  • 接着是很多个scanDirTracedLI方法,这个方法明显是扫描目录下的文件,根据参数的约束条件,在这里我直接全部贴上
//为了保证版本和安全问题,仅仅考虑一部分包
// Collect vendor overlay packages. (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in the right directory.
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR
        | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

mParallelPackageParserCallback.findStaticOverlayPackages();
//扫描FrameWork层
// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR
        | PackageParser.PARSE_IS_PRIVILEGED,
        scanFlags | SCAN_NO_DEX, 0);

//扫描收集系统特权包
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR
        | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

//收集普通系统包
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
//收集所有的供应商的包
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
    vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
    // failed to look up canonical path, continue with original one
}
scanDirTracedLI(vendorAppDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

//收集Android原版包
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
  • 接下来有一代码是检测一些可能已经卸载的App,但是还没有被mSettings.mPackages(也就是系统的集合)删掉的,继续
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
for (int i = 0; i < deletePkgsList.size(); i++) {
    // Actual deletion of code and data will be handled by later
    // reconciliation step
    final String packageName = deletePkgsList.get(i).name;
    logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + packageName);
    synchronized (mPackages) {
        mSettings.removePackageLPw(packageName);
    }
}
  • 这是将不完整的包之类的移除掉,继续
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
                        | PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);

-前句是遍历扫描已经安装的目录,还记得我们在之前得到了许多安卓目录文件吗?mAppInstallDir这个变量就是在那个时候初始化的,它代表安卓系统已经安装的App的目录
– 后句是什么私有的App的目录,这个因为不太明白安卓系统对已经安装的APP的划分,所以这里也就一知半解
– 在扫描完这两种Apk目录之后,先删掉一些更新系统的禁用包,
– 然后会遍历,这个时候会为之后的解析设置一些标志位,代码如下

for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
    final File scanFile = mExpectingBetter.valueAt(i);

    logCriticalInfo(Log.WARN, "Expected better " + packageName
            + " but never showed up; reverting to system");

    int reparseFlags = mDefParseFlags;
    if (FileUtils.contains(privilegedAppDir, scanFile)) {
        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR
                | PackageParser.PARSE_IS_PRIVILEGED;
    } else if (FileUtils.contains(systemAppDir, scanFile)) {
        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR;
    } else if (FileUtils.contains(vendorAppDir, scanFile)) {
        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR;
    } else if (FileUtils.contains(oemAppDir, scanFile)) {
        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR;
    } else {
        Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
        continue;
    }

    mSettings.enableSystemPackageLPw(packageName);

    try {
        scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
    } catch (PackageManagerException e) {
        Slog.e(TAG, "Failed to parse original system package: "
                + e.getMessage());
    }
}
}
  • 再往下,我们就已经得到所有我们需要的apk包了,继续往下
// Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.read(mPackages);
mCompilerStats.read();
  • 这句话根据他的意思大概能推断读取apk包中的有用的信息,不过点进去发现他好像只读取了版本相关
  • 再往下就是接着是为程序准备内存和缓存
  • 最后加载完成之后
  • 到这里PMS的构造方法就完了
  • 过了第一遍也很迷,不过总算有个大概认识,他是先进行一些各种包的扫描,然后过滤,最后读取,最后进行应用程序的初始化工作
  • 不过我们这里是为了分析他的解析已安装apk的过程,所以根据前面的分析,我们去看看scanDirTracedLI这个方法
private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
        try {
            scanDirLI(dir, parseFlags, scanFlags, currentTime);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
//获取该目录下所有的文件夹
        final File[] files = dir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + dir);
            return;
        }

//根据意思这是个包解析器
        ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback);

        // Submit files for parsing in parallel
        int fileCount = 0;
        //这个循环是遍历这个目录下的所有文件,如果是不是apk文件或者文件夹就continue,或者fileCount++,这样就得到了apk的数目
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            //如果是apk文件,就解析
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }

        // Process results one by one
        //这个好像是遍历一个个刚才解析出来的结果
        for (; fileCount > 0; fileCount--) {
            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
            Throwable throwable = parseResult.throwable;
            int errorCode = PackageManager.INSTALL_SUCCEEDED;

            if (throwable == null) {
                // Static shared libraries have synthetic package names
                if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                    renameStaticSharedLibraryPackage(parseResult.pkg);
                }
                try {
                    if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
                        scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
                                currentTime, null);
                    }
                } 
            } 
        }
        //释放解析器资源
        parallelPackageParser.close();
    }
  • 我们看看解析apk文件的这个方法(parallelPackageParser.submit(file, parseFlags);)的具体实现
public void submit(File scanFile, int parseFlags) {
    mService.submit(() -> { ParseResult pr = new ParseResult(); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]"); try { //先创建一个解析器 PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCacheDir(mCacheDir); pp.setCallback(mPackageParserCallback); pr.scanFile = scanFile; //解析apk包 pr.pkg = parsePackage(pp, scanFile, parseFlags); } try { mQueue.put(pr); } }); }
  • 这里是通过线程池来处理的,最后我们去看看解析的实现( 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类的parsePackage方法
public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
        return parsed;
    }

    if (packageFile.isDirectory()) {
    //是否是文件夹类型
        parsed = parseClusterPackage(packageFile, flags);
    } else {
    //解析单个apk文件
        parsed = parseMonolithicPackage(packageFile, flags);
    }

    cacheResult(packageFile, flags, parsed);

    return parsed;
}
  • 走到parseMonolithicPackage方法
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//构建应用资源
        final AssetManager assets = newConfiguredAssetManager();
        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
        if (mOnlyCoreApps) {
            if (!lite.coreApp) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                        "Not a coreApp: " + apkFile);
            }
        }

        try {
        //调用解析方法
            final Package pkg = parseBaseApk(apkFile, assets, flags);
            pkg.setCodePath(apkFile.getAbsolutePath());
            pkg.setUse32bitAbi(lite.use32bitAbi);
            return pkg;
        } finally {
            IoUtils.closeQuietly(assets);
        }
    }
  • 进入这个解析方法parseBaseApk(apkFile, assets, flags);
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();
    final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
    Resources res = null;
    XmlResourceParser parser = null;
    try {
        res = new Resources(assets, mMetrics, null);
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        final String[] outError = new String[1];
        //到这里继续解析
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
        pkg.setVolumeUuid(volumeUuid);
        pkg.setApplicationVolumeUuid(volumeUuid);
        pkg.setBaseCodePath(apkPath);
        pkg.setSignatures(null);
        return pkg;

    } 
}
  • 一些代码太多我直接删掉了,我们继续跟进去看
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
        final String splitName;
        final String pkgName;

        try {
        //这个方法点进去应该是解析MANIFEST结点的
            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;
            }
        } 

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

//构建Package对象
        final Package pkg = new Package(pkgName);
        //获取
        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifest);

        pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        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);

        sa.recycle();
//继续解析
        return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
    }
  • 额 ,parseBaseApkCommon,这个方法,写的真好,真长,我瞬间放弃了直接贴整个方法的想法,我们就从方法的开始往下看,一点一点解释着贴吧
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

}
  • 从开始到过了几十行的这句代码,我们发现了一点线索,这句代码是开始解析androidManifest.xml的开始
  • 接着的那个while循环是解析整个AndroidManifest.xml文件的整体,接下来的代码都是从这个while循环里面贴
String tagName = parser.getName();
  • 先得到最外层的标签值
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;
                }
  • 先看看是不是Application结点,这里涉及多个Application的判断,在这里就不细说了
  • 然后就是各种标签的判断
} 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)) {
        } else if (tagName.equals(TAG_USES_FEATURE)) {
  • 当然这个标签很多,这里就不贴完了,不过这些都是Manifest下的标签,我们平日最熟悉的四大组件的标签都是在Application标签下的
    《Android值Intent匹配规则挖掘(PMS获取系统apk信息过程)》
  • 所以我们从刚才的解析Application那里进他的parseBaseApplication方法
  • 这个方法一如既往写的如此的好(chang),大概看了一下, 前半部分是对Application的我们显示设置的属性进行读取
  • 后半部分跟上个方法的 结构差不多,都是解析一个个结点,我这里简单贴一下代码
final int innerDepth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
                owner.activities.add(a);
            } else if (tagName.equals("receiver")) {
            } else if (tagName.equals("service")) {
            } else if (tagName.equals("provider")) {
            } else if (tagName.equals("activity-alias")) {
            } else if (parser.getName().equals("meta-data")) {
            } else if (tagName.equals("static-library")) {
            } else if (tagName.equals("library")) {
            } else if (tagName.equals("uses-static-library")) {
            } else if (tagName.equals("uses-library")) {
            } else if (tagName.equals("uses-package")) {
            } 
        }
  • 这里我将大多结点解析过程直接删掉了,只留了Activity的解析过程,因为他们都是一样的
  • 我们看到解析完activity之后将他加入到了一个owner.activities.add(a);集合当中,这里可以推断,这个owner是用来存储这个AndroidManifest文件里面的详细内容的
  • 这里单独的解析activity的方法同样死长死长的,不过都不难理解,刚开始是解析activity 当中自身的属性,然后会解析activity内部的结点,内部的结点我们一般见到的就是“intent-filter”,而“intent-filter”内部又有action和category,代码如下
if (parser.getName().equals("intent-filter")) {
                ActivityIntentInfo intent = new ActivityIntentInfo(a);
                //在这里解析具体的action和category等
                if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
                        intent, outError)) {
                    return null;
                }
                //解析完之后会将信息存入intent里面
                if (intent.countActions() == 0) {
                    Slog.w(TAG, "No actions in intent filter at "
                            + mArchiveSourcePath + " "
                            + parser.getPositionDescription());
                } else {
                //如果这个intent里面有信息(也就是有action和category这些东西),就将他加入到a里面
                //这里的a就是我们解析的activity对象
                    a.intents.add(intent);
                }
                // adjust activity flags when we implicitly expose it via a browsable filter
                final int visibility = visibleToEphemeral
                        ? IntentFilter.VISIBILITY_EXPLICIT
                        : !receiver && isImplicitlyExposedIntent(intent)
                                ? IntentFilter.VISIBILITY_IMPLICIT
                                : IntentFilter.VISIBILITY_NONE;
                intent.setVisibilityToInstantApp(visibility);
                if (intent.isVisibleToInstantApp()) {
                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
                }
                if (intent.isImplicitlyVisibleToInstantApp()) {
                    a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
                }
                if (LOG_UNSAFE_BROADCASTS && receiver
                        && (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O)) {
                    for (int i = 0; i < intent.countActions(); i++) {
                        final String action = intent.getAction(i);
                        if (action == null || !action.startsWith("android.")) continue;
                        if (!SAFE_BROADCASTS.contains(action)) {
                            Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
                                    + owner.packageName + " as requested at: "
                                    + parser.getPositionDescription());
                        }
                    }
                }
            }
  • 在代码的前面我写了两句注释, 当然不只是解析action和category这两个东西,只不过这两个东西我们最熟悉,所以就来分析这两个东西,其他的都是一个原理
  • 我们接着去parseIntent方法里面看看
  • 这个部分同样很长,我只贴我们目前关注的action和category这两个东西的解析代码
int outerDepth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
        ....
            String nodeName = parser.getName();
            if (nodeName.equals("action")) {
                String value = parser.getAttributeValue(
                        ANDROID_RESOURCES, "name");
                if (value == null || value == "") {
                    outError[0] = "No value supplied for <android:name>";
                    return false;
                }
                XmlUtils.skipCurrentTag(parser);
//这个outinfo就是调用这个方法时传进来的 intent参数,忘记的话可以去上面瞅瞅
                outInfo.addAction(value);
            } else if (nodeName.equals("category")) {
                String value = parser.getAttributeValue(
                        ANDROID_RESOURCES, "name");
                if (value == null || value == "") {
                    outError[0] = "No value supplied for <android:name>";
                    return false;
                }
                XmlUtils.skipCurrentTag(parser);

                outInfo.addCategory(value);

            } else if (nodeName.equals("data")) {
 ...//其他部分删掉
            } 
        }
  • 这里看到我们将解析到的action和category存进刚才的intent里面,那么到这里整个AndroidManifest文件就解析完了,我们再来捋一遍
  • 在packageParse类下的parseBaseApplication方法中,将解析到的activity对象加入到owner.activities.中
owner.activities.add(a);
  • 这里的owner是最开始根据 app名建立的Package对象
final Package pkg = new Package(pkgName);
  • 然后将activity内部的intent-filter结点的内容存到activity的intents下
a.intents.add(intent);
  • 同时,这个intent里面存的是intent-filter内部的action和category
  • 除了我们看到的这些之外,其他的属性肯定也是存在相同层次的集合中的,通过前一句我们知道了Package这个类存的是解析出来的一个apk文件的所有信息
  • 然后再往前走,在packageParser类的parsePackage方法里面发现他将最终解析的apk的信息对象通过这个方法来缓存
cacheResult(packageFile, flags, parsed);
  • 这里的parse参数就是解析出来的apk信息文件,跟进去这个方法看看
private void cacheResult(File packageFile, int flags, Package parsed) {


        final String cacheKey = getCacheKey(packageFile, flags);
        //根据apk的名字创建一个文件
        final File cacheFile = new File(mCacheDir, cacheKey);


        final byte[] cacheEntry;
        try {
        //这里是将parsed对象序列化成一个byte数组
            cacheEntry = toCacheEntry(parsed);
        } catch (IOException ioe) {
            Slog.e(TAG, "Unable to serialize parsed package for: " + packageFile);
            return;
        }

        if (cacheEntry == null) {
            return;
        }

        try (FileOutputStream fos = new FileOutputStream(cacheFile)) {
        //将信息写进文件
            fos.write(cacheEntry);
        } catch (IOException ioe) {
            Slog.w(TAG, "Error writing cache entry.", ioe);
            cacheFile.delete();
        }
    }
  • 可以看到最后将解析到的信息写进了文件,那么到这里这个解析过程就完了
  • 好烦,是不是到这里你都忘了我们原来是打算干嘛的了 ,我们不是分析Intent的匹配规则的吗,他妈的分析这个PMS鬼东西干嘛,还把人累的够呛,我也不知道,书里面就这么写的,那我们接着看Intent的匹配规则吧
  • 当我们要启动某个组件的时候,通常会这么写,比方说启动activity
Intent i = new Intent(this,SecondActivity.class);
startActivity(i);
  • 这种情况下我们指定了具体的 组件,称为显式启动,还有一种隐式启动
Intent i = new Intent();
i.setAction(Intent.ACTION_SENDTO);
i.setData(Uri.parse("这里可以写data数据"));
i.addCategory("这里是一个category");
startActivity(i);
  • 当然这里也不一定非要把我举的这些信息都填,也可以只填一两个,无关紧要
  • 在调用StartActivity之后,经过几个函数跳转,会来到startActivityForResult这个方法
 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            //启动activity
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            //发送启动请求
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                mStartedActivity = true;
            }
            cancelInputsAndStartExitTransition(options);
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }
  • 在Instrumentation的execStartActivity方法里面负责启动activity的准备
public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        .................
        try {
        //将Intent中的数据迁移到粘贴板中 
            intent.migrateExtraStreamToClipData();
            //准备离开当前进程
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
           //检测结果,并回调给调用端
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
  • 这里的execStartActivity在最后调用了AMS的startActivity方法,而这个方法里面又调用了….,反正最后会调用PMS的
    queryIntentActivitiesInternal方法
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
            String resolvedType, int flags, int filterCallingUid, int userId,
            boolean resolveForStart) {
        if (!sUserManager.exists(userId)) return Collections.emptyList();
        final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
        enforceCrossUserPermission(Binder.getCallingUid(), userId,
                false /* requireFullPermission */, false /* checkShell */,
                "query intent activities");
        final String pkgName = intent.getPackage();
        //获取Intent的Component对象
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector();
                comp = intent.getComponent();
            }
        }

        flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
                comp != null || pkgName != null /*onlyExposedExplicitly*/);
                //不为空时表示显式跳转
        if (comp != null) {
            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                final boolean matchInstantApp =
                        (flags & PackageManager.MATCH_INSTANT) != 0;
                final boolean matchVisibleToInstantAppOnly =
                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
                final boolean matchExplicitlyVisibleOnly =
                        (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
                final boolean isCallerInstantApp =
                        instantAppPkgName != null;
                final boolean isTargetSameInstantApp =
                        comp.getPackageName().equals(instantAppPkgName);
                final boolean isTargetInstantApp =
                        (ai.applicationInfo.privateFlags
                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
                final boolean isTargetVisibleToInstantApp =
                        (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
                final boolean isTargetExplicitlyVisibleToInstantApp =
                        isTargetVisibleToInstantApp
                        && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
                final boolean isTargetHiddenFromInstantApp =
                        !isTargetVisibleToInstantApp
                        || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
                final boolean blockResolution =
                        !isTargetSameInstantApp
                        && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                        && isTargetHiddenFromInstantApp));
                if (!blockResolution) {
                    final ResolveInfo ri = new ResolveInfo();
                    ri.activityInfo = ai;
                    list.add(ri);
                }
            }
            return applyPostResolutionFilter(list, instantAppPkgName);
        }

        // reader
        boolean sortResult = false;
        boolean addEphemeral = false;
        List<ResolveInfo> result;
        final boolean ephemeralDisabled = isEphemeralDisabled();
        synchronized (mPackages) {
            if (pkgName == null) {
                List<CrossProfileIntentFilter> matchingFilters =
                        getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
                // Check for results that need to skip the current profile.
                ResolveInfo xpResolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,
                        resolvedType, flags, userId);
                if (xpResolveInfo != null) {
                    List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
                    xpResult.add(xpResolveInfo);
                    return applyPostResolutionFilter(
                            filterIfNotSystemUser(xpResult, userId), instantAppPkgName);
                }

                // Check for results in the current profile.
                result = filterIfNotSystemUser(mActivities.queryIntent(
                        intent, resolvedType, flags, userId), userId);
                addEphemeral = !ephemeralDisabled
                        && isInstantAppAllowed(intent, result, userId, false /*skipPackageCheck*/);
                // Check for cross profile results.
                boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
                xpResolveInfo = queryCrossProfileIntents(
                        matchingFilters, intent, resolvedType, flags, userId,
                        hasNonNegativePriorityResult);
                if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
                    boolean isVisibleToUser = filterIfNotSystemUser(
                            Collections.singletonList(xpResolveInfo), userId).size() > 0;
                    if (isVisibleToUser) {
                        result.add(xpResolveInfo);
                        sortResult = true;
                    }
                }
                if (hasWebURI(intent)) {
                    CrossProfileDomainInfo xpDomainInfo = null;
                    final UserInfo parent = getProfileParent(userId);
                    if (parent != null) {
                        xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
                                flags, userId, parent.id);
                    }
                    if (xpDomainInfo != null) {
                        if (xpResolveInfo != null) {
                            // If we didn't remove it, the cross-profile ResolveInfo would be twice
                            // in the result.
                            result.remove(xpResolveInfo);
                        }
                        if (result.size() == 0 && !addEphemeral) {
                            // No result in current profile, but found candidate in parent user.
                            // And we are not going to add emphemeral app, so we can return the
                            // result straight away.
                            result.add(xpDomainInfo.resolveInfo);
                            return applyPostResolutionFilter(result, instantAppPkgName);
                        }
                    } else if (result.size() <= 1 && !addEphemeral) {
                        // No result in parent user and <= 1 result in current profile, and we
                        // are not going to add emphemeral app, so we can return the result without
                        // further processing.
                        return applyPostResolutionFilter(result, instantAppPkgName);
                    }
                    // We have more than one candidate (combining results from current and parent
                    // profile), so we need filtering and sorting.
                    result = filterCandidatesWithDomainPreferredActivitiesLPr(
                            intent, flags, result, xpDomainInfo, userId);
                    sortResult = true;
                }
            } else {
            //隐式跳转,先通过名字得到Package
                final PackageParser.Package pkg = mPackages.get(pkgName);
                result = null;
                if (pkg != null) {
                    result = filterIfNotSystemUser(
                            mActivities.queryIntentForPackage(
                                    intent, resolvedType, flags, pkg.activities, userId),
                            userId);
                }
                if (result == null || result.size() == 0) {
                    // the caller wants to resolve for a particular package; however, there
                    // were no installed results, so, try to find an ephemeral result
                    addEphemeral = !ephemeralDisabled
                            && isInstantAppAllowed(
                                    intent, null /*result*/, userId, true /*skipPackageCheck*/);
                    if (result == null) {
                        result = new ArrayList<>();
                    }
                }
            }
        }
        if (addEphemeral) {
            result = maybeAddInstantAppInstaller(result, intent, resolvedType, flags, userId);
        }
        if (sortResult) {
            Collections.sort(result, mResolvePrioritySorter);
        }
        //最后会返回匹配到的组件信息
        return applyPostResolutionFilter(result, instantAppPkgName);
    }
  • 那么到这里就匹配完成了,接下来的事儿就是具体的启动了
    原文作者:不融化的雪人
    原文地址: https://blog.csdn.net/asffghfgfghfg1556/article/details/81675577
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞