Android值Intent匹配規則挖掘(PMS獲取系統apk資訊過程)
阿新 • • 發佈:2019-01-10
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