2.2 ApplicationInfo中的 privateFlags 和 PRIVATE_FLAG_PRIVILEGED
看一下ApplicationInfo源代碼
/**
* Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
* {@hide}
*/
public int privateFlags;
/**
* Value for {@link #privateFlags}: set to {@code true} if the application
* is permitted to hold privileged permissions.
*
* {@hide}
*/
public static final int PRIVATE_FLAG_PRIVILEGED = <<;
首先看到@hide,可見不會出現在Android公開API中,也就是說,第三方App無緣使用。先來看看這個字段在Android系統中是如何被使用的。随便找例子:
在系統應用安裝器(PackageInstaller)中,packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private boolean isInstallRequestFromUnknownSource(Intent intent) {
String callerPackage = getCallingPackage();
if (callerPackage != null && intent.getBooleanExtra(
Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
try {
mSourceInfo = mPm.getApplicationInfo(callerPackage, );
if (mSourceInfo != null) {
if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
!= ) {
// Privileged apps are not considered an unknown source.
return false;
}
}
} catch (NameNotFoundException e) {
}
}
return true;
}
frameworks/base/services/core/java/com/android/server/firewall/SenderFilter.java
static boolean isPrivilegedApp(int callerUid, int callerPid) {
if (callerUid == Process.SYSTEM_UID || callerUid == ||
callerPid == Process.myPid() || callerPid == ) {
return true;
}
IPackageManager pm = AppGlobals.getPackageManager();
try {
return (pm.getPrivateFlagsForUid(callerUid) & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
!= ;
} catch (RemoteException ex) {
Slog.e(IntentFirewall.TAG, "Remote exception while retrieving uid flags",
ex);
}
return false;
}
由上面這些代碼執行個體基本可以了解這個字段的用法。下面在PMS掃描/安裝應用的邏輯中找到處理這個字段的部分來分析。
我們知道,PMS是PackageManager的背景服務,先看看PMS側是如何實作PackageManager.getApplicationInfo()。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java:
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get application info");
// writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
// Note: isEnabledLP() does not apply here - always return info
return PackageParser.generateApplicationInfo(
p, flags, ps.readUserState(userId), userId);
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
}
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != ) {
return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
}
}
return null;
}
從這段代碼中看到,擷取到一個ApplicationInfo需要三步:
第一步:從PMS的緩存mPackages中擷取對應包名的PackageParser.Package對象p。
第二步:從PMS的緩存mSettings.mPackages中擷取對應包名的PackageSetting對象ps。
第三步:以上兩步的緩存對象為參數,使用PackageParser.generateApplicationInfo()生成一個ApplicationInfo并傳回。
return PackageParser.generateApplicationInfo(
p, flags, ps.readUserState(userId), userId);
首先看緩存mPackages是怎麼來的。這是mPackages的定義。
// Keys are String (package name), values are Package. This also serves
// as the lock for the global state. Methods that must be called with
// this lock held have the prefix "LP".
@GuardedBy("mPackages")
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
隻有一處會put value:
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
這段代碼出現在掃描方法 scanPackageDirtyLI(),這個方法非常長,取一段相關代碼:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
final File scanFile = new File(pkg.codePath);
if (pkg.applicationInfo.getCodePath() == null ||
pkg.applicationInfo.getResourcePath() == null) {
// Bail out. The resource and code paths haven't been set.
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Code and resource paths haven't been set correctly");
}
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != ) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
} else {
// Only allow system apps to be flagged as core apps.
pkg.coreApp = false;
}
if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != ) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
......
// writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
......
}
......
}
再來看看第三步中,生成ApplicationInfo的邏輯。
PackageParser.generateApplicationInfo():
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
PackageUserState state, int userId) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
if (!copyNeeded(flags, p, state, null, userId)
&& ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) ==
|| state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
// In this case it is safe to directly modify the internal ApplicationInfo state:
// - CompatibilityMode is global state, so will be the same for every call.
// - We only come in to here if the app should reported as installed; this is the
// default state, and we will do a copy otherwise.
// - The enable state will always be reported the same for the application across
// calls; the only exception is for the UNTIL_USED mode, and in that case we will
// be doing a copy.
updateApplicationInfo(p.applicationInfo, flags, state);
return p.applicationInfo;
}
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
ai.uid = UserHandle.getUid(userId, ai.uid);
ai.dataDir = Environment.getDataUserPackageDirectory(ai.volumeUuid, userId, ai.packageName)
.getAbsolutePath();
if ((flags & PackageManager.GET_META_DATA) != ) {
ai.metaData = p.mAppMetaData;
}
if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != ) {
ai.sharedLibraryFiles = p.usesLibraryFiles;
}
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
updateApplicationInfo(ai, flags, state);
return ai;
}
private static void updateApplicationInfo(ApplicationInfo ai, int flags,
PackageUserState state) {
// CompatibilityMode is global state.
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
if (state.installed) {
ai.flags |= ApplicationInfo.FLAG_INSTALLED;
} else {
ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
}
if (state.hidden) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
} else {
ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
}
if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != ;
} else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|| state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
ai.enabledSetting = state.enabled;
}
這段代碼并不複雜,是根據generateApplicationInfo(Package p, int flags, PackageUserState state, int userId)傳入的第一個參數p的成員變量applicationInfo,通過構造方法ApplicationInfo(ApplicationInfo orig)建立一個對象(這個構造方法會逐個字段淺拷貝orig到新的對象),然後通過updateApplicationInfo(ApplicationInfo ai, int flags,PackageUserState state) 完成一些字段的更新計算。在整個上述過程中,privateFlags字段的<<3位沒有變化,也就是說,其表征是否private app的字段位就是第一步中得到的PackageParser.Package對象p對應的字段位。
基于以上分析,我們隻要找到掃描過程生成的緩存PackageParser.Package對象中對應的privateFlags的<<3位是如何指派即可。前文提到過,添加緩存PackageParser.Package對象mPackages.put()隻有在scan方法中被調用。省略無關的邏輯分支,細化上述調用關系如下:
(1)public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) ->
(2)private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) ->
(3)private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException ->
(4)private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException ->
(5)private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException ->
(6)mPackages.put()
依次在上述過程中分析相關代碼段:
(1)掃描system/priv-app下的apk檔案
// 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, );
此外,掃描framework resource也會被這樣處理,一般而言,其對應ROM中 system/framework/framework-res.apk
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
......
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, );
傳給(2)的參數 int parseFlags,隻有上述兩種情況中,PackageParser.PARSE_IS_PRIVILEGED标志位會置為1,其餘為0。
(2)參數 int parseFlags,不修改PackageParser.PARSE_IS_PRIVILEGED标志位,作為同名參數傳給(3)。
(3)參數 int parseFlags,作為同名參數傳給(4)。其間有如下邏輯,如果發現通過諸如OTA更新新的package仍然在/system/priv-app目錄下,則參數int parseFlags中PackageParser.PARSE_IS_PRIVILEGED标志位為置為1。此處暫時存疑,并沒有發現parseFlags重置PackageParser.PARSE_IS_PRIVILEGED标志位為置為0的邏輯,也就是說如果OTA更新的package不在/system/priv-app目錄下了,是如何實作的修改?不影響主流程分析。
if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != ) {
// If new package is not located in "/system/priv-app" (e.g. due to an OTA),
// it needs to drop FLAG_PRIVILEGED.
if (locationIsPrivileged(scanFile)) {
updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
} else {
updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
......
}
if (updatedPkg != null) {
// An updated system app will not have the PARSE_IS_SYSTEM flag set
// initially
parseFlags |= PackageParser.PARSE_IS_SYSTEM;
// An updated privileged app will not have the PARSE_IS_PRIVILEGED
// flag set initially
if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != ) {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
}
(4)參數int parseFlags作為同名參數透傳給(5)。
(5)根據傳入的參數int parseFlags,對傳入的參數PackageParser.Package pkg相關的字段applicationInfo.privateFlags設定标志位<<3
if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != ) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
将pkg放入緩存
// writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
......
}
總結:
(1)使用LOCAL_PRIVILEGED_MODULE設定為true編譯的app,即ROM中的system/priv-app/下的app,通過PackageManager拿到的ApplicationInfo,其privateFlags字段<<3标志位為1,即ApplicationInfo.PRIVATE_FLAG_PRIVILEGED。也就是說,LOCAL_PRIVILEGED_MODULE為true編譯的app即所謂privileged app(特權app)。
(2)除此之外,ROM中的system/framework/framework-res.apk,也具有上述特征。
LOCAL_PRIVILEGED_MODULE 詳解(1)
LOCAL_PRIVILEGED_MODULE 詳解(2)
LOCAL_PRIVILEGED_MODULE 詳解(3)
LOCAL_PRIVILEGED_MODULE 詳解(4)
LOCAL_PRIVILEGED_MODULE 詳解(5)