LOCAL_PRIVILEGED_MODULE 详解(3)
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 = 1<<3;
首先看到@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, 0);if (mSourceInfo != null) {if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)!= 0) {// 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 == 0 ||callerPid == Process.myPid() || callerPid == 0) {return true;}IPackageManager pm = AppGlobals.getPackageManager();try {return (pm.getPrivateFlagsForUid(callerUid) & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)!= 0;} 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:
@Overridepublic ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {if (!sUserManager.exists(userId)) return null;enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get application info");// writersynchronized (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 inforeturn PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId);}if ("android".equals(packageName)||"system".equals(packageName)) {return mAndroidApplication;}if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);}}return null;}
从这段代码中看到,获取到一个ApplicationInfo需要三步:
第一步:从PMS的缓存mPackages中获取对应包名的PackageParser.Package对象p。
PackageParser.Package p = mPackages.get(packageName);
第二步:从PMS的缓存mSettings.mPackages中获取对应包名的PackageSetting对象ps。
PackageSetting ps = mSettings.mPackages.get(packageName);
第三步:以上两步的缓存对象为参数,使用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 mPackagesmPackages.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) != 0) {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) != 0) {pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;}......// writersynchronized (mPackages) {// We don't expect installation to fail beyond this point// Add the new setting to mSettingsmSettings.insertPackageSettingLPw(pkgSetting, pkg);// Add the new setting to mPackagesmPackages.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) == 0|| 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 safelyApplicationInfo 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) != 0) {ai.metaData = p.mAppMetaData;}if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {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) != 0;} 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, 0);
此外,扫描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, 0);
传给(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) != 0) {// 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// initiallyparseFlags |= PackageParser.PARSE_IS_SYSTEM;// An updated privileged app will not have the PARSE_IS_PRIVILEGED// flag set initiallyif ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;}}
(4)参数int parseFlags作为同名参数透传给(5)。
(5)根据传入的参数int parseFlags,对传入的参数PackageParser.Package pkg相关的字段applicationInfo.privateFlags设置标志位<<3
if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;}
将pkg放入缓存
// writersynchronized (mPackages) {// We don't expect installation to fail beyond this point// Add the new setting to mSettingsmSettings.insertPackageSettingLPw(pkgSetting, pkg);// Add the new setting to mPackagesmPackages.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)
LOCAL_PRIVILEGED_MODULE 详解(3)相关推荐
- LOCAL_PRIVILEGED_MODULE 详解(1)
LOCAL_PRIVILEGED_MODULE 是Android ROM编译时的一个变量,其与编译.安装.权限管理等几个方面都有关系.本文整理一下与LOCAL_PRIVILEGED_MODULE有关的 ...
- LOCAL_PRIVILEGED_MODULE 详解(4)
2.3 packages.xml中的privateFlags字段 前文提到,packages.xml 这个文件位于ROM设备的 data/system,读取需要root权限.记录系统中所有安装的应用信 ...
- LOCAL_PRIVILEGED_MODULE 详解(2)
2.安装时作用 2.1 准备知识 在介绍LOCAL_PRIVILEGED_MODULE在安装时的作用之前,先介绍几个关键的准备知识: (1)packages.xml 这个文件位于ROM设备的 data ...
- 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)
首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...
- JVM年轻代,老年代,永久代详解
秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...
- docker常用命令详解
docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...
- 通俗易懂word2vec详解词嵌入-深度学习
https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...
- 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法
深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...
- CUDA之nvidia-smi命令详解---gpu
nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...
- Bert代码详解(一)重点详细
这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...
最新文章
- 如何才能做好绩效管理?
- 我的第一个appium+Python自动化实例
- 彻底解决Spring mvc中时间的转换和序列化等问题
- 转贴 jQuery Datepicker by Example
- 并查集 - 交换字符串中的元素
- 第三章 中间件,3.1 万亿级数据洪峰下的分布式消息引擎(作者:冯嘉、誓嘉、尘央、牟羽)...
- 【精辟】socket阻塞与非阻塞,同步与异步,select,pool,epool
- 使用rdbtools分析redis内存使用
- keil用c语言编程怎么打开,用keil软件新建,关闭,打开一个完整工程的操作流程...
- 攻防世界 ics-05 write up
- python 微信公众号接口之上传图片素材
- Docker系列之八:在Dockerfile中使用多段构建Muti-stage build
- 百度云盘停止服务器,又一家网盘关闭!不要再问为什么百度网盘要收费了
- 论文阅读笔记---《TransferNet: An Effective and Transparent Framework for Multi-hop Question Answering over》
- 基于面部视频的心率监测系统 day 10
- 大恒相机开发问题(错误码: -8)无法打开相机
- Android系统SystemUI启动过程
- 对于pytorch中nn.CrossEntropyLoss()与nn.BCELoss()的理解和使用
- 找计算机研究的论文18个平台
- Markdown学习(入门级)