1. 程式人生 > >Android值Intent匹配規則挖掘(PMS獲取系統apk資訊過程)

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的資訊樹,大概流程如下
    這裡寫圖片描述
  • 下面我們來看看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標籤下的
    這裡寫圖片描述
  • 所以我們從剛才的解析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