Android Permissions
寫這篇blog主要是源於一個GTS case: testDefaultGrants, 這個case的意圖是測試系統裡預設granted的runtime permissions.
轉載請標明來處:http://www.jianshu.com/p/ffd583f720f4
在 Android M以後, Android加入了runtime permissions, 也就是dangerous permissons, 這些許可權有可能會刺探使用者隱私等等危害。 這樣系統在安裝APP的時候就不會預設grant runtime permissions.
在Android M之前, Runtime permissions是直接被當作是install permissons,即在安裝的時候就直接grant了。
而針對一些系統內建的app,OEM vendor可以修改程式碼來預設grant runtime permissions.
所以Google的這個GTS case 就是檢查是否OEM產商隨意的grant runtime permission.
一、 系統配置許可權的初始化
SystemConfig systemConfig = SystemConfig.getInstance();
該配置的初始化主要是從系統配置檔案中讀取自定義的許可權, 包括 /etc/sysconfig /etc/permissions /oem/etc/sysconfig /oem/etc/permissions裡所有的xml檔案。
下圖是讀出來的permission相關的UML圖為
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
// Propagate permission configuration in to package manager.
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) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
以上這些程式碼片段主要目的是將SystemConfig裡解析出來的值取出來賦值給PackageManagerService裡的變數mSettings,在這個階段解析出來的permission的sourcePackage全部命名為 android 因為這是系統裡定義的permission.
二、 讀取系統裡的package.xml
2.1 mSettings.readLPw(this, …)
解析/data/system/package.xml
2.1.1 readPermissionsLPw
if (tagName.equals("permissions")) {
readPermissionsLPw(mPermissions, parser);
在package.xml裡permissions放在前面,所以這裡先讀的是permissions section, permissions欄位,儲存到mSettings.mPermissions,permissions是所有apk自定義的permissions
2.1.2 readPackageLPw
if (tagName.equals("package")) {
readPackageLPw(parser); //解析 package section
void readPackageLPw(XmlPullParser parser)
packageSetting = addPackageLPw(name.intern(), realName, …)
//生成該package對應的 PackageSetting, 然後根據xml裡值進行填充
readInstallPermissionsLPr(parser, packageSetting.getPermissionsState());
//讀取安裝許可權
void readInstallPermissionsLPr(XmlPullParser parser,
PermissionsState permissionsState)
//解析package section裡的 <perms>中當前package裡所有的許可權
String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
final boolean granted = grantedStr == null
|| Boolean.parseBoolean(grantedStr);
if (granted) {
permissionsState.grantInstallPermission(bp) //grant安裝許可權
2.1.3 grantInstallPermission
這些安裝許可權是apk在安裝時自動grant的,都是normal的等級,不是dangeous許可權。
該函式的主要作用是
- 生成permission對應的PermissionData,並用加入到PermissionsState mPermissions裡
- 對使用者id,grant許可權,即生成PermissionState物件,並用mUserStates來track.
2.2 從/data/system/users/x/runtime-permissions.xml 讀取runtime許可權, 並grant
for (UserInfo user : users) {
mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
readStateForUserSyncLPr->parseRuntimePermissionsLPr
parseRuntimePermissionsLPr()->parsePermissionsLPr
//解析 <pkg> 下的每一個item得到每一個許可權,然後進行grant
if (granted) {
permissionsState.grantRuntimePermission(bp, userId); //grant runtime許可權
permissionsState.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS, flags);
}
如下面這個package.xml版本,摘自/data/system/package.xml
<package name="com.android.tv.settings" codePath="/system/priv-app/TvSettings" nativeLibraryPath="/system/priv-app/TvSettings/lib" primaryCpuAbi="arm64-v8a" publicFlags="944291397" privateFlags="8" ft="1539c6151e8" it="1539c6151e8" ut="1539c6151e8" version="1" sharedUserId="1000">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
<item name="android.permission.INSTALL_LOCATION_PROVIDER" granted="true" flags="0" />
注意:如果是系統第一次開機的時候,系統裡是沒有package.xml的,那麼將不會生成package對應的PackageSetting, 在這種情況下,PackageSetting會在掃描apk檔案時進行生成.
三、 掃描系統apk檔案,解析apk檔案
scanDirLI -> scanPackageLI(file, …) ->
pkg = pp.parsePackage(scanFile, parseFlags); //解析apk檔案
parseClusterPackage
pkg = parseBaseApk(baseApk, assets, flags);
PackageParser.Package pkg = parseBaseApk(res, …) //解析apk中的AndroidManifest.xml
其中PackageParser.Package代表一個解析出來的apk, 而PackageParser指的是一個解析類,
scanPackageLI(pkg, xxx) -> scanPackageDirtyLI(pkg, …)
該步驟
- 與Permission相關的的操作主要是解析pkg即PackageParser.Package裡的欄位,將apk相關的資訊儲存到PackageManagerService裡或mSettings裡.
- 根據pkg裡解析出來的資訊生成PackageSetting
pkgSetting = mSettings.getPackageLPw(pkg, …)
PackageSetting p = getPackageLPw(name, …)
//如果系統第一次開機啟動,那麼從Setting裡是拿不到PackageSetting的,這時只能新生成一個,
//並將它通過addPackageSettingLPw加入到mSettings.mPackages裡;
//如果不是第一次開機,那麼將會直接從Settings.mPackages裡取
四、更新permissions.
分為兩種情況
- 在讀取package.xml的時候已經grant了
- 第一次開機,updatePermissionsLPw 會 grant相應的許可權,
updatePermissionsLPw(null, null, updateFlags)->grantPermissionsLPw(pkg, …)
//為每個package grant所請求的permissions
void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) { return; }
//獲得package的PackageSettings
PermissionsState permissionsState = ps.getPermissionsState();
//獲得package的PermissionState類
PermissionsState origPermissions = permissionsState;
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) { //遍歷當前所有請求的permissions,即<uses-permission>
final String name = pkg.requestedPermissions.get(i);
//獲得permisison name
final BasePermission bp = mSettings.mPermissions.get(name);
//根據permission name獲得permission所表示的結構
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
//獲得permission的等級
switch (level) {
case PermissionInfo.PROTECTION_NORMAL: {grant = GRANT_INSTALL}
case PermissionInfo.PROTECTION_DANGEROUS: {grant = GRANT_RUNTIME;}
case PermissionInfo.PROTECTION_SIGNATURE: {}
}
switch (grant) {
case GRANT_INSTALL: {
permissionsState.grantInstallPermission(bp)
//為所有使用者頒佈安裝permission,即生成permission對應的PermissionState
//並用PermissionData的mUserStates來track
//其實這裡grant install的操作在之前 readLPw就已經執行過了,這裡沒什麼實際用處,主要是對sharedUser 進行一些update ?????
case GRANT_RUNTIME: {
}
}
五、grant預設的runtime 許可權
mPackageManagerService.systemReady();
int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
grantPermissionsUserIds = ArrayUtils.appendInt(
grantPermissionsUserIds, userId);
}
}
// 如果是系統第一次啟動,即沒有runtime-permissions.xml, 那麼系統會進入grant default permission的階段.
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
為系統級的app grant預設runtime許可權
public void grantDefaultPermissions(int userId) {
grantPermissionsToSysComponentsAndPrivApps(userId);
grantDefaultSystemHandlerPermissions(userId);
}
void grantPermissionsToSysComponentsAndPrivApps(int userId) {
for (PackageParser.Package pkg : mService.mPackages.values()) {
if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
|| !doesPackageSupportRuntimePermissions(pkg)
|| pkg.requestedPermissions.isEmpty()) {
/*
過濾出特定的apk,
1, sdk version >= 23的
2, platform簽名
3, persistent privilege App進行 grant runtime許可權
*/
continue;
}
Set<String> permissions = new ArraySet<>();
final int permissionCount = pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
String permission = pkg.requestedPermissions.get(i);
BasePermission bp = mService.mSettings.mPermissions.get(permission);
if (bp != null && bp.isRuntime()) {
permissions.add(permission);
}
}
if (!permissions.isEmpty()) {
grantRuntimePermissionsLPw(pkg, permissions, true, userId);
}
void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions, boolean systemFixed, boolean overrideUserChoice, int userId) {
mService.grantRuntimePermission(pkg.packageName, permission, userId);
mService.updatePermissionFlags (permission, pkg.packageName,
newFlags, newFlags, userId);
}
void grantRuntimePermission(String packageName, String name, final int userId) {
int result = permissionsState.grantRuntimePermission(bp, userId);
mOnPermissionChangeListeners.onPermissionsChanged(uid);
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
為系統預設的元件grant 預設許可權
void grantDefaultSystemHandlerPermissions(int userId) {
// Camera
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackageLPr(
cameraIntent, userId);
if (cameraPackage != null
&& doesPackageSupportRuntimePermissions(cameraPackage)) {
grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
}
//獲得預設的camera package,然後grant給該package camera/microhphone/storage許可權
//將/vendor/etc/permissions中的package.xml裡定義的permissions grant給這個package,
//注意:這個feature是OEM自己加的,不在AOSP裡.
grantDefaultPermissionsForProduct(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions"), userId);
}
private void grantDefaultPermissionsForProduct(File path, int userId) {
…
for (File f: path.listFiles()) {
grantDefaultPermissionsFromXml(f, userId);
}
}
六、 安裝時grant install permissions
installPackageAsUser(pm install) -> INIT_COPY -> MCS_BOUND -> startCopy
(InstallParams) -> handleReturnCode -> processPendingInstall -> installPackageLI
void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, parseFlags);
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, volumeUuid, res);
}
}
void installNewPackageLI(PackageParser.Package pkg, … ) {
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
}
//更新Settings
void updateSettingsLI(PackageParser.Package newPackage, …) {
//在這裡更新permissions, grant install的許可權(包括normal, signature的permission)
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
//並寫入到packages.xml裡
mSettings.writeLPr();
}
七、Shared User ID 的許可權
在packages.xml 和 runtime-permissions.xml裡有一節是 <shared-user>, 表示的是這個UID所獲得的許可權,
Eg, android.uid.system的install許可權. (來自packages.xml)
<shared-user name="android.uid.system" userId="1000">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
<item name="android.permission.INSTALL_LOCATION_PROVIDER" granted="true" flags="0" />
<item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
<item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
<item name="android.permission.GET_TOP_ACTIVITY_INFO" granted="true" flags="0" />
</shared-user>
android.uid.system的runtime許可權. (來自runtime-permissions.xml)
<shared-user name="android.uid.system">
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="30" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="30" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="30" />
<item name="android.permission.CAMERA" granted="true" flags="30" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="30" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="30" />
<item name="android.permission.RECORD_AUDIO" granted="true" flags="30" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
</shared-user>
在packages.xml或runtime-permissions.xml裡會發現如果擁有相同uid的package,它們的grant 許可權和UID在shared-user裡獲得的許可權是一樣的。(因為擁有相同UID, 且簽名一樣的話,他們可以彼此訪問資料,理所當然應該獲得所有的相同的許可權)。
Shared userId的類圖,如下
如圖6可知, SharedUserSettings繼承於SettingBase, 因此也就有PermissionsState 許可權狀態類,接下來看看android是怎樣grant許可權給SharedUserSettings的。
在解析每個apk檔案後,通過以下程式碼,將sharedUser與package建立連線了。
mSettings.insertPackageSettingLPw(pkgSetting, pkg); -> addPackageSettingLPw(p, pkg.packageName, p.sharedUser);
p.pkg = pkg;
sharedUser.addPackage(p);
//加入到SharedUserSetting裡的packages裡,這樣就把SharedUserSetting就track了package.
p.sharedUser = sharedUser;
p.appId = sharedUser.userId;
那麼程式碼是如何是保證擁有一樣的UID具有相同的許可權呢?所有的install, signature, dangeous許可權都是由該函式grant的,
void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
String packageOfInterest) {
PermissionsState permissionsState = ps.getPermissionsState();
}
public PermissionsState getPermissionsState() {
return (sharedUser != null)
? sharedUser.getPermissionsState()
: super.getPermissionsState();
}
由 getPermissionsState可以看出,在獲得 PermissionsState的時候先找sharedUser,即Shared User Id的PermissionsState, 如果沒有設定shared uid才用package自己的,所以可以看出來, UID的PermissionsState是所有在AndroidManifest.xml設定了sharedUserId裡的permission的一個合集,且他們permission也是一樣的。
八、 結論
- 系統自定義的許可權以及一直feature, 可以直接定義在xml(/etc/permissions下面)配置檔案裡.
- 一個apk在安裝的時候已經將非執行時許可權直接grant給裝置上所有的使用者,而執行時許可權是跟許可權直接相關的.
- 一個apk可以申請permission, 即用<uses-permission />表示,也可以自定義許可權,如systemserver相關的許可權大多是在 framework-res.apk裡定義的。
- 具體相同的sharedUserId的package擁有相同的permission, 程式碼中使用的是Shared UID的permissions, 且該permissions是所有sharedUserId的合集。
九、參考
相關文章
- List of Android System PermissionsAndroid
- Android Wear-Detecting Location on Android Wear,Requesting Permissions on Android WearAndroid
- adb devices: no permissionsdev
- 國產 Android 許可權申請最佳適配方案 —— permissions4mAndroid
- This task rrequires the application to have elevated permissionsUIAPP
- Ubuntu下android手機通過usb連線電腦,顯示"???????????? no permissions"問題UbuntuAndroid
- hadoop 啟動 Permissions for id_rsa are too openHadoop
- You don't have write permissions for the /usr/bin directory.
- [jenkins]Can’t connect to window server – not enough permissionsJenkinsServer
- ERROR: Check /etc/pam.conf file permissions and ownershipError
- Permissions 0755 for '/home/lonecloud/.ssh/id_rsa' are too open.Cloud
- root使用者操作檔案提示 changing permissions of '***': Operation not permittedMIT
- 解決Arch Linux安裝AndroidStudio連結不上Android,執行adb devices出現no permissions的錯誤LinuxAndroiddev
- SQL Server 建立使用者賦權報錯之Permissions at the server scope canSQLServer
- windows ssh遠端登入阿里雲遇到permissions are too open的錯誤Windows阿里
- 解決安卓 linux adb 連線機器提示 no permissions 的問題安卓Linux
- Expo大作戰(三十)–expo sdk api之Permissions(許可權管理模組),Pedometer(計步器api)API
- Expo大作戰(三十)--expo sdk api之Permissions(許可權管理模組),Pedometer(計步器api)API
- Django Error: [WinError 10013] An attempt was made to access a socket in a way forbidden by its access permissionsDjangoErrorORB
- Arkts錄製音訊報錯 ERROR: The reason and usedScene attributes are mandatory for user_grant permissions.音訊Error
- QTVA-2015-198545、WooYun-2015-104148 .NET Framework Arbitrary File Permissions Modify VulQTFramework
- Android學習—— Android佈局Android
- Android之android exported="false"作用AndroidExportFalse
- [Android元件化]Android app BundleAndroid元件化APP
- 聚焦 Android 11: Android 開發者工具Android
- 【Android】Android設計準則Android
- Android Transition(Android過渡動畫)Android動畫
- 愛上Android之初識AndroidAndroid
- [Android]Android原始碼下載Android原始碼
- [android]android命令列截圖Android命令列
- 迴歸Android Focus on Android.Android
- AndroidAndroid
- android:id="@android:id/tabhost" 、android:id="@+id/llRoot" 、android:id="@id/llRoot" 之間的區別Android
- android開發 之 Bolts-AndroidAndroid
- Android總結篇系列:Android ServiceAndroid
- Android APIs (Class Index - Android SDK)(一)AndroidAPIIndex
- Android APIs (Class Index - Android SDK)(二)AndroidAPIIndex
- Android元件---重新認識Android(2)Android元件