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)相关推荐

  1. LOCAL_PRIVILEGED_MODULE 详解(1)

    LOCAL_PRIVILEGED_MODULE 是Android ROM编译时的一个变量,其与编译.安装.权限管理等几个方面都有关系.本文整理一下与LOCAL_PRIVILEGED_MODULE有关的 ...

  2. LOCAL_PRIVILEGED_MODULE 详解(4)

    2.3 packages.xml中的privateFlags字段 前文提到,packages.xml 这个文件位于ROM设备的 data/system,读取需要root权限.记录系统中所有安装的应用信 ...

  3. LOCAL_PRIVILEGED_MODULE 详解(2)

    2.安装时作用 2.1 准备知识 在介绍LOCAL_PRIVILEGED_MODULE在安装时的作用之前,先介绍几个关键的准备知识: (1)packages.xml 这个文件位于ROM设备的 data ...

  4. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  5. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

  6. docker常用命令详解

    docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...

  7. 通俗易懂word2vec详解词嵌入-深度学习

    https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...

  8. 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法

    深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...

  9. CUDA之nvidia-smi命令详解---gpu

    nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...

  10. Bert代码详解(一)重点详细

    这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...

最新文章

  1. 如何才能做好绩效管理?
  2. 我的第一个appium+Python自动化实例
  3. 彻底解决Spring mvc中时间的转换和序列化等问题
  4. 转贴 jQuery Datepicker by Example
  5. 并查集 - 交换字符串中的元素
  6. 第三章 中间件,3.1 万亿级数据洪峰下的分布式消息引擎(作者:冯嘉、誓嘉、尘央、牟羽)...
  7. 【精辟】socket阻塞与非阻塞,同步与异步,select,pool,epool
  8. 使用rdbtools分析redis内存使用
  9. keil用c语言编程怎么打开,用keil软件新建,关闭,打开一个完整工程的操作流程...
  10. 攻防世界 ics-05 write up
  11. python 微信公众号接口之上传图片素材
  12. Docker系列之八:在Dockerfile中使用多段构建Muti-stage build
  13. 百度云盘停止服务器,又一家网盘关闭!不要再问为什么百度网盘要收费了
  14. 论文阅读笔记---《TransferNet: An Effective and Transparent Framework for Multi-hop Question Answering over》
  15. 基于面部视频的心率监测系统 day 10
  16. 大恒相机开发问题(错误码: -8)无法打开相机
  17. Android系统SystemUI启动过程
  18. 对于pytorch中nn.CrossEntropyLoss()与nn.BCELoss()的理解和使用
  19. 找计算机研究的论文18个平台
  20. Markdown学习(入门级)

热门文章

  1. 经纬度转换 gcj02转wgs84
  2. 宏碁台式计算机u盘启动,宏基台式机U盘重装怎么设置U盘启动项
  3. 银行卡四要素认证api接口_银行卡实名认证查询-银行卡四要素鉴权
  4. 读书感受 之 《穷查理宝典》
  5. 前端实现——html2pdf功能(完成)
  6. 2.11 神奇的自定义画笔 [Ps教程]
  7. ps画笔工具、填充选区文字工具、合成
  8. Mac上最强大好用的的右键工具「iRightMouse 超级右键」(上)
  9. Unity 抗锯齿方案梳理
  10. ae合成设置快捷键_【教程】你不知道的全网最全ae快捷键【基础篇】