前言:
帶著問(wèn)題研究問(wèn)題,如下分析就是按照這種思路進(jìn)行的分析。
背景:
運(yùn)行時(shí)權(quán)限是Android 6.0 變更中尤為突出的一點(diǎn)。6以下的手機(jī)在安裝時(shí)候提示權(quán)限列表,用戶全部同意后才能安裝程序;6以后的手機(jī)直接安裝,在運(yùn)行過(guò)程中動(dòng)態(tài)的向用戶申請(qǐng)所需權(quán)限,另外用戶可以在設(shè)置界面對(duì)程序的各個(gè)權(quán)限進(jìn)行管理。
Android 中的權(quán)限可以分為三類:
普通權(quán)限(Normal Permissions)
危險(xiǎn)權(quán)限(Dangerous Permissions)
特殊權(quán)限(Special Permissions)
普通權(quán)限不涉及用戶隱私,在AndroidManifest.xml中聲明即獲??;危險(xiǎn)權(quán)限涉及用戶的隱私,在用戶授權(quán)之后方能使用。另外,Google 對(duì)危險(xiǎn)權(quán)限進(jìn)行了分組,當(dāng)某一個(gè)組中的某一個(gè)權(quán)限被授權(quán)之后應(yīng)用同時(shí)就獲取到了整個(gè)組的所有權(quán)限。而且,調(diào)用 API 申請(qǐng)權(quán)限時(shí)系統(tǒng)將向用戶顯示一個(gè)標(biāo)準(zhǔn)對(duì)話框,該對(duì)話框上的提示權(quán)限說(shuō)明是針對(duì)整個(gè)組的說(shuō)明,應(yīng)用無(wú)法配置或更改此對(duì)話框。
問(wèn)題:
1.怎么默認(rèn)就給予了apk相應(yīng)的權(quán)限?
案例一:怎么默認(rèn)就給予了apk相應(yīng)的權(quán)限?
條件:
Android 8.1系統(tǒng)源碼分析
過(guò)程:
我們了解到PackageManagerService(簡(jiǎn)稱:PMS)管理著所有跟package相關(guān)的工作,例如安裝、卸載應(yīng)用等,所以,應(yīng)該大概從PMS入手。
我們系統(tǒng)開(kāi)機(jī)的過(guò)程,init進(jìn)程拉起Zygote進(jìn)程,Zygote進(jìn)程拉起了system_server進(jìn)程,而system_server初始化Binder通信服務(wù),這里包含PMS。
代碼:
framework/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
······
startBootstrapServices();
/// M: For mtk systemserver
sMtkSystemServerIns.startMtkBootstrapServices();
startCoreServices();
/// M: for mtk other service.
sMtkSystemServerIns.startMtkCoreServices();
startOtherServices();
······
}
private void startBootstrapServices() {
······
traceBeginAndSlog("StartPackageManagerService");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
······
}
private void startOtherServices() {
······
traceBeginAndSlog("MakePackageManagerServiceReady");
try {
mPackageManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
traceEnd();
······
}
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void systemReady() {
······
// If we upgraded grant all default permissions before kicking off.
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
······
}
frameworks/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
public void grantDefaultPermissions(int userId) {
**// 方案一:只要強(qiáng)制把FEATURE_EMBEDDED 打開(kāi),這樣默認(rèn)授權(quán)了所有apk的權(quán)限**
if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
grantAllRuntimePermissions(userId);
} else {
// 系統(tǒng)組件和priv-app下面的默認(rèn)授權(quán)
grantPermissionsToSysComponentsAndPrivApps(userId);
//系統(tǒng)特殊處理的apk
grantDefaultSystemHandlerPermissions(userId);
grantDefaultPermissionExceptions(userId);
}
}
方案二:
第一部分:
// 1.isSysComponentOrPersistentPlatformSignedPrivAppLPr
// a.priv-app b.apk為persist屬性 c.apk簽名一致
// 2.doesPackageSupportRuntimePermission
// apk的版本大于LOLLIPOP_MR1
// 3.pkg.requestedPermissions.isEmpty()
// 沒(méi)有權(quán)限的apk
private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
Log.i(TAG, "Granting permissions to platform components for user " + userId);
synchronized (mService.mPackages) {
for (PackageParser.Package pkg : mService.mPackages.values()) {
if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
|| !doesPackageSupportRuntimePermissions(pkg)
|| pkg.requestedPermissions.isEmpty()) {
continue;
}
grantRuntimePermissionsForPackageLocked(userId, pkg);
}
}
}
private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
return true;
}
if (!pkg.isPrivilegedApp()) {//priv-app
return false;
}
PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
if (sysPkg != null && sysPkg.pkg != null) {//特別說(shuō)明,app的AndroidManifest.xml中application可以設(shè)置persist屬性
if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;
}
} else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {//persistent app
return false;
}
return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;//apk簽名一致
}
第二部分:
private void grantDefaultSystemHandlerPermissions(int userId) {
······
//可以參考方法內(nèi)部對(duì)各種apk的權(quán)限處理
//或者直接參考grantAllRuntimePermissions(userId);中對(duì)apk的權(quán)限處理
······
}
第三部分:
private void grantDefaultPermissionExceptions(int userId) {
······
if (mGrantExceptions == null) {
//讀取的是system/etc/default-permissions下面的xml文件
//也就是說(shuō),我們只要把想要獲取動(dòng)態(tài)權(quán)限的apk,按照既定格式
//放到此目錄下面,也是可以讓apk默認(rèn)給予權(quán)限的
mGrantExceptions = readDefaultPermissionExceptionsLPw();
}
······
for (int i = 0; i < exceptionCount; i++) {
String packageName = mGrantExceptions.keyAt(i);
//從這里繼續(xù)研究跟蹤下去可知:這是system app條件
PackageParser.Package pkg = getSystemPackageLPr(packageName);
······
}
······
}
詳細(xì)分析如下:
private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
readDefaultPermissionExceptionsLPw() {
File[] files = getDefaultPermissionFiles();//獲取的目錄
·······
try (
InputStream str = new BufferedInputStream(new FileInputStream(file))
) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(str, null);
parse(parser, grantExceptions);//既定格式
} catch (XmlPullParserException | IOException e) {
Slog.w(TAG, "Error reading default permissions file " + file, e);
}
······
}
private File[] getDefaultPermissionFiles() {
ArrayList<File> ret = new ArrayList<File>();
File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
if (dir.isDirectory() && dir.canRead()) {
Collections.addAll(ret, dir.listFiles());
}
dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
if (dir.isDirectory() && dir.canRead()) {
Collections.addAll(ret, dir.listFiles());
}
return ret.isEmpty() ? null : ret.toArray(new File[0]);
}
private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
outGrantExceptions) throws IOException, XmlPullParserException {
//簡(jiǎn)單說(shuō)明格式
//<exceptions>
//<exception name="com.qiyi.video.speaker">
//<item name="android.permission.READ_PHONE_STATE" fixed="true" />
//<item name="android.permission.WRITE_EXTERNAL_STORAGE" fixed="true" />
//</exception>
//</exceptions>
//重點(diǎn)說(shuō)明:fixed的意思:如果為true代表其狀態(tài)跟誰(shuí)系統(tǒng)狀態(tài)而定,如果為false,代表強(qiáng)制默認(rèn)打開(kāi)
//介紹處:PackageManager.FLAG_PERMISSION_SYSTEM_FIXED/PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
}
private PackageParser.Package getSystemPackageLPr(String packageName) {
PackageParser.Package pkg = getPackageLPr(packageName);
if (pkg != null && pkg.isSystemApp()) {//代表滿足isSystemApp的apk都是可以這么做,例如:system/app system/priv-app vendor/app vendor/priv-app
return !isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg) ? pkg : null;
}
return null;
}
總結(jié):系統(tǒng)默認(rèn)了3種實(shí)現(xiàn)方式默認(rèn)給apk授權(quán)權(quán)限
補(bǔ)充說(shuō)明:
問(wèn)題點(diǎn):不管是系統(tǒng)默認(rèn)操作還是我們手動(dòng)操作,最后的動(dòng)態(tài)權(quán)限是否保存在文件中?
關(guān)鍵部分:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void updatePermissionFlags(String name, String packageName, int flagMask,
int flagValues, int userId) {
······
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
······
}
frameworks/base/services/core/java/com/android/server/pm/Settings.java
public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
if (sync) {
mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
} else {
mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
}
}
public void writePermissionsForUserAsyncLPr(int userId) {
······
if (mWriteScheduled.get(userId)) {
······
Message message = mHandler.obtainMessage(userId);
mHandler.sendMessageDelayed(message, writeDelayMillis);
} else {
mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
Message message = mHandler.obtainMessage(userId);
mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
mWriteScheduled.put(userId, true);
}
}
private final class MyHandler extends Handler {
public MyHandler() {
super(BackgroundThread.getHandler().getLooper());
}
@Override
public void handleMessage(Message message) {
final int userId = message.what;
Runnable callback = (Runnable) message.obj;
writePermissionsSync(userId);
if (callback != null) {
callback.run();
}
}
}
private void writePermissionsSync(int userId) {
//寫入的是這個(gè)文件:/data/system/users/0/runtime-permissions.xml
}
案例二:檢查權(quán)限和默認(rèn)授予權(quán)限是怎么關(guān)聯(lián)起來(lái)的?
關(guān)鍵點(diǎn):
Context.checkSelfPermission
Activity.requestPermissions
Activity.onRequestPermissionsResult
Activity.shouldShowRequestPermissionRationale
從Activity.requestPermissions入手分析
Activity.java -->AMS-->PackageInstaller(GrantPermissionsActivity.java)-->PMS:updatePermissionFlags
關(guān)鍵代碼:
PackageManagerService.java
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
private PackageInfo getPackageInfoInternal(String packageName, int versionCode,
int flags, int filterCallingUid, int userId) {
//從這段方法可以知道,最終的數(shù)據(jù)來(lái)自mSettings
//因?yàn)榛旧螾ackageSetting來(lái)自mSettings
//所以,初始化mSettings很重要
//這里涉及PMS構(gòu)造方法中的這句語(yǔ)句:mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
//把mSettings的各種權(quán)限狀態(tài)都初始化了,特別注意boolean readLPw(@NonNull List<UserInfo> users)中的
//mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);這是runtime權(quán)限初始化的地方
·····
}
private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
//重點(diǎn)在PermissionsState
//這里的初始化的動(dòng)作都在PMS中
······
final PermissionsState permissionsState = ps.getPermissionsState();
// Compute GIDs only if requested
final int[] gids = (flags & PackageManager.GET_GIDS) == 0
? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
// Compute granted permissions only if package has requested permissions
final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
······
PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
······
}
從Context.checkSelfPermission入手分析
ContextImpl.java
public int checkSelfPermission(String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return checkPermission(permission, Process.myPid(), Process.myUid());
}
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
final IActivityManager am = ActivityManager.getService();
if (am == null) {
// Well this is super awkward; we somehow don't have an active
// ActivityManager instance. If we're testing a root or system
// UID, then they totally have whatever permission this is.
final int appId = UserHandle.getAppId(uid);
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
return PackageManager.PERMISSION_GRANTED;
}
}
try {
return am.checkPermission(permission, pid, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
ActivityManagerService.java
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
ActivityManager.java
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
·······
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
}
PackageManagerService.java
public int checkUidPermission(String permName, int uid){
//關(guān)鍵點(diǎn):mSettings、PermissionsState
``````
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
``````
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
BasePermission bp = mSettings.mPermissions.get(permName);
if (bp != null && bp.isInstant()) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
return PackageManager.PERMISSION_GRANTED;
}
}
}
注:
Tips
a.權(quán)限涉及到的模塊AMS、PMS、PackageInstaller(安裝器)。動(dòng)態(tài)權(quán)限的對(duì)話框也是安裝器展示
b.PackageManager的實(shí)現(xiàn)類為ApplicationPackageManager
adb快速查看
查看系統(tǒng)所擁有的危險(xiǎn)權(quán)限
adb shell pm list permissions -d -g 查看危險(xiǎn)權(quán)限指令
查看某一個(gè)apk所擁有的權(quán)限
adb shell pm dump 包名 | findstr permission
例如:adb shell pm dump com.smart.launcher | findstr permission
參考學(xué)習(xí)
https://blog.csdn.net/sinat_20059415/article/details/80369872
https://blog.csdn.net/zchy198799/article/details/89341974
https://blog.csdn.net/u013553529/article/details/53167072
https://blog.csdn.net/csdnxialei/article/details/93628890