- 原文發(fā)布與個(gè)人博客KuTear,轉(zhuǎn)載請(qǐng)注明*
pm命令的使用
pm 基本使用
pm工具在Android系統(tǒng)System/bin下,由于該路徑已經(jīng)在環(huán)境變量PATH下,所以我們可以在Android終端下運(yùn)行pm命令
pm <command>
通過鍵入pm我們可以看見他的用法,主要包括以下幾個(gè)方面
- list => 列出安裝的APK/權(quán)限組/機(jī)器硬件功能(NFC/藍(lán)牙/WIFI)/設(shè)備支持庫(kù)
- path => 查詢指定apk的路徑
- install => APK安裝
- dump => 獲取系統(tǒng)中的信息(電量/activity/server)
- clear => 清除APK數(shù)據(jù)
- hide/unhide => 隱藏與恢復(fù)應(yīng)用(被隱藏應(yīng)用在應(yīng)用管理中變得不可見,桌面圖標(biāo)也會(huì)消失)
- enable/disable => 禁用和啟用應(yīng)用
- grant/revoke => 賦予/撤銷應(yīng)用權(quán)限
pm 命令源碼分析
Pm命令的源碼在framework/base/cmds/pm下(本文基于5.1.1 r1 代碼地址),只有一個(gè)類Pm.java,在其靜態(tài)入口函數(shù)main(args)中執(zhí)行函數(shù)run(args),這里我們可以看看其中一部分Pm list package 。
public int run(String[] args) throws IOException, RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
}
mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
mInstaller = mPm.getPackageInstaller();
mArgs = args;
String op = args[0];
mNextArg = 1;
if ("list".equals(op)) {
return runList();
}
//....其他命令
}
上面的代碼通過進(jìn)程間通信獲取的PackageManagaerServer,然后運(yùn)行具體的指令,下面看看list指令是怎么獲取到數(shù)據(jù)的。
/**
* Execute the list sub-command.
*
* pm list [package | packages]
* pm list permission-groups
* pm list permissions
* pm list features
* pm list libraries
* pm list instrumentation
*/
private int runList() {
String type = nextArg();
if (type == null) {
System.err.println("Error: didn't specify type of data to list");
return 1;
}
if ("package".equals(type) || "packages".equals(type)) {
return runListPackages(false);
} else if ("permission-groups".equals(type)) {
return runListPermissionGroups();
} else if ("permissions".equals(type)) {
return runListPermissions();
} else if ("features".equals(type)) {
return runListFeatures();
} else if ("libraries".equals(type)) {
return runListLibraries();
} else if ("instrumentation".equals(type)) {
return runListInstrumentation();
} else if ("users".equals(type)) {
return runListUsers();
} else {
System.err.println("Error: unknown list type '" + type + "'");
return 1;
}
}
這里沒有實(shí)質(zhì)性的操作,只是分發(fā)list的子指令,下面我們看package指令的實(shí)現(xiàn)邏輯。
/*
* Lists all the installed packages.
*/
private int runListPackages(boolean showApplicationPackage) {
int getFlags = 0;
boolean listDisabled = false, listEnabled = false;
boolean listSystem = false, listThirdParty = false;
boolean listInstaller = false;
//A user id constant to indicate the "owner" user of the device
int userId = UserHandle.USER_OWNER;
try {
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-l")) {
// old compat
} else if (opt.equals("-lf")) {
showApplicationPackage = true;
} else if (opt.equals("-f")) {
showApplicationPackage = true;
} else if (opt.equals("-d")) {
listDisabled = true;
} else if (opt.equals("-e")) {
listEnabled = true;
} else if (opt.equals("-s")) {
listSystem = true;
} else if (opt.equals("-3")) {
listThirdParty = true;
} else if (opt.equals("-i")) {
listInstaller = true;
} else if (opt.equals("--user")) {
userId = Integer.parseInt(nextArg());
} else if (opt.equals("-u")) {
getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
} else {
System.err.println("Error: Unknown option: " + opt);
return 1;
}
}
} catch (RuntimeException ex) {
System.err.println("Error: " + ex.toString());
return 1;
}
String filter = nextArg();
try {
//重點(diǎn)關(guān)注
final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags, userId);
int count = packages.size();
for (int p = 0 ; p < count ; p++) {
PackageInfo info = packages.get(p);
if (filter != null && !info.packageName.contains(filter)) {
continue;
}
final boolean isSystem =
(info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
if ((!listDisabled || !info.applicationInfo.enabled) &&
(!listEnabled || info.applicationInfo.enabled) &&
(!listSystem || isSystem) &&
(!listThirdParty || !isSystem)) {
System.out.print("package:");
if (showApplicationPackage) {
System.out.print(info.applicationInfo.sourceDir);
System.out.print("=");
}
System.out.print(info.packageName);
if (listInstaller) {
System.out.print(" installer=");
System.out.print(mPm.getInstallerPackageName(info.packageName));
}
System.out.println();
}
}
return 0;
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
}
上面的代碼一方面是添加過濾參數(shù),然后獲取所有安裝的APK,在根據(jù)過濾參數(shù)展示對(duì)應(yīng)的APK,而我們最為關(guān)心的就是獲取所有APK的過程.到底是怎么獲取的呢, 我們接著往下看。
@SuppressWarnings("unchecked")
private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags, int userId)
throws RemoteException {
ParceledListSlice<PackageInfo> slice = pm.getInstalledPackages(flags, userId);
return slice.getList();
}
到此,我們發(fā)現(xiàn)其實(shí)獲取APK列表的過程是由IPackageManager來實(shí)現(xiàn)的,同樣的方式分析其他命令的操作,其最后都是歸集到IPackageManager上,而IPackageManager就是我們今天的主角,下面我們會(huì)重點(diǎn)分析PM啟動(dòng)的過程
PackageManagerServer的啟動(dòng)位置
在SystemServer啟動(dòng)后,會(huì)實(shí)例化PackageManagerServer, 具體位置在PackageManagerServer#startBootstrapServices() 。
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
在這個(gè)main()函數(shù)中實(shí)現(xiàn)很簡(jiǎn)單,只是實(shí)例化一個(gè)PMS對(duì)象,然后將其加入SystemServer中,而最為重要的過程就是PackageManagerServer的實(shí)例化的過程,下面將會(huì)重點(diǎn)分析這個(gè)過程。
PackageManagerServer 的實(shí)例過程
PM相關(guān)的目錄
-
系統(tǒng)APK
-
/system/priv-app
-
/system/app
-
/vendor/app
-
/oem/app
-
-
用戶APK
-
/data/app
-
PMS配置文件
-
/data/system/packages.xml
記錄系統(tǒng)中所有已經(jīng)安裝的應(yīng)用信息,包括基本信息,簽名和權(quán)限。
-
/system/etc/permissions/
讀取當(dāng)前Android設(shè)備的硬件特性和設(shè)定的相關(guān)權(quán)限。
PM初始化過程
//PackageManagerServer.java構(gòu)造函數(shù)
//build類型,可以在shell下通過getprop來查看全部
//使用getprop ro.build.type獲取到該build type
//build type有user,userdebug,eng
//user:性能好,Log/Debug信息少,相當(dāng)于是正式版
//eng:Log/Debug信息相對(duì)多
//userdebug:debug強(qiáng)
//根據(jù)這里的返回,我們猜測(cè)返回值和dexopt相關(guān)
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
上面主要就是新建一個(gè)Setting對(duì)象,然后調(diào)用函數(shù)addSharedUserLPw(...),在Setting的構(gòu)造函數(shù)中主要就是為上文說的/data/system/packages.xml等文件的創(chuàng)建和賦權(quán)限.現(xiàn)在看看函數(shù)addSharedUserLPw()的實(shí)現(xiàn)。
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
//ArrayMap<String, SharedUserSetting> mSharedUsers
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
if (addUserIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
該函數(shù)主要就是共享UID,例如有的系統(tǒng)應(yīng)用會(huì)有
android:sharedUserId="android.uis.system"
而根據(jù)上面的設(shè)置就是該APK的UID為Process.SYSTEM_UID,從而達(dá)到共享系統(tǒng)UID的目的.而下面調(diào)用的函數(shù)addUserIdLPw(...)就是保存該UID和對(duì)應(yīng)的ShareUserSetting 。接著看PackageMangerService的構(gòu)造函數(shù)的下一部分。
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
SystemConfig在構(gòu)造函數(shù)里對(duì)一些目錄進(jìn)行讀取,這些目錄包括
/system/etc/sysconfig
/system/etc/permissions
/oem/etc/sysconfig
/oem/etc/permissions
然后解析這些目錄下面的文件,這些文件的作用是聲明當(dāng)前設(shè)備的功能(NFC/WIFI)等.下面我們看看解析函數(shù)
private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}
final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
int type;
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
//要求根結(jié)點(diǎn)為permissions OR config
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException("Unexpected start tag in " + permFile
+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
//遍歷XML,并將不同的功能保存到不同的ArraySet<>中
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
if ("group".equals(name) && !onlyFeatures) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = android.os.Process.getGidForName(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<group> without gid in " + permFile + " at "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
readPermission(parser, perm);
} else if ("assign-permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
} else if ("library".equals(name) && !onlyFeatures) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<library> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
mSharedLibraries.put(lname, lfile);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("feature".equals(name)) {
String fname = parser.getAttributeValue(null, "name");
boolean allowed;
if (!lowRam) {
allowed = true;
} else {
String notLowRam = parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
if (fname == null) {
Slog.w(TAG, "<feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
//Log.i(TAG, "Got feature " + fname);
FeatureInfo fi = new FeatureInfo();
fi.name = fname;
mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("unavailable-feature".equals(name)) {
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mUnavailableFeatures.add(fname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSaveExceptIdle.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mFixedImeApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("app-link".equals(name)) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<app-link> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mLinkedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
}
}
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} catch (IOException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} finally {
IoUtils.closeQuietly(permReader);
}
for (String fname : mUnavailableFeatures) {
if (mAvailableFeatures.remove(fname) != null) {
Slog.d(TAG, "Removed unavailable feature " + fname);
}
}
}
上面代碼很容易理解,解析xml節(jié)點(diǎn)的數(shù)據(jù)到具體的List或Set中,在PackageManagerService的構(gòu)造函數(shù)中就取出了mGlobalGids/mAvailableFeatures/mSystemPermissions出來,分別對(duì)應(yīng)的TAG節(jié)點(diǎn)為
<group gid="" ></group>
<feature></feature>
<assign-permission></assign-permission>
下面接著分析PackageManagerService的構(gòu)造函數(shù)
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); //HandlerThread
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
File dataDir = Environment.getDataDirectory(); //dataDir = "/data/"
mAppDataDir = new File(dataDir, "data"); // = "/data/data"
mAppInstallDir = new File(dataDir, "app"); // = "/data/app"
mAppLib32InstallDir = new File(dataDir, "app-lib"); // = "/data/app-lib"
mAsecInternalPath = new File(dataDir, "app-asec").getPath(); // = "data/app-asec"
mUserAppDataDir = new File(dataDir, "user"); //= "/data/user"
mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); // = "/data/app-private"
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
// Propagate permission configuration in to package manager.
//合并SystemConfig讀取的permission到Settings下
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions(); //permission標(biāo)簽下的
for (int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
//從SystemConfig中讀取的libs存入共享庫(kù)
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
for (int i=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
上面的代碼主要就是通過上面的SystemConfig獲取到的信息存在在Settings和PMS內(nèi)部。
//這里會(huì)讀取前面說的/data/system/packages.xml文件以及他的備份文件
//這里特別說明下,會(huì)把解析的application存放到mSetting的mPackages中,后面會(huì)用到
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
mSdkVersion, mOnlyCore);
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
if (TextUtils.isEmpty(customResolverActivity)) {
customResolverActivity = null;
} else {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivity);
}
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
// Set flag to monitor and not change apk file paths when
// scanning install directories.
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
//已經(jīng)dexopt的apk存放位置
final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
/**
* Add everything in the in the boot class path to the
* list of process files because dexopt will have been run
* if necessary during zygote startup.
*/
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
//系統(tǒng)庫(kù)類不要優(yōu)化 可以通過echo $BOOTCLASSPATH查看
if (bootClassPath != null) {
String[] bootClassPathElements = splitString(bootClassPath, ':');
for (String element : bootClassPathElements) {
alreadyDexOpted.add(element);
}
} else {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
if (systemServerClassPath != null) {
String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
for (String element : systemServerClassPathElements) {
alreadyDexOpted.add(element);
}
} else {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
上面代碼開始時(shí)解析packages.xml,該文件保存了系統(tǒng)內(nèi)安裝了的APK的信息,然后就是添加一些庫(kù)類到alreadyDexOpted這個(gè)List里面,目的是以后做dexopt的時(shí)候跳過這些不必要的優(yōu)化。這里說明一下packages.xml中的字段的保存位置(以下是在Settings.java中)。
package -> mPackages(readPackageLPw()-->addPackageLPw()) //重點(diǎn)
permissions -> mPermissions(readPermissionsLPw())
permission-trees -> mPermissionTrees(readPermissionsLPw())
shared-user -> mSharedUsers(readSharedUserLPw()-->addSharedUserLPw())
updated-package -> mDisabledSysPackages(readDisabledSysPackageLPw()) //這個(gè)標(biāo)簽是在OTA中添加的?刪除也會(huì)有這個(gè)標(biāo)記?
renamed-package -> mRenamedPackages
這里強(qiáng)調(diào)一下,會(huì)把package的信息存在Settings.mPackages中,并根據(jù)package標(biāo)簽下的installStatus字段判斷app安裝的狀態(tài),這在后面有用到。下面繼續(xù)看PMS的構(gòu)造函數(shù)。
//通過命令getprop ro.product.cpu.abilist查看設(shè)備支持的指令集
final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
final String[] dexCodeInstructionSets =
getDexCodeInstructionSets(
allInstructionSets.toArray(new String[allInstructionSets.size()]));
/**
* Ensure all external libraries have had dexopt run on them.
*/
if (mSharedLibraries.size() > 0) {
// NOTE: For now, we're compiling these system "shared libraries"
// (and framework jars) into all available architectures. It's possible
// to compile them only when we come across an app that uses them (there's
// already logic for that in scanPackageLI) but that adds some complexity.
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib = libEntry.path;
if (lib == null) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
}
這段代碼主要就是執(zhí)行dexopt的過程,并將優(yōu)化過的apk/jar放入alreadyDexOpted,這里mInstaller內(nèi)部通過LocalStock發(fā)送一條指令dexopt apkpath ....,讓LocalStock的服務(wù)端處理這個(gè)請(qǐng)求,我沒有找到服務(wù)端的實(shí)現(xiàn)位置....但不影響我們繼續(xù)閱讀。
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// Gross hack for now: we know this file doesn't contain any
// code, so don't dexopt it to avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
// Gross hack for now: we know this file is only part of
// the boot class path for art, so don't dexopt it to
// avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
/**
* There are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
* run from a non-root shell.
*/
String[] frameworkFiles = frameworkDir.list();
if (frameworkFiles != null) {
// TODO: We could compile these only for the most preferred ABI. We should
// first double check that the dex files for these commands are not referenced
// by other system apps.
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(frameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
// Skip the file if we already did it.
if (alreadyDexOpted.contains(path)) {
continue;
}
// Skip the file if it is not a type we want to dexopt.
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
} catch (IOException e) {
Slog.w(TAG, "Exception reading jar: " + path, e);
}
}
}
}
這段代碼和上面的實(shí)現(xiàn)幾乎一樣,讀取/system/framework/下的幾個(gè)目錄的jar包和apk,判斷是否需要進(jìn)行優(yōu)化,值得注意的是并沒有加入列表alreadyDexOpted 。
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
// when upgrading from pre-M, promote system app permissions from install to runtime 運(yùn)行時(shí)權(quán)限
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}
當(dāng)系統(tǒng)有LOLLIPOP_MR1一下升級(jí)時(shí)到LOLLIPOP_MR1以上時(shí),android在6.0引入動(dòng)態(tài)權(quán)限機(jī)制,在這里也就是mPromoteSystemApps=true,會(huì)執(zhí)行下面一段代碼。
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);// = "/vendor/overlay"
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM //system/framework
| 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");
scanDirLI(privilegedAppDir, 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");
scanDirLI(systemAppDir, 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
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles(); //執(zhí)行LocalStock發(fā)送movefiles命令
上面的代碼的邏輯就是掃描指定的目錄,這里的目錄包括下面這些
/vendor/overlay
/system/framework
/system/priv-app
/system/app
/vendor/app
/oem/app
也就是我們上文提到的系統(tǒng)APK的存放目錄,掃描結(jié)束之后會(huì)把a(bǔ)pk信息存放在mPackages(這里是PMS,區(qū)別于Settings的mPackages)。下面我們來分析scanDirLI()的過程。
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;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
//遍歷該目錄下的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;
}
try {
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
// Delete invalid userdata apps
// 刪除無效的用戶APK
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
}
}
}
}
上面代碼核心就是遍歷指定的文件夾,對(duì)文件夾內(nèi)部的文件執(zhí)行函數(shù)scanPackageLI(File,...) ,通過其注釋我們了解到他是掃描包的。它的代碼也比較長(zhǎng),下面選擇其中一部分說明。
private PackageParser.Package scanPackageLI(File scanFile,
int parseFlags, int scanMode, long currentTime) {
......
String scanPath = scanFile.getPath();
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
......
final PackageParser.Package pkg = pp.parsePackage(scanFile,
scanPath, mMetrics, parseFlags);
......
return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime);
}
為指定的文件創(chuàng)建PackageParser,將解析結(jié)果存入Package ,最后在調(diào)用函數(shù)scanPackageLI(Package,...)。而在函數(shù)PackageParser.Package.parsePackage(...)中會(huì)判斷scanFile是文件還是目錄(Android分包),會(huì)對(duì)他們做不同的處理,我們這里簡(jiǎn)單點(diǎn),就看是文件的分支,當(dāng)時(shí)文件時(shí),會(huì)調(diào)用函數(shù)parseMonolithicPackage(packageFile, flags) ,下面分析這個(gè)函數(shù)。
public Package parseMonolithicPackage(File apkFile, int flags){
final AssetManager assets = new AssetManager();
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
}
這里的核心就是函數(shù)parseBaseApk(File,...),根據(jù)名稱感覺有些明朗了,不就解析APK嘛,看看到底是怎么實(shí)現(xiàn)的吧。
private Package parseBaseApk(File apkFile, AssetManager assets, int flags){
....
Resources res = null;
XmlResourceParser parser = null;
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, "AndroidManifest.xml");
final String[] outError = new String[1];
final Package pkg = parseBaseApk(res, parser, flags, outError);
...
return pkg;
}
臥槽,又是圈套,有調(diào)函數(shù)parseBaseApk(Resources,...)來解析,不過上面我們已經(jīng)看見關(guān)鍵的AndroidManifest.xml已經(jīng)出現(xiàn)了。通過閱讀parseBaseApk(Resources,...),我們發(fā)現(xiàn)他會(huì)解析AndroidManifest.xml中的一部分文件,這里大體包括以下標(biāo)簽
- application
- overlay
- key-sets
- permission-group
- permission-tree
- uses-permission
- uses-permission-sdk-m | uses-permission-sdk-23
- uses-configuration
- uses-feature
- feature-group
- uses-sdk
- supports-screens
- protected-broadcast
- instrumentation
- original-package
- adopt-permissions
- uses-gl-texture
- compatible-screens
- supports-input
- eat-comment
慚愧,好多標(biāo)簽沒見過,查看官網(wǎng)發(fā)現(xiàn)官網(wǎng)并沒有列舉以上全部AndroidManifest 。我們這里繼續(xù)跟進(jìn)application標(biāo)簽,發(fā)現(xiàn)他調(diào)用函數(shù)parseBaseApplication()。這個(gè)函數(shù)就是對(duì)Application內(nèi)部四大組件進(jìn)行解析。我們這里選取activity的部分來看看。
// 函數(shù)參數(shù) Package owner
if (tagName.equals("activity")) {
//class Activity extends Component<ActivityIntentInfo>
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
}
parseActivity()就是解析activity標(biāo)簽下的內(nèi)容,比如Activity的Theme什么的,解析的過程主要是利用TypedArray,具體的屬性可以看看Activity,很多屬性都是存儲(chǔ)在一個(gè)flags標(biāo)記了,這樣減少了類中成員字段過多,這在Android中使用的比較多,比如View中很多屬性也是存在一個(gè)flags中。到此一個(gè)解析好了的Package就好了,不知不覺,已經(jīng)偏了十萬八千里,不要急,拉回來,上面我們講到scanPackageLI(File,...)的最后調(diào)用了scanPackageLI(Package,...),那么這個(gè)函數(shù)有是做什么的呢?這個(gè)函數(shù)調(diào)用了scanPackageDirtyLI(),這個(gè)函數(shù)的代碼量也是相當(dāng)嚇人,這里不打算具體分析,主要工作就是為app創(chuàng)建目錄,也就是/data/data/apk.name/這個(gè)目錄,還有就是APK對(duì)應(yīng)的libs的存放位置,App簽名驗(yàn)證,收集APK要的權(quán)限,最重要的就是把解析信息存放到了PMS的mPackages變量中,意味著App安裝成功了,后面會(huì)用到這個(gè)變量?;氐?code>PackageManagerService的構(gòu)造函數(shù)中來。
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
//mSettings.mPackages來自與package.xml的package標(biāo)簽
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
/*
* If this is not a system app, it can't be a
* disable system app.
*/
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
/*
* If the package is scanned, it's not erased.
*/
//PMS的mPackages存放掃描過的APK
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
//掃描到了 && packages.xml中存在
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
* disabled packages list, then it must have been
* added via OTA. Remove it from the currently
* scanned package so the previously user-installed
* application can be scanned.
*/
//package.xml的package標(biāo)簽和updated-package標(biāo)簽都包含這個(gè)pkg
//根據(jù)上面注釋,意味著這個(gè)APK是通過OTA添加的,暫時(shí)移除
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "Expecting better updated system app for "
+ ps.name + "; removing system app. Last known codePath="
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
removePackageLI(ps, true);//mPackages.remove(ps.name);
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}
//沒有掃描到,在package標(biāo)簽下,但不在updated-package標(biāo)簽下,說明該APP已經(jīng)不存在了
//因此要?jiǎng)h掉他的目錄
//直接從mSettings.mPackages中移除
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; wiping its data");
removeDataDirsLI(null, ps.name);
} else {
//沒有掃描到,在package標(biāo)簽下,也在updated-package標(biāo)簽下
//可能由OTA引入(或刪除?)
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
處理被用戶隱藏的APP(前面講的pm hide package),因?yàn)楸浑[藏的APP在package.xml還存在,這里就是把這些APP從保存他們的列表中移除。另外就是在package.xml中還有該APK,但是掃描系統(tǒng)目錄發(fā)現(xiàn)這個(gè)APK已經(jīng)不存在了的處理方式。執(zhí)行完之后mSetting.mPackages剩下的就是無效的APK,我們需要將這些清除,于是就有了下面的幾行代碼
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();//獲取mSettings.mPackages中installStatus為未成功安裝的App(前文有講,package中installStatus=false的apk)
//clean up list
for(int i = 0; i < deletePkgsList.size(); i++) {
//clean up here
cleanupInstallFailedPackage(deletePkgsList.get(i));
}
//delete tmp files
deleteTempPackageFiles();
// Remove any shared userIDs that have no associated packages
mSettings.pruneSharedUsersLPw();//移除沒有被關(guān)聯(lián)的mSharedUsers
上面的作用就是清除無效APK引入的文件夾等。系統(tǒng)APK裝載完了,下面就開始裝載用戶APK,
scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
上面的代碼和前面掃描系統(tǒng)APK是一樣的,這是目錄和flags變了,邏輯是一樣的。這里的目錄包括
/data/app
/data/app-private
繼續(xù)看構(gòu)造函數(shù)。
/**
* Remove disable package settings for any updated system
* apps that were removed via an OTA. If they're not a
* previously-updated app, remove them completely.
* Otherwise, just revoke their system-level permissions.
*/
//在引進(jìn)了用戶app之后mPackages內(nèi)容增加了,再看看是否有這些app
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
if (deletedPkg == null) { //OTA刪除
msg = "Updated system package " + deletedAppName
+ " no longer exists; wiping its data";
removeDataDirsLI(null, deletedAppName);
} else { //在用戶app中找到了,當(dāng)然會(huì)移除系統(tǒng)包標(biāo)識(shí)
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
+ deletedAppName;
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
logCriticalInfo(Log.WARN, msg);
}
/**
* Make sure all system apps that we expected to appear on
* the userdata partition actually showed up. If they never
* appeared, crawl back and revive the system version.
*/
for (int i = 0; i < mExpectingBetter.size(); i++) { //有新包,更新APK
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");
final int reparseFlags;
//不同目錄flags不一樣
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;
}
//加入mStting.mPackages
mSettings.enableSystemPackageLPw(packageName);
try {
scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
mExpectingBetter.clear();
上面這段代碼就是刪除被OTA移除app的目錄,更新新引入的App的目錄。
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLPw(); //為需要sharelibs的apk關(guān)聯(lián)libs,放在pkg.usesLibraryFiles
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
// NOTE: We ignore potential failures here during a system scan (like
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
false /* force dexopt */, false /* defer dexopt */);
}
// Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.readLP();//讀/data/system/package-usage.list
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
updatePermissionsLPw(null, null, updateFlags);//Apk分配權(quán)限
ver.sdkVersion = mSdkVersion;
// clear only after permissions have been updated
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
// If this is the first boot, and it is a normal boot, then
// we need to initialize the default preferred apps.
//第一次啟動(dòng),初始化默認(rèn)程序,如瀏覽器,email程序
if (!mRestoredSettings && !onlyCore) {
mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER);
applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER);
primeDomainVerificationsLPw(UserHandle.USER_OWNER);
}
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
if (mIsUpgrade && !onlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
checkDefaultBrowser();
// All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// can downgrade to reader
mSettings.writeLPr(); //寫package.xml
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
mRequiredVerifierPackage = getRequiredVerifierLPr(); //string
mRequiredInstallerPackage = getRequiredInstallerLPr(); //string
mInstallerService = new PackageInstallerService(context, this); //根據(jù)名字知道大概是app安裝相關(guān)服務(wù)
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
} // synchronized (mPackages)
} // synchronized (mInstallLock)
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Runtime.getRuntime().gc();
// Expose private service for system components to use.
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
}
到此,PMS的構(gòu)造函數(shù)就閱讀完畢了。
總結(jié)
本文由pm命令引入,講解PMS的構(gòu)造過程,這里在梳理一下這個(gè)過程。
- 通過
packages.xml(backup)讀取系統(tǒng)已經(jīng)安裝了的app,保存到mSettings的相關(guān)字段中去。 - 通過掃描系統(tǒng)目錄/用戶app目錄,將掃描的app保存到
PMS的mPackages中去。 - 根據(jù)上兩步的結(jié)果判斷哪些app無效(刪除),哪些被OTA的方式更新,刪除或重建對(duì)應(yīng)app的目錄
- app掃描結(jié)束,app權(quán)限分配,app引用的庫(kù)的關(guān)聯(lián)
- 重新保存這些新信息到
packages.xml供下次開機(jī)使用
參考
- Android-6.0之PMS解析系列
- android系統(tǒng)啟動(dòng)之PMS啟動(dòng)源碼解析
- Android應(yīng)用程序安裝過程源代碼分析
- <<Android 源碼設(shè)計(jì)模式-第十章>>
廣告
