permission
文件的处理在PMS
的构造方法中初始化了PermissionManagerService
,经过PermissionManagerService.create()
方法,相关调用以下:java
public static PermissionManagerInternal create(......) {
......
new PermissionManagerService(context, defaultGrantCallback, externalLock);
......
}
PermissionManagerService(......) {
......
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler);
......
SystemConfig systemConfig = SystemConfig.getInstance();
mSystemPermissions = systemConfig.getSystemPermissions();
......
}
复制代码
上面列出来PermissionManagerService
初始化的过程当中两个操做:android
Watchdog
监听,此处列出它来主要是为了呼应前面的章节。哈哈哈,重点是第二点SystemConfig
的getSystemPermissions
方法来获取系统的Permission
列表 public SparseArray<ArraySet<String>> getSystemPermissions() {
return mSystemPermissions;
}
复制代码
而对于mSystemPermissions
,是在SystemConfig
的构造方法中完成的,相关代码以下:api
//单例模式
SystemConfig() {
// 读取 /system/etc/sysconfig 目录下的 permission 文件
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// 读取 /system/etc/permissions 目录下的 permission 文件
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
......
// 读取 /vendor/etc/sysconfig 目录下的 permission 文件
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
// 读取 /vendor/etc/permissions 目录下的 permission 文件
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
...// 省略 /oem、/odm、/product对应分区目录下 readPermissions()
}
复制代码
不用说,核心是这个readPermissions()
方法,看看:markdown
void readPermissions(File libraryDir, int permissionFlag) {
// Read permissions from given directory.
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
return;// 若是目录不存在或者libraryDir不是目录,直接返回
}
if (!libraryDir.canRead()) {
return;// 若是目录不可读,直接返回
}
File platformFile = null;
// 循环能处理目录文件
for (File f : libraryDir.listFiles()) {
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
// 检测到platform.xml文件,先记录,循环中不作处理
platformFile = f;
continue;
}
if (!f.getPath().endsWith(".xml")) {
// 跳过非xml文件
continue;
}
if (!f.canRead()) {
// 跳过不可读文件
continue;
}
readPermissionsFromXml(f, permissionFlag);// 解析xml
}
// 最后,解析platform.xml文件
if (platformFile != null) {
readPermissionsFromXml(platformFile, permissionFlag);
}
}
复制代码
readPermissions()
方法先检测指定目录下的xml
文件,而后调用readPermissionsFromXml
方法来解析文件并将解析结果填充到SystemConfig
对应的数据结构中。数据结构
readPermissionsFromXml()
方法内容以下:app
private void readPermissionsFromXml(File permFile, int permissionFlag) {
FileReader permReader = new FileReader(permFile);
......
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
int type;
// 查找 START_TAG 或 END_DOCUMENT
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
// 从 START_TAG 开始解析
if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
// 只针对<permissions/>和<config/>标签进行解析
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException(...);
}
......
while (true) {
......
String name = parser.getName();
// 可以识别的标签以下:
if ("group".equals(name) && allowAll) {
} else if ("permission".equals(name) && allowPermissions) {
} else if ("assign-permission".equals(name) && allowPermissions) {
} else if ("library".equals(name) && allowLibs) {
} else if ("feature".equals(name) && allowFeatures) {
} else if ("unavailable-feature".equals(name) && allowFeatures) {
} else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
} else if ("allow-in-power-save".equals(name) && allowAll) {
} else if ("allow-in-data-usage-save".equals(name) && allowAll) {
} else if ("allow-unthrottled-location".equals(name) && allowAll) {
} else if ("allow-implicit-broadcast".equals(name) && allowAll) {
} else if ("app-link".equals(name) && allowAppConfigs) {
} else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
} else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
} else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
} else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) {
} else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name) && allowAppConfigs) {
} else if ("disabled-until-used-preinstalled-carrier-app".equals(name) && allowAppConfigs) {
} else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
} else if ("oem-permissions".equals(name) && allowOemPermissions) {
} else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
} else {
}
}
}
......
}
复制代码
if else
中的字符串就是xml
的关键标签,解析时会分别保存到SystemConfig
对应的final
成员变量中,这部分就不贴源码了哈ide
从整个解析过程来看,这些xml
文件分红了<permissions/>
和<config/>
两个大标签,而后又包含permission
、library
等子标签。函数
以platform.xml
为例,咱们来看下相关内容结构:oop
<permissions>
<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>
<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>
......
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
<assign-permission name="android.permission.WAKE_LOCK" uid="media" />
......
<library name="android.test.base" file="/system/framework/android.test.base.jar" />
<library name="android.test.mock" file="/system/framework/android.test.mock.jar" />
......
<permissions/>
复制代码
platform.xml
上面主要由3块内容:测试
<permission/>
:将属性name
中字符串表示的权限赋予<group/>
标签中经过gid
指定的用户组<assign-permission/>
:将属性name
中字符串表示的权限赋予经过属性uid
指定的用户<library/>
:指定系统自动加载的动态库
Android
的不少功能都是经过所谓的permission
字符串来保护的,应用中若是须要使用某项受保护的功能,须要在它的AndroidManifest
文件中显示声明该功能对应的permission
字符串。可是,有些功能只能由特定的用户组使用,而在platform.xml
文件中定义的就是这种规则
Android 5.0
引入了Split APK
机制,主要是为了解决65536
上限以及APK
安装包愈来愈大等问题。
Split APK
机制能够将一个APK
拆分红多个独立APK
。 在引入了Split APK
机制后,APK
有两种分类:
Single APK
:安装文件为一个完整的APK
,即base APK
。Android
称其为Monolithic
。Mutiple APK
:安装文件在一个文件目录中,其内部有多个被拆分的APK
,这些APK
由一个base APK
和一个或多个split APK
组成。Android
称其为Cluster
。scanDirLI
上面两种格式的扫描逻辑,是在PMS
的构造方法中,经过scanDirLI
来实现。
scanDirLI
代码以下:
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = scanDir.listFiles();
......
// 建立 ParallelPackageParser apk 解析帮助类
// ParallelPackageParser核心实际上是一个 ExecutorService
// 当前目录下全部的 apk 解析任务经过ExecutorService.submit来建立
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(...)) {
// 遍历查找指定目录下的apk文件
int fileCount = 0;
for (File file : files) {
// 判读是否为apk文件
// 或者不是smdl2tmp打头的文件夹
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
continue;
}
// 建立提交解析任务,解析符合条件的文件
parallelPackageParser.submit(file, parseFlags);
// 记录任务数量
fileCount++;
}
// 根据建立的任务数量,查看任务结果
for (; fileCount > 0; fileCount--) {
// 此方法是阻塞的,直到队列中有结果添加进来
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
// 对解析结果进行判断
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// 若是是静态共享库,进行rename操做
......
// 若是应用正常,执行进一步的扫描
if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
}
}
...// 省略异常状况处理
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
errorCode != PackageManager.INSTALL_SUCCEEDED) {
.......
removeCodePathLI(parseResult.scanFile);
}
}
}
}
复制代码
根据上面的代码咱们能够划分为两个阶段:
scanDirLI
之parsePackage
APK
解析阶段APK
的解析是经过ParallelPackageParser
类的submit()
方法提交解析任务来实现的scanDirLI
之scanPackageChildLI
APK
添加到PMS
的mPackages
中咱们逐一来分析
scanDirLI
之parsePackage
ParallelPackageParser
类是Android
封装的一个线程池,核心实现是ExecutorService
类。主要是为了方便收集并行解析的结果。
解析任务的建立都是经过此类的submit()
方法来完成的。
ParallelPackageParser
的submit()
方法咱们来简单看下:
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
ParseResult pr = new ParseResult();
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags);
......
mQueue.put(pr);
......
});
}
protected PackageParser.Package parsePackage(......) throws PackageParser.PackageParserException {
return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}
复制代码
函数中的submit()
新建了一个解析线程,线程中建立了一个PackageParser
对象,而后调用它的parsePackage
方法来进行解析。
PackageParser
的parsePackage()
方法PackageParser
的parsePackage()
方法以下:
public Package parsePackage(File packageFile, int flags, boolean useCaches) throws PackageParserException {
......
// 根据是否为directory执行不一样的解析逻辑
if (packageFile.isDirectory()) {
// 解析一组apk
parsed = parseClusterPackage(packageFile, flags);
} else {
// 解析单个apk
parsed = parseMonolithicPackage(packageFile, flags);
}
......
}
复制代码
到这里咱们能够发现,真正的APK
解析流程就在PackageParser
中,你们能够跟踪下上面两种状况下的解析,以parseClusterPackage()
为例,咱们看下
parseClusterPackage()
解析split
应用private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
// 获取应用目录的 PackageLite 对象,这个对象中分开保存了目录下的
// base应用和其余非核心应用
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
......
try {
// 先装载base应用资源
final AssetManager assets = assetLoader.getBaseAssetManager();
final File baseApk = new File(lite.baseCodePath);
final Package pkg = parseBaseApk(baseApk, assets, flags);
......
// 再装载split应用资源
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
......
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
parseSplitApk(pkg, i, splitAssets, flags);
}
}
pkg.setCodePath(packageDir.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
}
......
}
复制代码
parseClusterPackage()
方法中首先调用parseClusterPackageLite()
方法对目录下的APK
进行初步分析
base
和split
应用:base
应用只有一个。split
应用能够没有或者多个split
应用的做用是用来保存资源和代码parseClusterPackageLite()
方法会将分析结果经过PackageLite
对象返回parseClusterPackage()
方法接下来:
base
应用经过parseBaseApk()
来进行分析split
应用经过parseSplitApk()
来进行分析Package
对象中对于parseMonolithicPackage()
方法省去了parseSplitApk
的过程。但在解析时两者都会执行到parseBaseApk()
方法,而parseBaseApk()
最后会执行到parseBaseApkCommon()
方法中。
parseBaseApkCommon()
解析AndroidManifest.xml
parseBaseApkCommon()
其实主要是对AndroidManifest.xml
的解析,简要方法以下:
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException {
......
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
// 读取 AndroidManifest 的sharedUserId信息
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
......
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
......
String tagName = parser.getName();
......
if (tagName.equals(TAG_APPLICATION)) {
......
// 解析 <application/> 标签信息
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
}
// 省略各类 AndroidManifest 的标签解析
......
else if (tagName.equals(TAG_PERMISSION)) {
// 解析 permission 标签
if (!parsePermission(pkg, res, parser, outError)) {
return null;
}
}
......
}
......
return pkg;
}
复制代码
这个方法对AndroidManifest.xml
的标签进行解析,把解析结果存放到到Package
对象中,并返回。
咱们在while
循环中省略了不少AndroidManifest.xml
的标签,简单介绍下应用常见的一些标签:
<manifest/>
:AndroidManifest.xml
的根元素。
<application/>
元素xlmns:android
属性:android
的XML
命名空间package
属性:应用的包名versionCode
:指定应用版本号,整数类型versionName
:显示给用户的版本号,字符串类型<uses-permission/>
:声明某种权限。应用必须声明可权限才可使用对应的功能<permission-group/>
:定义权限组<permission-tree/>
:定义一颗权限树,能够经过PackageManager.addPermission
插入Permission
<path-permission/>
:为ContentProvider
下的数据目录定义访问权限<application/>
:应用配置的根元素,包含全部与应用有关的属性配置<activity/>
:Activity
的声明标签<activity-alias/>
:Activity
的别名定义标签<service/>
:Service
组件的声明标签<provider/>
:ContentProvider
的声明标签<receiver/>
:Broadcast Receiver
的声明标签<intent-filter/>
:<intent-filter/>
表示Intent
过滤的声明
<intent-filter/>
能够放在<activity/>
、<activity-alias/>
、<service/>
、<receiver/>
等标签下<intent-filter/>
必须包含有<action/>
,用于描述Intent
的名称<category/>
,用于描述Intent
的类别<data/>
,用于描述Intent
须要处理的数据格式<meta-data/>
:用于储存预约义的数据。
<meta-data/>
能够放在<activity/>
、<activity-alias/>
、<application/>
、<service/>
、<receiver/>
等标签下<meta-data/>
数据通常以键值对的形式出现,可使用ActivityInfo
、ServiceInfo
、ApplicationInfo
队形的metaData
变量来访问它们<instrumentation/>
:用来声明Instrumentation
测试类来监视应用的行为<uses-sdk/>
:指定应用所需使用的Android SDK
的最高版本、最低版本、目标版本。<uses-configration/>
:声明应用须要的一些系统配置,一共有五种定义 <uses-configuration android:reqFiveWayNav=["true" | "false"] android:reqHardKeyboard=["true" | "false"] android:reqKeyboardType=["undefined" | "nokeys" | "qwerty" | "twelvekey"] android:reqNavigation=["undefined" | "nonav" | "dpad" | "trackball" | "wheel"] android:reqTouchScreen=["undefined" | "notouch" | "stylus" | "finger"] />
复制代码
<uses-feture/>
:声明应用使用的软件或硬件feature
<uses-library/>
:指定应用须要使用的用户库,系统将会在应用运行时装载这些库<support-screens/>
:指定应用支持的屏幕种类到这里,
Android
已经把APK
解析完成,并建立了一个完整的Package
对象。那么接下来就是经过scanPackageChildLI
方法将解析好的Package
对象添加到系统中的mPackages
中了
scanDirLI
之scanPackageChildLI
前面已经讲过,对于扫描成功的应用,会循环执行scanPackageChildLI()
将其添加到系统中。
代码以下:
private PackageParser.Package scanPackageChildLI(......){
......
// 扫描当前应用
PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
// 循环扫描子应用
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
......// 省略一个递纳入口
return scannedPkg;
}
复制代码
scanPackageChildLI()
方法最重要的操做就是经过addForInitLI()
方法来对当前应用作进一步的解析,主要流程以下:
private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException {
......
// 设置应用资源、代码等相关的路径
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
// 判断系统应用是否须要升级
synchronized (mPackages) {
......
// 检查应用是否属于rename标签
renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
......
// 检查是否属于update标签
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
......
}
// 判断升级的重点是将当前package与mSettings中的进行比对
// 咱们知道mSettings中的package数据基本上都是从platform.xml文件中解析出来的
// 这种对比至关于当前扫描到的应用和其历史信息的比对,这里主要是检查<renamed-package/>和<updated-package/>标签
......
// 扫描应用文件的签名
collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
......
// 处理应用包名有冲突的状况
boolean shouldHideSystemApp = false;
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
// 当前扫描到的应用是系统应用,可是也存在一个同名的普通应用
// 先比较签名
if (...) {
......
// 签名不匹配,删除扫描到系统的应用
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
pkgSetting = null;
} else if (newPkgVersionGreater) {
......
// 签名相同,当前系统应用版本高,移除普通应用
InstallArgs args = createInstallArgsForExisting(...);
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
} else {
// 签名相同,当前系统应用版本低,须要记录升级关系
// 先把系统应用隐藏
shouldHideSystemApp = true;
......
}
}
// 调用 scanPackageNewLI 继续扫描
final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
return scannedPkg;
}
复制代码
又继续调用了scanPackageNewLI()
来扫描,咱们简单看看作了什么:
private PackageParser.Package scanPackageNewLI(......) throws PackageManagerException {
......
synchronized (mPackages) {
......
SharedUserSetting sharedUserSetting = null;
if (pkg.mSharedUserId != null) {
// 若是当前应用的sharedUserId不为空的话
// 将该sharedUserId添加到Settings的集合中
sharedUserSetting = mSettings.getSharedUserLPw(
pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
......
}
......
try {
......
// 继续调用 scanPackageOnlyLI 进行扫描操做
// scanPackageOnlyLI 基本上就是组装PackageSetting对象的过程
// ScanResult 中会包含扫描解析出的PackageSetting对象
final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
if (result.success) {
// 扫描成功后,将应用相关数据组织到系统中
commitScanResultsLocked(request, result);
}
}
......
}
return pkg;
}
复制代码
代码中会先对应用的sharedUserId
进行检查,不为空的话添加到Settings
的mSharedUsers
集合中。
接下来,调用scanPackageOnlyLI
作进一步的扫描,扫描成功后会调用commitScanResultsLocked()
把package
添加到PMS
的mPackages
中。
scanPackageOnlyLI
和commitScanResultsLocked()
就不细讲了哈,对这两个方法留有印象就好啦。
咱们下面重点了解下PMS
启动后应用安装的过程。下一篇深刻Android系统(十)PMS-3-应用安装过程