1、概述。
此处只研究了一下framework层的权限相关的代码和逻辑,至于和linux层的对应后续再跟踪。分析6.0的权限模型,其实就是分
析应用apk被解析安装到手机上时,是如何解析apk的manifest清单文件中的关于权限的一些配置的。此处从两方面着手的安装流程,
一方面是系统解析本身就有的apk,一方面是下载到手机里面的apk,然后点击安装。
2、解析系统应用。
此处就从SystemServer开始介绍,前面开机如何加载这个服务就不介绍了。
Step1.PackageManagerService.main
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);ServiceManager.addService("package", m);return m;
}
这个函数创建了一个PMS的实例,然后把这个服务添加到ServicesManager中去。那么创建这个实例的时候就会去走
PMS的构造,如下。
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
......
mSettings = new Settings(mPackages); //实例化一个Settings类的对象,用于在解析这个apk的过程中存放各种变量
......
//加载一些全局的系统的配置,如system/etc/Permissions 文件下面的各个xml的解析(有权限,有特征等等)
//然后放到相应的成员变量如:
//1、final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();每个权限名字对应一
个
//PermissionEntry,这个成员变量解析的是<permission>的标签。
//2、final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();每个uid,所对应的所有
//
权限,这个成员变量是解析<assign-permission>标签下的权限。
SystemConfig systemConfig = SystemConfig.getInstance();
......
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {SystemConfig.PermissionEntry perm = permConfig.valueAt(i);BasePermission bp = mSettings.mPermissions.get(perm.name);if (bp == null) {//一个权限对应一个 BasePermission,和是那个应用的权限无关。最终都放到 mSettings.mPermissionsbp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);mSettings.mPermissions.put(perm.name, bp);}if (perm.gids != null) {bp.setGids(perm.gids, perm.perUser);}}
......
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
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);//扫描 /system/framework
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR| PackageParser.PARSE_IS_PRIVILEGED,scanFlags | SCAN_NO_DEX, 0);//扫描 /system/priv-app
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);
//扫描 /system/appfinal File systemAppDir = new File(Environment.getRootDirectory(), "app");scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
//扫描vendor/appFile vendorAppDir = new File("/vendor/app");scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 扫描oem/appfinal File oemAppDir = new File(Environment.getOemDirectory(), "app");scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);//以上会调用scanDirLI函数来设备上的指定目录下的apk文件,注意其传入的参数有各种系统级别的解析才传入的以// 来证明是系统应用等等。......
}
Step2.PackageManagerService.scanDirLI
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();......for (File file : files) {//是否是个apk文件或者是个目录。final boolean isPackage = (isApkFile(file) || file.isDirectory()) &&!PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}.............try {//调用scanPackageLI进一步解析scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,scanFlags, currentTime, null);} catch (PackageManagerException e) {...... }}}此函数就是解析传过来的第一个参数,如果是apk文件或者是目录,就对其一一调用 scanPackageLI进行解析。
Step3.PackageManagerService.
scanPackageLI
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
PackageParser pp = new PackageParser();
......
final PackageParser.Package pkg;
try {pkg = pp.parsePackage(scanFile, parseFlags);} catch (PackageParserException e) {... }......| SCAN_UPDATE_SIGNATURE, currentTime, user);......return scannedPkg;
}
通过创建PackagePaser实例并调用它的parsePackage来解析pkg文件,注意这个函数的参数也是通过path路劲来解析的,最终还
得需要
另外的一个重写的
scanPackageLI方法来实现包解析玩的pkg保存在PMS中。
Step4.PackageParser.parsePackage
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
//通过判断是一个apk文件还是一个目录if (packageFile.isDirectory()) {return parseClusterPackage(packageFile, flags);} else {return parseMonolithicPackage(packageFile, flags);}}此函数会进一步的判断传入的是一个apk文件还是一个目录,进而在调用不同的解析方法。最终都会调用到parseBaseApk里private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)throws PackageParserException {if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,"Invalid package file: " + apkPath);}
// The AssetManager guarantees uniqueness for asset paths, so if this asset path// already exists in the AssetManager, addAssetPath will only return the cookie// assigned to it.int cookie = assets.addAssetPath(apkPath);if (cookie == 0) {throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,"Failed adding asset path: " + apkPath);}return cookie;}
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)throws PackageParserException {......Resources res = null;XmlResourceParser parser = null;try {res = new Resources(assets, mMetrics, null);......//zy this method is main to get ANDROID_MANIFEST_FILENAME outparser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];final Package pkg = parseBaseApk(res, parser, flags, outError);......return pkg;
} catch (PackageParserException e) {...... } catch (Exception e) {......} finally {IoUtils.closeQuietly(parser);}}
这个函数就是先进行一些基本的判断那,如路径合不合法等,并且解析出apk文件中的AndroidManifest.xml文件,然后调用另一
个
重载的
parsePackage函数对这个文件进行进一步的解析。
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
......
final Package pkg = new Package(pkgName);//第一次出现这个类,用于封装包的各种信息
......
int outerDepth = parser.getDepth();
//没有到AndroidManifest.xml文件的结尾处,就一直循环。
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
......
String tagName = parser.getName();
if
(
tagName
.
equals
(
"application"
))
{
......
} else if ( tagName . equals ( "overlay" )) {......} else if ( tagName . equals ( "key-sets" )) {......} else if ( tagName . equals ( "permission-group" )) {if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {return null;}} else if ( tagName . equals ( "permission" )) {if (parsePermission(pkg, res, parser, attrs, outError) == null) { return null;}} else if ( tagName . equals ( "permission-tree" )) {if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) { return null;}} else if ( tagName . equals ( "uses-permission" )) {if (!parseUsesPermission(pkg, res, parser, attrs)) { return null;}} else if (tagName.equals("uses-permission-sdk-m")|| tagName.equals("uses-permission-sdk-23")) { if (!parseUsesPermission(pkg, res, parser, attrs)) { return null; } }.......//解析各种标签,然调用本类的相应的方法,完成解析并保存在pkg的变量当中,最后把pkg返回。}
return pkg;
}
这个函数用来完成对AndroidManifest.xml文件的各个标签进行解析,此处我们只关心和权限相关的
uses-permission标签。
Step5 PackageParser.parseUsesPermission
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
AttributeSet attrs) throws XmlPullParserException, IOException {
......
if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) {if (name != null) {int index = pkg.requestedPermissions.indexOf(name);if (index == -1) {pkg.requestedPermissions.add(name.intern());} else {...... }}}return true;}此函数就是把 Uses-Permission标签下的每一个权限的名字添加到pkg. requestedPermissions的变量当中。然后一层层返回到step3中
Step.6 PackageManagerService.scanPackageLI
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
boolean success = false;
try {final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,currentTime, user);success = true;return res;} finally {...... }
}
继续调用
scanPackageDirtyLI来完成解析。注意到现在为止pkg里面已经有了大量的信息。
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
......
//通过pkg创建一个PackageSetting对象,也是临时保存一个指定的package的数据和信息。
//如此出会找到这个package的uid、还会通过层层的父类的初始化new PermissionsState()类。
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
pkg.applicationInfo.primaryCpuAbi,
pkg.applicationInfo.secondaryCpuAbi,
pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
user, false);
......
pkg.applicationInfo.uid = pkgSetting.appId; //赋值uid
pkg.mExtras = pkgSetting;
......
synchronized (mPackages) {
//把
pkgSetting保存到Settings的变量
mPackages中, String对应于包名。
//final ArrayMap<String, PackageSetting> mPackages =
new ArrayMap<String, PackageSetting>();
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
//把pkg保存到PMS的成员变量
mPackages
中,Stirng对应于包名。
//
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
mPackages.put(pkg.applicationInfo.packageName, pkg);
......
int N = pkg.providers.size();
......//解析
providers 把相应的provider添加到mProviders当中。
N = pkg.services
.size();
......//解析services
把相应的
services
添加到mS
ervices
当中。
N = pkg.receivers
.size();
......//解析receivers
把相应的
receivers
添加到
mR
eceivers
当中。
N = pkg.activities
.size();
......//解析activities
把相应的
activities
添加到
mA
ctivities
当中。
N = pkg.permissionGroups
.size();
......//解析permissionGroups
把相应的
permissionGroups
添加到
mPermissionGroups
当中。
N = pkg.permissions
.size();
......//解析permissions
把相应的permissions
添加到permissionMap
当中。
N = pkg.instrumentation.size();
......//解析
instrumentation
把相应的
instrumentation
添加到
mInstrumentation
当中。
......
}
return pkg;
}
此函数现在看来除啦进一步解析pkg外,还把pkg的一些属性添加到PMS的成员变量中。
Step7.PackageManagerService
至此完成了对系统中的相应的目录下的apk的解析,那么它们的权限怎么设定的那?完全没有踪影啊,别急。接下来继续回到
PMS的构造当中。
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
//此处接着Step1的步骤分析。......int updateFlags = UPDATE_PERMISSIONS_ALL;if (ver.sdkVersion != mSdkVersion) {updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;}updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);/
}
在构造中解析完系统的apk后调用
updatePermissionsLPw来设定应用的权限。
Step 8.PackageManagerService.
updatePermissionsLPw
private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
......
//下面的这个flags从PMS构造中传入,所以符合条件可以进入,并且replace为false。
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
//
mPackages,在Step 6中已经完成保存
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {final String volumeUuid = getVolumeUuidForPackage(pkg);final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)&& Objects.equals(replaceVolumeUuid, volumeUuid);grantPermissionsLPw(pkg, replace, changingPkg);}
}
//从PMS构造中传过来的是null,此处一般手动安装的应用会走,并且
replace
为true。
if (pkgInfo != null) {final String volumeUuid = getVolumeUuidForPackage(pkgInfo);final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)&& Objects.equals(replaceVolumeUuid, volumeUuid);grantPermissionsLPw(pkgInfo, replace, changingPkg);}
}
此函数更具传入的参数的不同调用方法中不同地方的
grantPermissionsLPw,并把相应的参数传入.
Step 9.PackageManagerService.
grantPermissionsLPw
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
String packageOfInterest) {
//在Step 6 已经赋值
pkg.mExtras。
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {return;}//此时的 permissionsState不为null,因为在初始化 PackageSetting 时,已经实例化它,但是它的成员变量很多//都是null因为实例化它的时候用的是无参数的构造,如: public ArrayMap<String, PermissionData> mPermissions;//key对应于权限名字,Values对应于一个用于封装的 PermissionData类PermissionsState permissionsState = ps.getPermissionsState();PermissionsState origPermissions = permissionsState;......//已经在Step 5中完成了解析。final int N = pkg.requestedPermissions.size();for (int i=0; i<N; i++) {final String name = pkg.requestedPermissions.get(i);//在Step1中(还有别的地方)已经初始化此项final BasePermission bp = mSettings.mPermissions.get(name);......final String perm = bp.name;boolean allowedSig = false;int grant = GRANT_DENIED;......//从何处导致的 protectionLevel的不同,暂时还没分析出来!!!!??????final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;switch (level) {case PermissionInfo.PROTECTION_NORMAL: {// 当不是 PROTECTION_DANGEROUS类型的时候都安装为安装权限。grant = GRANT_INSTALL;} break;
case PermissionInfo.PROTECTION_DANGEROUS: {if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {// 当targtversion小于6.0是赋值这个。grant = GRANT_INSTALL_LEGACY;} else if (origPermissions.hasInstallPermission(bp.name)) {// For legacy apps that became modern, install becomes runtime.grant = GRANT_UPGRADE;} else if (mPromoteSystemApps && isSystemApp(ps)&& mExistingSystemPackages.contains(ps.name)) {grant = GRANT_UPGRADE;} else {// 想是正常的6.0的第一次安装的时候,就会赋值此处。grant = GRANT_RUNTIME;}} break;case PermissionInfo.PROTECTION_SIGNATURE: {// For all apps signature permissions are install time ones.allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);if (allowedSig) {grant = GRANT_INSTALL;}} break;}......}if(grant != GRANT_DENIED){......switch (grant) {case GRANT_INSTALL: {
for (int userId : UserManagerService.getInstance().getUserIds()) {if (origPermissions.getRuntimePermissionState( bp.name, userId) != null) {origPermissions.revokeRuntimePermission(bp, userId);origPermissions.updatePermissionFlags(bp, userId,PackageManager.MASK_PERMISSION_FLAGS, 0);
changedRuntimePermissionUserIds = ArrayUtils.appendInt(changedRuntimePermissionUserIds, userId);}}// 允许所有的安装时的权限,通过 grantInstallPermission。此处的逻辑在最后单独分析。int flag = permissionsState.grantInstallPermission(bp);android.util.Log.d("zy_test","GRANT_INSTALL flag = "+flag);if (flag != PermissionsState.PERMISSION_OPERATION_FAILURE) {changedInstallPermission = true;}} break;
case GRANT_INSTALL_LEGACY: {//当应用的targetversion<23并且权限是运行时权限,才会走到这里。也是全部允许。int flag = permissionsState.grantInstallPermission(bp);if (flag !=PermissionsState.PERMISSION_OPERATION_FAILURE) {changedInstallPermission = true;}} break;case GRANT_RUNTIME: {for (int userId : UserManagerService.getInstance().getUserIds()) {PermissionState permissionState = origPermissions .getRuntimePermissionState(bp.name, userId);final int flags = permissionState != null ? permissionState.getFlags() : 0;//此时 hasRuntimePermission会返回false,由于 origPermissions. mPermissions==nullif (origPermissions.hasRuntimePermission(bp.name, userId)) {//所以不会进入到这里进而允许所有的运行时权限。if (permissionsState.grantRuntimePermission(bp, userId) ==PermissionsState.PERMISSION_OPERATION_FAILURE) {changedRuntimePermissionUserIds = ArrayUtils.appendInt(changedRuntimePermissionUserIds, userId);}}permissionsState.updatePermissionFlags(bp, userId, flags, flags);}} break;
case GRANT_UPGRADE: {......}......
}else{......}}......
}
此函数真正的初始化了每个应用的framework层的权限设置,根据不同的权限类型和应用的targetversion。但是这样的话系统应用
很多必要的权限默认也是不允许的,这样体验很不好。
Step 10 PackageManagerService.systemReady()
systemServer调用完PMS的main方法后,会调用PMS的systemReady。
public void systemReady() {
......
for (int userId : grantPermissionsUserIds) {//此处初始化所有用户的一些默认的权限。
mDefaultPermissionPolicy.grantDefaultPermissions(userId);}......
}
我们只分析和权限相关的,
mDefaultPermissionPolicy 是
DefaultPermissionGrantPolicy类的实例,在PMS创建的时候就完成了实
例化,
通过调用
grantDefaultPermissions来初始化一些应用的默认权限。
Step 11 DefaultPermissionGrantPolicy.
grantDefaultPermissions
public void grantDefaultPermissions(int userId) {
grantPermissionsToSysComponentsAndPrivApps(userId);//对PMS.mPackages里面的符合一定条件pkg的权限的初始化。
grantDefaultSystemHandlerPermissions(userId);//对如:Mms、Dialer、Contact等应用的权限的初始化
}
此函数分别通过再次调用另外的方法,完成最终的某些应用的权限的初始化,此处就不再深入分析,有兴趣的可以自己在往下看,
也
很简单。最终是通过PMS的grantRuntimePermission(,,)来完成对应用的权限的设置。至此我们完成了系统的apk的解析和默
认权限的设置,当中忽略了很多细节有兴趣可以自己深入研究,此文只起到抛砖引玉作用。
3、解析下载好的apk(手动点击安装)
手动点击安装和系统的解析很多地方都会走相同的代码,只是一开始的入口方式不同,此处就不重复讨论相同的地方了。且下面的分析
省略了具体的PackageInstaller对点击安装apk往外发送的广播的处理,直接分析最终的在PMS的实现。
Step 1.PackageManagerService.installPackage
此处就直接从安装的接口调用处开始分析。
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,String packageAbiOverride) {installPackageAsUser(originPath, observer, installFlags, installerPackageName,verificationParams, packageAbiOverride, UserHandle.getCallingUserId());}直接调用了另外的一个函数 installPackageAsUser。public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,int installFlags, String installerPackageName, VerificationParams verificationParams,String packageAbiOverride, int userId) {
......
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,null, verificationParams, user, packageAbiOverride, null);mHandler.sendMessage(msg);
}
此函数主要就是做些基本的判断,如:是否有权限、是否可以默认允许安装时权限。最后往mHandler
发送消息,进行处理。
Step 2.PackageManager
Service
.
PackageHandler
PackageHandler.doHandleMessage(.)会被多次调用进行一些必要的处理,如:判断APK路径是否合法,把Apk复制过来,
检查签名和包名是否合法等等。最终在某个消息中会调用到,processPendingInstall。
Step 3.PackageManagerService.processPendingInstall
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
......
mHandler.post(new Runnable() {
......
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);synchronized (mInstallLock) {installPackageLI(args, res);}args.doPostInstall(res.returnCode, res.uid);}......
});
}
此函数被异步调用,一直在等待安装的flag变为
INSTALL_SUCCEEDED。最后调用
installPackageLI开始真正的安装。
Step 4.PackageManagerService.installPackageLI
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
......
PackageParser pp = new PackageParser();
......
final PackageParser.Package pkg;
try {//此处的解析和前面所说的解析系统应用的刘晨个相同,注意一下某些参数和标志位的区别即可。pkg = pp.parsePackage(tmpPackageFile, parseFlags);} catch (PackageParserException e) {return;}......if (replace) {replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,installerPackageName, volumeUuid, res);} else {//第一次安装apk时,会走到这里。installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,args.user, installerPackageName, volumeUuid, res);}
}
此函数主要是通过
parsePackage解析apk的manifest的一些属性。然后调用
installNewPackageLI方法。
Step 5.PackageManagerService.installNewPackageLI
private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res) {
......
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
.......
}
此函数通过
scanPackageLI进行进一步的解析,此处解析和解析系统应用的Step 6相同,此处不再重复分析。通过它完成
了对apk的解析,最终在调用
updateSettingsLI来实现对此应用权限相关的默认设置。
Step 6.
PackageManagerService.updateSettingsLI
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
UserHandle user) {
......
updatePermissionsLPw(newPackage.packageName, newPackage, // zy updatePermissionsLPw??UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0? UPDATE_PERMISSIONS_ALL : 0));...
}
函数调用
updatePermissionsLPw来实现权限的默认设置,和解析系统应用的Step 8~9功能相同的。
至此就完成了普通的apk的安装解析和设置权限的流程,可以看出核心功能和解析系统的并无两样。综上可以看到对于M基线的
权限设置,核心是不区分系统应用和普通应用的,只在一些开始解析时传入的flag上有些区别。也就是说在安装解析完后,默认
的对于targetversion<23的应用,所有的基本和危险权限默认允许,对于
targetversion=23的,所有的安装权限默认允许,危险权
限全部默认拒绝。至于系统的一些应用默认就允许一些权限,是解析完后转门对应用的权限进行了设置(Step11)。
4、权限PermissionsState的机制分析。
在解析系统应用的Step 9中我们,我们只是分析道调用
grantInstallPermission,没有具体的跟踪再往下的逻辑。还有
hasRuntimePermission的实现等。
PermissionsState基本囊括了一个应用的所有权限的状态。
public final class PermissionsState {
......
public ArrayMap<String, PermissionData> mPermissions;//key :权限名字,value :
PermissionData 对单个权限的封装
......
public PermissionsState() {
/* do nothing */ //空的构造,在前面解析系统应用的Step 6中 pkgSetting = mSettings.getPackageLPw(...)//内部就调用啦这个构造函数,这就导致成员变量 mPermissions==null.}
public PermissionsState(PermissionsState prototype) {copyFrom(prototype);//从另一个 PermissionsState获取数据,此时一般 mPermissions都不是null。//此处就不详细分析这个方,就是简单的获取一下。}//查看是否有这个权限,此时的有不仅仅是权限列表有,还需要已经允许(包含运行时和安装时通过 userId区分 )public boolean hasPermission(String name, int userId) {//如果mPermissions==null,正好对应到系统解析的Step 9中,刚空构造初始化完毕,为允许任何权限的时候。if (mPermissions == null) {return false;}
PermissionData permissionData = mPermissions.get(name);//只有当 PermissionData != null, 并且权限已经允许时才返回true。return permissionData != null && permissionData.isGranted(userId);}
private int grantPermission(BasePermission permission, int userId) {//如果已经允许这个权限了,那么就没必要再允许一次了。if (hasPermission(permission.name, userId)) {return PERMISSION_OPERATION_FAILURE;}//此处不太懂可能是要和Linux中的gid是相对应。???final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;//下面这个方法很关键,用来实例化 PermissionData 的.PermissionData permissionData = ensurePermissionData(permission);
//然后通过调用 permissionData 的grant方法来完成权限的允许过程。if (!permissionData.grant(userId)) {return PERMISSION_OPERATION_FAILURE;}
if (hasGids) {final int[] newGids = computeGids(userId);if (oldGids.length != newGids.length) {return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;}}
return PERMISSION_OPERATION_SUCCESS;}
private PermissionData ensurePermissionData(BasePermission permission) {if (mPermissions == null) {mPermissions = new ArrayMap<>();//当第一次到这 mPermissions 为null时,初始化一个}//第一次获取某个权限时必定为null。PermissionData permissionData = mPermissions.get(permission.name);if (permissionData == null) {permissionData = new PermissionData(permission);//实例化 PermissionData。mPermissions.put(permission.name, permissionData);//添加到 mPermissions当中。}return permissionData;}private int revokePermission(BasePermission permission, int userId) {//如果已经拒绝了就没必要操作了。if (!hasPermission(permission.name, userId)) {return PERMISSION_OPERATION_FAILURE;}......PermissionData permissionData = mPermissions.get(permission.name);//调用 permissionData revoke方法来完成拒绝的操作if (!permissionData.revoke(userId)) {return PERMISSION_OPERATION_FAILURE;}......return PERMISSION_OPERATION_SUCCESS;}......
private static final class PermissionData {
private final BasePermission mPerm; //一个权限对应一个 BasePermission// mUserStates ,由于一个权限对应一个PermissionData,所以此处其实 mUserStates 的大小只为1,key = userid,//Value = PermissionState.private SparseArray<PermissionState> mUserStates = new SparseArray<>();
public PermissionData(BasePermission perm) {mPerm = perm;}
public PermissionData(PermissionData other) {this(other.mPerm);......//此处不做分析}......//此处仅仅写出了重要的方法,且以允许权限为例。public boolean grant(int userId) {
...... //前面进行一些判断,是否有必要进行接下来的允许工作PermissionState userState = mUserStates.get(userId);//第一次获取的时候必然为nullif (userState == null) {//此处new一个单个权限对应的 PermissionState,以权限的名字为参数。
userState = new PermissionState(mPerm.name);mUserStates.put(userId, userState);//然后放到成员变量 mUserStates中。以userid为key。(反正只有一个)}
userState.mGranted = true; //最后改变这个成员变量来标记是否允许。return true;}......public boolean revoke(int userId) {...... //也是一些有没有必要继续执行的判断,应该是加快工作效率的吧
PermissionState userState = mUserStates.get(userId);userState.mGranted = false;//直接把变量 mGranted 至为falseif (userState.isDefault()) {//如果默认就是ifalse,那么移除这个mUserStates.remove(userId);}return true;}}//单一的某个权限,对它的封装,此处封装的是名字和,状态。public static final class PermissionState {private final String mName;private boolean mGranted;private int mFlags;
public PermissionState(String name) {mName = name;}
public PermissionState(PermissionState other) {
mName = other.mName;mGranted = other.mGranted;mFlags = other.mFlags;}
public boolean isGranted() {return mGranted;}
}
}
至此我们分析完PermissionsState类的主要的一些方法和变量。然后对应到我们的解析系统应用的Step 9当中。
当权限是
安装时权限的时候,调用的
permissionsState.grantInstallPermission(bp)。最终会调用到grantPermis
sion的
方法当中。此时首
先判断
hasPermission,由于mPermissions == null,返回的是false。接下来就调用ensure
PermissionData(..)和permission
Data.grant(userId)完成最终的允许状态使,PermissionState.mGranted = =true.
当权限是运行时权限的时候,调用hasRuntimePermission(..)。最终也会调用到hasPermission。由于mPerm
issions == null。最终返回false。进而不会进入判断,也就不能调用grantRuntimePermission。
5、权限的检查、允许和禁止的机制。
权限的检查通过AMS里面的一些列封装,最后到PMS当中。
检查权限的方法主要有以下几种。
@Override
public int checkPermission(String permName, String pkgName, int userId) {//判断用户id是否合法。if (!sUserManager.exists(userId)) {return PackageManager.PERMISSION_DENIED;}
synchronized (mPackages) {final PackageParser.Package p = mPackages.get(pkgName);if (p != null && p.mExtras != null) {final PackageSetting ps = (PackageSetting) p.mExtras;//取出psfinal PermissionsState permissionsState = ps.getPermissionsState();//取出 permissionsStateif (permissionsState.hasPermission(permName, userId)) { //调用 hasPermission,看看是否已经允许。return PackageManager.PERMISSION_GRANTED;}//特殊的权限的处理if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {return PackageManager.PERMISSION_GRANTED;}}}return PackageManager.PERMISSION_DENIED;//以上都不符合,那么默认就是不允许的}
@Overridepublic int checkUidPermission(String permName, int uid) {...... //userid、uid是否合法的判断synchronized (mPackages) {//此处取得的是SettingsBase也就是ps,此文没有具体分析到这一块。Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));if (obj != null) {final SettingBase ps = (SettingBase) obj;final PermissionsState permissionsState = ps.getPermissionsState();if (permissionsState.hasPermission(permName, userId)) { //还是看是否已经允许return PackageManager.PERMISSION_GRANTED;}//特殊权限的处理if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {return PackageManager.PERMISSION_GRANTED;}} else {ArraySet<String> perms = mSystemPermissions.get(uid);if (perms != null) {if (perms.contains(permName)) { //如果是解析来的权限不是自己定义的,那么就允许。return PackageManager.PERMISSION_GRANTED;}if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {return PackageManager.PERMISSION_GRANTED;}}}}
return PackageManager.PERMISSION_DENIED; //默认还是拒绝}
权限允许的方法:
由于安装时权限是在安装的时候就已经允许的,所以此时的允许权限,其实就是允许运行时权限。在解析系统apk的Step 11
也有说明,调用PMS的grantRuntimePermission.
public void grantRuntimePermission(String packageName, String name, final int userId) {
..... //一些权限和userid合法性的检查。
final int uid;
final SettingBase sb;
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
......final BasePermission bp = mSettings.mPermissions.get(name);......uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);sb = (SettingBase) pkg.mExtras;......
final int result = permissionsState.grantRuntimePermission(bp, userId);
...... //此处做完权限允许操作后,会先更具返回的result经行一些处理。最终再把这些操作信息
//持久化到文件中在/data/system/users/X/runtime-permissions.xml当中。此处在说另外几个xml
//packages.xml 、package.list 都存放在data/system/下面,文件里存放了所有apk的包的信息,如:包名
}
}
grantRuntimePermission最终也会调用到
permissionsState 的
grantPermission方法中。基本流程和上述一样。
权限禁止的方法。
此处也是指禁止运行时的权限,因为安装时的权限是不可控制的。安装时就已经允许。
@Override
public void revokeRuntimePermission(String packageName, String name, int userId) {
.......//前面的这些操作和允许运行时权限调用的类似,不再做分析
if (permissionsState.revokeRuntimePermission(bp, userId) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
return;
}
......
}
最终就是调用PermissonState的revoke方法把mGranted变量置为false。
6、解析安装APK时和去学奶奶相关的一些重要的类的成员变量。
Settings类的成员变量:
final ArrayMap<String, BasePermission> mPermissions =
new ArrayMap<String, BasePermission>():每个权限字符串对应一
个BasePermission类。
final ArrayMap<String, PackageSetting> mPackages =
new ArrayMap<String, PackageSetting>():每一个包名对应一个
Packag
eSetting类。
PackageManagerService的成员变量:
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>():
每一个包名
对应一个PackageParser.Package对象。(很关键)
private static final int GRANT_DENIED = 1;
private static final int GRANT_INSTALL = 2;
private static final int GRANT_INSTALL_LEGACY = 3;
private static final int GRANT_RUNTIME = 4;
private static final int GRANT_UPGRADE = 5;
./system/core/include/private/android_filesystem_config.h :uid字符串和linux中的uid的对应。
7、重启手机后走的流程(不是第一次恢复出厂设置的加载。)
a、关机重启后应用安装问题
如果彻底关机之后再开机,那么系统就会重新安装一遍所有的应用程序的,因为关机之后,我们是可以改变系统中的应用
程序的,
例如,增加、删除或者修改系统中的应用程序。如果不重新检查一遍的话,那么就会有问题了。在实际使用中,我们
很少会彻底地关
机,一
般意义上的关机只是让系统深度睡眠,这种情况不会导致系统重新安装一遍系统中的应用程序。
系统除
了会把应用程序的安装
信息保存在内存中之外,还会保存在一个本地文件中,因为有些应用程序的安装信息无论安装多少次,
都是必
须保持一致的,例如,
应用程序的Linux用户ID。如果不保存下来的话,那么每次应用程序重新安装之后,表现可能都
会不一致。
/data/system/packages.xml里面 会在构造中调用Settings类的
readLPw进行读取。其实就是会重新解析,但是有些
属性配置不会变,会重新重xml里面读取。也会在addPackageLPw中new PackageSetting
b、shared UID相关问题
假设程序A要与程序B共享一个UID,那么程序B不需要配置Shared UID,它会获得一个普
通的UID,需要配置Shared UID
的是程序A,这时候系统会将程序B的UID分配给程序A,这样就达到了共享UID的目的。
两个程序有相同的UID,并不意味着
它们会运行在同一个进程中。一个程序运行在哪一个进程,一般是由它的Package名称和UID来决定的
,也就是说,UID相同
但是Package名称不同的两个程序是运行两个不同的进程中的。给每一个程序都分配一个UID是用来控制权限的,因此,两
个程序具有相同的UID,就意味它们有相同的权限,可以进行资源共享。关于进程的创建可以看连接: