Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 6.0 Runtime permission

Android 6.0 Runtime permission

編輯:關於Android編程

Android版本升級到6.0之後,為了一改往日安全受人诟病的形象,將權限授權的安裝時授予的基礎上,對於一部分危險的權限采用動態控制授權的方式。類似國內手機安全助手權限控制的功能。

一、權限控制組:

Permission Group

Permissions

CALENDAR

· READ_CALENDAR

· WRITE_CALENDAR

CAMERA

· CAMERA

CONTACTS

· READ_CONTACTS

· WRITE_CONTACTS

· GET_ACCOUNTS

LOCATION

· ACCESS_FINE_LOCATION

· ACCESS_COARSE_LOCATION

MICROPHONE

· RECORD_AUDIO

PHONE

· READ_PHONE_STATE

· CALL_PHONE

· READ_CALL_LOG

· WRITE_CALL_LOG

· ADD_VOICEMAIL

· USE_SIP

· PROCESS_OUTGOING_CALLS

SENSORS

· BODY_SENSORS

SMS

· SEND_SMS

· RECEIVE_SMS

· READ_SMS

· RECEIVE_WAP_PUSH

· RECEIVE_MMS

STORAGE

· READ_EXTERNAL_STORAGE

· WRITE_EXTERNAL_STORAGE


以上的9組權限在本次修改的范圍之內,可能有人要問了,明明有一些權限更敏感啊,比如修改MODIFY_PHONE_STATE之類的為什麼不控制一下呢?其實這些權限早就控制住了,這些級別的權限是非系統應用不能獲取到的,當然也不需要如此控制。

二、對應用的影響:
增加這一步之後對應用的影響當然就是不能讓你好好玩耍了,當禁用你的某項權限之後,應用調用與此相關的功能和接口時往往會得不到正常的結果,更有甚者會拋出異常。現在的流程都應該改成這樣:
有界面入口的情況,先檢查權限是否授權,若未授予則請求,在請求回調中進行處理。
ctx.checkSelfPermission(p)
requestPermissions(p, REQUEST_PERMISSIONS_CODE);
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {}
沒有界面入口的情況就只能檢查是否有權限授予,有執行正常操作,如果沒有可以選擇彈出界面按以上流程操作也可以直接終止。

三、內部實現:
這裡以定位的Manifest.permission.ACCESS_COARSE_LOCATION權限為例進行說明。以為這個權限如果被禁止比較直觀,它會拋出一個SecurityException異常出來,很容易分析。
先來看看我們定位請求的分發流程。
應用層調用定位調用的是LocationManager.requestLocationUpdates接口。
\

 

 

來看看checkUidPermission方法做了一些什麼:

@Override

public int checkUidPermission(String permName, int uid){

//多用戶檢測

final int userId = UserHandle.getUserId(uid);

if (!sUserManager.exists(userId)) {

returnPackageManager.PERMISSION_DENIED;

}

synchronized (mPackages) {

Object obj =mSettings.getUserIdLPr(UserHandle.getAppId(uid));

if (obj != null) {

final SettingBase ps =(SettingBase) obj;

final PermissionsStatepermissionsState = ps.getPermissionsState();

if (permissionsState.hasPermission(permName,userId)) {

returnPackageManager.PERMISSION_GRANTED;

}

// Special case:ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION

if(Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) &&permissionsState

.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION,userId)) {

returnPackageManager.PERMISSION_GRANTED;

}

} else {

ArraySet perms =mSystemPermissions.get(uid);

if (perms != null) {

if(perms.contains(permName)) {

returnPackageManager.PERMISSION_GRANTED;

}

if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName)&& perms

.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {

returnPackageManager.PERMISSION_GRANTED;

}

}

}

}

returnPackageManager.PERMISSION_DENIED;

}

一眼掃過去,關鍵中關鍵在於mSettings裡面保存的這個SettingBase對象,它記錄了PermissionsState也就是權限的授予情況。先不直接分析,我們從另外一邊來看,看看是如何授權的。

授權有兩個地方,一個是設置裡面的入口,還有一個是申請權限彈框界面的入口,代碼都在PackageInstaller裡面,分別是ManagePermissionsActivity和GrantPermissionsActivity。就不仔細分析了最終授權和撤銷都是在AppPermissionGroup這裡實現的,grantRuntimePermissions和revokeRuntimePermissions兩個方法。最終生效的代碼還是在PackageManagerService裡面。

@Override

public void grantRuntimePermission(String packageName, String name,final int userId) {

if (!sUserManager.exists(userId)) {

Log.e(TAG, "No such user:" + userId);

return;

}

//授予權限是需要GRANT_RUNTIME_PERMISSIONS權限的

mContext.enforceCallingOrSelfPermission(

android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,

"grantRuntimePermission");

enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,

"grantRuntimePermission");

final int uid;

final SettingBase sb;

synchronized (mPackages) {

final PackageParser.Package pkg = mPackages.get(packageName);

if (pkg == null) {

throw newIllegalArgumentException("Unknown package: " + packageName);

}

final BasePermission bp = mSettings.mPermissions.get(name);

if (bp == null) {

throw newIllegalArgumentException("Unknown permission: " + name);

}

enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);

uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);

sb = (SettingBase) pkg.mExtras;

if (sb == null) {

throw newIllegalArgumentException("Unknown package: " + packageName);

}

final PermissionsState permissionsState = sb.getPermissionsState();

final int flags = permissionsState.getPermissionFlags(name, userId);

//fix的權限是不能修改的

if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {

throw newSecurityException("Cannot grant system fixed permission: "

+ name + " forpackage: " + packageName);

}

if (bp.isDevelopment()) {

// Development permissions mustbe handled specially, since they are not

// normal runtimepermissions. For now they apply to allusers.

if(permissionsState.grantInstallPermission(bp) !=

PermissionsState.PERMISSION_OPERATION_FAILURE) {

scheduleWriteSettingsLocked();

}

return;

}

final int result = permissionsState.grantRuntimePermission(bp,userId);

switch (result) {

casePermissionsState.PERMISSION_OPERATION_FAILURE: {

return;

}

casePermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {

final int appId =UserHandle.getAppId(pkg.applicationInfo.uid);

mHandler.post(newRunnable() {

@Override

public void run() {

killUid(appId,userId, KILL_APP_REASON_GIDS_CHANGED);

}

});

} break;

}

mOnPermissionChangeListeners.onPermissionsChanged(uid);

// Not critical if that is lost - app has to request again.

mSettings.writeRuntimePermissionsForUserLPr(userId,false);

}

// Only need to do this if user is initialized. Otherwise it's a newuser

// and there are no processes running as the user yet and there's noneed

// to make an expensive call to remount processes for the changedpermissions.

if (READ_EXTERNAL_STORAGE.equals(name)

||WRITE_EXTERNAL_STORAGE.equals(name)) {

final long token = Binder.clearCallingIdentity();

try {

if(sUserManager.isInitialized(userId)) {

MountServiceInternalmountServiceInternal = LocalServices.getService(

MountServiceInternal.class);

mountServiceInternal.onExternalStoragePolicyChanged(uid, packageName);

}

} finally {

Binder.restoreCallingIdentity(token);

}

}

}





@Override

public void revokeRuntimePermission(String packageName, String name, intuserId) {

if (!sUserManager.exists(userId)) {

Log.e(TAG, "No such user:" + userId);

return;

}

mContext.enforceCallingOrSelfPermission(

android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,

"revokeRuntimePermission");

enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,

"revokeRuntimePermission");

final int appId;

synchronized (mPackages) {

final PackageParser.Package pkg = mPackages.get(packageName);

if (pkg == null) {

throw newIllegalArgumentException("Unknown package: " + packageName);

}

final BasePermission bp = mSettings.mPermissions.get(name);

if (bp == null) {

throw newIllegalArgumentException("Unknown permission: " + name);

}

enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);

SettingBase sb = (SettingBase) pkg.mExtras;

if (sb == null) {

throw newIllegalArgumentException("Unknown package: " + packageName);

}

final PermissionsState permissionsState = sb.getPermissionsState();

final int flags = permissionsState.getPermissionFlags(name, userId);

if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {

throw newSecurityException("Cannot revoke system fixed permission: "

+ name + " forpackage: " + packageName);

}

if (bp.isDevelopment()) {

// Development permissions mustbe handled specially, since they are not

// normal runtimepermissions. For now they apply to allusers.

if(permissionsState.revokeInstallPermission(bp) !=

PermissionsState.PERMISSION_OPERATION_FAILURE) {

scheduleWriteSettingsLocked();

}

return;

}

if (permissionsState.revokeRuntimePermission(bp,userId) ==

PermissionsState.PERMISSION_OPERATION_FAILURE) {

return;

}

mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);

// Critical, after this call app should never have the permission.

mSettings.writeRuntimePermissionsForUserLPr(userId, true);

appId = UserHandle.getAppId(pkg.applicationInfo.uid);

}

killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);

}

大家咋一看這三個方法,是不是對裡面的PermissionsState是不是同一個東西產生懷疑,別想多了,他們就是一個玩意,有興趣的可以看看這個方法Settings.getPackageLPw這個方法,這是在安裝應用掃描的時候scanPackageDirtyLI方法調用的,裡面可以看到Settings類中的mUserIds、mPackages裡面存的value還有PackageManagerService中的mPackages.pkg. mExtras都是同一個玩意奏是個PackageSetting。其實上面說的檢查權限的流程是本來就有的,差異,差異,差異僅在於可以動態修改:也就是修改PermissionState的mGranted值。

最後大家可能在

 

這裡看到讀寫存儲的權限變化還需要另外一個服務(MountServiceInternal)的策略變化,這個以後在分析,先埋伏一下。

 

四、默認授予規則:

默認授予是在PackageManagerService執行systemReady的時候執行的,主要是這個類DefaultPermissionGrantPolicy,名字也一目了然。

public void grantDefaultPermissions(int userId) {

//針對系統組件和Privileged的應用做默認權限的處理

grantPermissionsToSysComponentsAndPrivApps(userId);

//對符合系統處理原則的模塊進行默認權限的處理

grantDefaultSystemHandlerPermissions(userId);

}

private voidgrantPermissionsToSysComponentsAndPrivApps(int userId) {

Log.i(TAG, "Granting permissionsto platform components for user " + userId);

synchronized (mService.mPackages) {

for (PackageParser.Package pkg :mService.mPackages.values()) {

//遍歷所有的package,如果是系統組的,或者是platform簽名的特權應用和persistent應用就可以默認授予

if(!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)

||!doesPackageSupportRuntimePermissions(pkg)

||pkg.requestedPermissions.isEmpty()) {

continue;

}

Set permissions =new ArraySet<>();

final int permissionCount =pkg.requestedPermissions.size();

for (int i = 0; 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);

}

}

}

}

privatebooleanisSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {

//小於10000的系統進程

if(UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {

return true;

}

if(!pkg.isPrivilegedApp()) { //這裡我心存疑惑不確定這一類APP的范圍

return false;

}

//下面是對當前禁用的APP如果不是persistent的就不用授予了

PackageSetting sysPkg =mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);

if(sysPkg != null) {

if((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0){

return false;

}

} else if((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {

return false;

}

//必須還要滿足platform簽名

returnPackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,

pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;

}

private voidgrantRuntimePermissionsLPw(PackageParser.Package pkg, Setpermissions,

boolean systemFixed, booleanoverrideUserChoice, int userId) {

if (pkg.requestedPermissions.isEmpty()){

return;

}

List requestedPermissions= pkg.requestedPermissions;

Set grantablePermissions= null;

if (pkg.isUpdatedSystemApp()) {

PackageSetting sysPs =mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);

if (sysPs != null) {

if(sysPs.pkg.requestedPermissions.isEmpty()) {

return;

}

if(!requestedPermissions.equals(sysPs.pkg.requestedPermissions)) {

grantablePermissions = newArraySet<>(requestedPermissions);

requestedPermissions =sysPs.pkg.requestedPermissions;

}

}

}

final int grantablePermissionCount =requestedPermissions.size();

for (int i = 0; i

String permission =requestedPermissions.get(i);

// If there is a disabled systemapp it may request a permission the updated

// version ot the data partitiondoesn't, In this case skip the permission.

if (grantablePermissions != null&& !grantablePermissions.contains(permission)) {

continue;

}

if(permissions.contains(permission)) {

final int flags =mService.getPermissionFlags(permission, pkg.packageName, userId);

// If any flags are set to thepermission, then it is either set in

// its current state by thesystem or device/profile owner or the user.

// In all these cases we do notwant to clobber the current state.

// Unless the caller wants tooverride user choices. The override is

// to make sure we can grantthe needed permission to the default

// sms and phone apps after theuser chooses this in the UI.

if (flags == 0 ||overrideUserChoice) {

// Never clobber policy orsystem.

final int fixedFlags =PackageManager.FLAG_PERMISSION_SYSTEM_FIXED

|PackageManager.FLAG_PERMISSION_POLICY_FIXED;

if ((flags &fixedFlags) != 0) {

continue;

}

mService.grantRuntimePermission(pkg.packageName, permission, userId);

if (DEBUG) {

Log.i(TAG,"Granted " + permission + " to default handler "

+pkg.packageName);

}

int newFlags =PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;

if (systemFixed) {

newFlags |=PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;

}

mService.updatePermissionFlags(permission, pkg.packageName,

newFlags, newFlags,userId);

}

}

}

}

上面這個授予的方法我就不細說了,大家需要注意的是這裡授予權限的同時會控制一下權限的Flags,這個東西記錄了當前授予的這個運行時權限的狀態,主要有以下幾種,分表代碼什麼含義英文注釋已經夠清晰了不用我翻譯了吧。

/**

* Permission flag: The permission is set inits current state

* by the user and apps can still request itat runtime.

* @hide

*/

public static final intFLAG_PERMISSION_USER_SET = 1 << 0;



/**

* Permission flag: The permission is setin its current state

* by the user and it is fixed, i.e. appscan no longer request

* this permission.

* @hide

*/

public static final intFLAG_PERMISSION_USER_FIXED = 1 <<1;



/**

* Permission flag: The permission is setin its current state

* by device policy and neither apps northe user can change

* its state.

* @hide

*/

public static final intFLAG_PERMISSION_POLICY_FIXED = 1<< 2;



/**

* Permission flag: The permission is setin a granted state but

* access to resources it guards isrestricted by other means to

* enable revoking a permission on legacyapps that do not support

* runtime permissions. If this permissionis upgraded to runtime

* because the app was updated to supportruntime permissions, the

* the permission will be revoked in theupgrade process.

* @hide

*/

public static final intFLAG_PERMISSION_REVOKE_ON_UPGRADE = 1<< 3;



/**

* Permission flag: The permission is setin its current state

* because the app is a component that is apart of the system.

* @hide

*/

public static final intFLAG_PERMISSION_SYSTEM_FIXED = 1<< 4;



/**

* Permission flag: The permission isgranted by default because it

* enables app functionality that isexpected to work out-of-the-box

* for providing a smooth user experience.For example, the phone app

* is expected to have the phonepermission.

* @hide

*/

public static final intFLAG_PERMISSION_GRANTED_BY_DEFAULT = 1<< 5;

在我的有限知識裡,一直對ApplicationInfo.PRIVATE_FLAG_PRIVILEGED這個flag也就是特權應用的范圍不是很清楚,還請知道的朋友指點一二。

下面繼續說符合系統默認規則的一類應用的默認授予情況,下面這個方法:

private voidgrantDefaultSystemHandlerPermissions(int userId) {

Log.i(TAG, "Granting permissions todefault platform handlers for user " + userId);



final PackagesProviderimePackagesProvider;

final PackagesProviderlocationPackagesProvider;

final PackagesProvidervoiceInteractionPackagesProvider;

final PackagesProvidersmsAppPackagesProvider;

final PackagesProviderdialerAppPackagesProvider;

final PackagesProvidersimCallManagerPackagesProvider;

final SyncAdapterPackagesProvidersyncAdapterPackagesProvider;



synchronized (mService.mPackages) {

imePackagesProvider =mImePackagesProvider;

locationPackagesProvider =mLocationPackagesProvider;

voiceInteractionPackagesProvider =mVoiceInteractionPackagesProvider;

smsAppPackagesProvider =mSmsAppPackagesProvider;

dialerAppPackagesProvider =mDialerAppPackagesProvider;

simCallManagerPackagesProvider =mSimCallManagerPackagesProvider;

syncAdapterPackagesProvider =mSyncAdapterPackagesProvider;

}



String[] imePackageNames =(imePackagesProvider != null)

?imePackagesProvider.getPackages(userId) : null;

String[] voiceInteractPackageNames =(voiceInteractionPackagesProvider != null)

?voiceInteractionPackagesProvider.getPackages(userId) : null;

String[] locationPackageNames =(locationPackagesProvider != null)

?locationPackagesProvider.getPackages(userId) : null;

String[] smsAppPackageNames = (smsAppPackagesProvider!= null)

?smsAppPackagesProvider.getPackages(userId) : null;

String[] dialerAppPackageNames =(dialerAppPackagesProvider != null)

?dialerAppPackagesProvider.getPackages(userId) : null;

String[] simCallManagerPackageNames =(simCallManagerPackagesProvider != null)

?simCallManagerPackagesProvider.getPackages(userId) : null;

String[] contactsSyncAdapterPackages =(syncAdapterPackagesProvider != null) ?

syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY,userId) : null;

String[] calendarSyncAdapterPackages =(syncAdapterPackagesProvider != null) ?

syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY,userId) : null;



synchronized (mService.mPackages) {

// Installer

PackageParser.PackageinstallerPackage = getSystemPackageLPr(

mService.mRequiredInstallerPackage);

if (installerPackage != null

&&doesPackageSupportRuntimePermissions(installerPackage)) {

grantRuntimePermissionsLPw(installerPackage, STORAGE_PERMISSIONS, true,userId);

}



// Verifier

PackageParser.PackageverifierPackage = getSystemPackageLPr(

mService.mRequiredVerifierPackage);

if (verifierPackage != null

&&doesPackageSupportRuntimePermissions(verifierPackage)) {

grantRuntimePermissionsLPw(verifierPackage,STORAGE_PERMISSIONS, true, userId);

grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false,userId);

grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false,userId);

}



// SetupWizard

Intent setupIntent = newIntent(Intent.ACTION_MAIN);

setupIntent.addCategory(Intent.CATEGORY_SETUP_WIZARD);

PackageParser.Package setupPackage= getDefaultSystemHandlerActivityPackageLPr(

setupIntent, userId);

if (setupPackage != null

&&doesPackageSupportRuntimePermissions(setupPackage)) {

grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);

grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId);

}



// Camera

Intent cameraIntent = newIntent(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);

}



// Media provider

PackageParser.PackagemediaStorePackage = getDefaultProviderAuthorityPackageLPr(

MediaStore.AUTHORITY,userId);

if (mediaStorePackage != null) {

grantRuntimePermissionsLPw(mediaStorePackage,STORAGE_PERMISSIONS, true, userId);

}



// Downloads provider

PackageParser.PackagedownloadsPackage = getDefaultProviderAuthorityPackageLPr(

"downloads", userId);

if (downloadsPackage != null) {

grantRuntimePermissionsLPw(downloadsPackage, STORAGE_PERMISSIONS, true,userId);

}



// Downloads UI

Intent downloadsUiIntent = newIntent(DownloadManager.ACTION_VIEW_DOWNLOADS);

PackageParser.PackagedownloadsUiPackage = getDefaultSystemHandlerActivityPackageLPr(

downloadsUiIntent, userId);

if (downloadsUiPackage != null

&&doesPackageSupportRuntimePermissions(downloadsUiPackage)) {

grantRuntimePermissionsLPw(downloadsUiPackage, STORAGE_PERMISSIONS,true, userId);

}



// Storage provider

PackageParser.PackagestoragePackage = getDefaultProviderAuthorityPackageLPr(

"com.android.externalstorage.documents", userId);

if (storagePackage != null) {

grantRuntimePermissionsLPw(storagePackage, STORAGE_PERMISSIONS, true,userId);

}



// CertInstaller

Intent certInstallerIntent = newIntent(Credentials.INSTALL_ACTION);

PackageParser.PackagecertInstallerPackage = getDefaultSystemHandlerActivityPackageLPr(

certInstallerIntent, userId);

if (certInstallerPackage != null

&&doesPackageSupportRuntimePermissions(certInstallerPackage)) {

grantRuntimePermissionsLPw(certInstallerPackage, STORAGE_PERMISSIONS,true, userId);

}



// Dialer

if (dialerAppPackageNames == null){

Intent dialerIntent = newIntent(Intent.ACTION_DIAL);

PackageParser.PackagedialerPackage = getDefaultSystemHandlerActivityPackageLPr(

dialerIntent, userId);

if (dialerPackage != null) {

grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage,userId);

}

} else {

for (StringdialerAppPackageName : dialerAppPackageNames) {

PackageParser.PackagedialerPackage = getSystemPackageLPr(dialerAppPackageName);

if (dialerPackage != null){

grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage,userId);

}

}

}



// Sim call manager

if (simCallManagerPackageNames !=null) {

for (String simCallManagerPackageName: simCallManagerPackageNames) {

PackageParser.PackagesimCallManagerPackage =

getSystemPackageLPr(simCallManagerPackageName);

if (simCallManagerPackage!= null) {

grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage,

userId);

}

}

}



// SMS

if (smsAppPackageNames == null) {

Intent smsIntent = newIntent(Intent.ACTION_MAIN);

smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);

PackageParser.PackagesmsPackage = getDefaultSystemHandlerActivityPackageLPr(

smsIntent, userId);

if (smsPackage != null) {

grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);

}

} else {

for (String smsPackageName :smsAppPackageNames) {

PackageParser.PackagesmsPackage = getSystemPackageLPr(smsPackageName);

if (smsPackage != null) {

grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);

}

}

}



// Cell Broadcast Receiver

Intent cbrIntent = newIntent(Intents.SMS_CB_RECEIVED_ACTION);

PackageParser.Package cbrPackage =

getDefaultSystemHandlerActivityPackageLPr(cbrIntent, userId);

if (cbrPackage != null &&doesPackageSupportRuntimePermissions(cbrPackage)) {

grantRuntimePermissionsLPw(cbrPackage, SMS_PERMISSIONS, userId);

}



// Carrier Provisioning Service

Intent carrierProvIntent = newIntent(Intents.SMS_CARRIER_PROVISION_ACTION);

PackageParser.PackagecarrierProvPackage =

getDefaultSystemHandlerServicePackageLPr(carrierProvIntent, userId);

if (carrierProvPackage != null&& doesPackageSupportRuntimePermissions(carrierProvPackage)) {

grantRuntimePermissionsLPw(carrierProvPackage, SMS_PERMISSIONS, false,userId);

}



// Calendar

Intent calendarIntent = newIntent(Intent.ACTION_MAIN);

calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);

PackageParser.PackagecalendarPackage = getDefaultSystemHandlerActivityPackageLPr(

calendarIntent, userId);

if (calendarPackage != null

&&doesPackageSupportRuntimePermissions(calendarPackage)) {

grantRuntimePermissionsLPw(calendarPackage, CALENDAR_PERMISSIONS,userId);

grantRuntimePermissionsLPw(calendarPackage,CONTACTS_PERMISSIONS, userId);

}



// Calendar provider

PackageParser.PackagecalendarProviderPackage = getDefaultProviderAuthorityPackageLPr(

CalendarContract.AUTHORITY,userId);

if (calendarProviderPackage !=null) {

grantRuntimePermissionsLPw(calendarProviderPackage,CONTACTS_PERMISSIONS, userId);

grantRuntimePermissionsLPw(calendarProviderPackage,CALENDAR_PERMISSIONS,

true, userId);

grantRuntimePermissionsLPw(calendarProviderPackage, STORAGE_PERMISSIONS,userId);

}



// Calendar provider sync adapters

ListcalendarSyncAdapters = getHeadlessSyncAdapterPackagesLPr(

calendarSyncAdapterPackages, userId);

final int calendarSyncAdapterCount= calendarSyncAdapters.size();

for (int i = 0; i

PackageParser.Package calendarSyncAdapter =calendarSyncAdapters.get(i);

if(doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {

grantRuntimePermissionsLPw(calendarSyncAdapter, CALENDAR_PERMISSIONS,userId);

}

}



// Contacts

Intent contactsIntent = newIntent(Intent.ACTION_MAIN);

contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);

PackageParser.PackagecontactsPackage = getDefaultSystemHandlerActivityPackageLPr(

contactsIntent, userId);

if (contactsPackage != null

&&doesPackageSupportRuntimePermissions(contactsPackage)) {

grantRuntimePermissionsLPw(contactsPackage, CONTACTS_PERMISSIONS,userId);

grantRuntimePermissionsLPw(contactsPackage, PHONE_PERMISSIONS, userId);

}



// Contacts provider sync adapters

ListcontactsSyncAdapters = getHeadlessSyncAdapterPackagesLPr(

contactsSyncAdapterPackages, userId);

final int contactsSyncAdapterCount= contactsSyncAdapters.size();

for (int i = 0; i < contactsSyncAdapterCount;i++) {

PackageParser.PackagecontactsSyncAdapter = contactsSyncAdapters.get(i);

if(doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {

grantRuntimePermissionsLPw(contactsSyncAdapter, CONTACTS_PERMISSIONS,userId);

}

}



// Contacts provider

PackageParser.PackagecontactsProviderPackage = getDefaultProviderAuthorityPackageLPr(

ContactsContract.AUTHORITY,userId);

if (contactsProviderPackage !=null) {

grantRuntimePermissionsLPw(contactsProviderPackage,CONTACTS_PERMISSIONS,

true, userId);

grantRuntimePermissionsLPw(contactsProviderPackage, PHONE_PERMISSIONS,

true, userId);

grantRuntimePermissionsLPw(contactsProviderPackage, STORAGE_PERMISSIONS,userId);

}



// Device provisioning

Intent deviceProvisionIntent = newIntent(

DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);

PackageParser.PackagedeviceProvisionPackage =

getDefaultSystemHandlerActivityPackageLPr(deviceProvisionIntent,userId);

if (deviceProvisionPackage != null

&&doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {

grantRuntimePermissionsLPw(deviceProvisionPackage, CONTACTS_PERMISSIONS,userId);

}



// Maps

Intent mapsIntent = newIntent(Intent.ACTION_MAIN);

mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);

PackageParser.Package mapsPackage =getDefaultSystemHandlerActivityPackageLPr(

mapsIntent, userId);

if (mapsPackage != null

&&doesPackageSupportRuntimePermissions(mapsPackage)) {

grantRuntimePermissionsLPw(mapsPackage, LOCATION_PERMISSIONS, userId);

}



// Gallery

Intent galleryIntent = newIntent(Intent.ACTION_MAIN);

galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);

PackageParser.PackagegalleryPackage = getDefaultSystemHandlerActivityPackageLPr(

galleryIntent, userId);

if (galleryPackage != null

&&doesPackageSupportRuntimePermissions(galleryPackage)) {

grantRuntimePermissionsLPw(galleryPackage, STORAGE_PERMISSIONS, userId);

}



// Email

Intent emailIntent = newIntent(Intent.ACTION_MAIN);

emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);

PackageParser.Package emailPackage =getDefaultSystemHandlerActivityPackageLPr(

emailIntent, userId);

if (emailPackage != null

&&doesPackageSupportRuntimePermissions(emailPackage)) {

grantRuntimePermissionsLPw(emailPackage,CONTACTS_PERMISSIONS, userId);

}



// Browser

PackageParser.PackagebrowserPackage = null;

String defaultBrowserPackage =mService.getDefaultBrowserPackageName(userId);

if (defaultBrowserPackage != null){

browserPackage =getPackageLPr(defaultBrowserPackage);

}

if (browserPackage == null) {

Intent browserIntent = newIntent(Intent.ACTION_MAIN);

browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);

browserPackage =getDefaultSystemHandlerActivityPackageLPr(

browserIntent, userId);

}

if (browserPackage != null

&&doesPackageSupportRuntimePermissions(browserPackage)) {

grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS,userId);

}



// IME

if (imePackageNames != null) {

for (String imePackageName :imePackageNames) {

PackageParser.PackageimePackage = getSystemPackageLPr(imePackageName);

if (imePackage != null

&&doesPackageSupportRuntimePermissions(imePackage)) {

grantRuntimePermissionsLPw(imePackage, CONTACTS_PERMISSIONS, userId);

}

}

}



// Voice interaction

if (voiceInteractPackageNames !=null) {

for (String voiceInteractPackageName: voiceInteractPackageNames) {

PackageParser.PackagevoiceInteractPackage = getSystemPackageLPr(

voiceInteractPackageName);

if (voiceInteractPackage !=null

&&doesPackageSupportRuntimePermissions(voiceInteractPackage)) {

grantRuntimePermissionsLPw(voiceInteractPackage,

CONTACTS_PERMISSIONS, userId);

grantRuntimePermissionsLPw(voiceInteractPackage,

CALENDAR_PERMISSIONS, userId);

grantRuntimePermissionsLPw(voiceInteractPackage,

MICROPHONE_PERMISSIONS, userId);

grantRuntimePermissionsLPw(voiceInteractPackage,

PHONE_PERMISSIONS, userId);

grantRuntimePermissionsLPw(voiceInteractPackage,

SMS_PERMISSIONS, userId);

grantRuntimePermissionsLPw(voiceInteractPackage,

LOCATION_PERMISSIONS, userId);

}

}

}



// Voice recognition

Intent voiceRecoIntent = newIntent("android.speech.RecognitionService");

voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);

PackageParser.PackagevoiceRecoPackage = getDefaultSystemHandlerServicePackageLPr(

voiceRecoIntent, userId);

if (voiceRecoPackage != null

&&doesPackageSupportRuntimePermissions(voiceRecoPackage)) {

grantRuntimePermissionsLPw(voiceRecoPackage, MICROPHONE_PERMISSIONS,userId);

}



// Location

if (locationPackageNames != null) {

for (String packageName :locationPackageNames) {

PackageParser.PackagelocationPackage = getSystemPackageLPr(packageName);

if (locationPackage != null

&&doesPackageSupportRuntimePermissions(locationPackage)) {

grantRuntimePermissionsLPw(locationPackage, CONTACTS_PERMISSIONS,userId);

grantRuntimePermissionsLPw(locationPackage,CALENDAR_PERMISSIONS, userId);

grantRuntimePermissionsLPw(locationPackage, MICROPHONE_PERMISSIONS,userId);

grantRuntimePermissionsLPw(locationPackage, PHONE_PERMISSIONS, userId);

grantRuntimePermissionsLPw(locationPackage, SMS_PERMISSIONS, userId);

grantRuntimePermissionsLPw(locationPackage, LOCATION_PERMISSIONS,

true, userId);

grantRuntimePermissionsLPw(locationPackage, CAMERA_PERMISSIONS, userId);

grantRuntimePermissionsLPw(locationPackage, SENSORS_PERMISSIONS,userId);

grantRuntimePermissionsLPw(locationPackage, STORAGE_PERMISSIONS,userId);

}

}

}



// Music

Intent musicIntent = newIntent(Intent.ACTION_VIEW);

musicIntent.addCategory(Intent.CATEGORY_DEFAULT);

musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),

AUDIO_MIME_TYPE);

PackageParser.Package musicPackage= getDefaultSystemHandlerActivityPackageLPr(

musicIntent, userId);

if (musicPackage != null

&&doesPackageSupportRuntimePermissions(musicPackage)) {

grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);

}



mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);

}

}

這個方法好長好長,其實說白了就是針對滿足一些系統固有的規則(必須應用安裝,校驗,聯系人存儲、下載、撥號等等)的應用授予最基本的權限(其實就是強關聯的權限,比如downloadprovider授予讀寫存儲的權限和網絡權限),同時這個權限並不是fix的是可以關閉的。

 

五、特別的權限:

android.permission.SYSTEM_ALERT_WINDOW
android.permission.WRITE_SETTINGS

這兩個權限的實現是完全新加的也是跟上面的檢查過程不一樣的。

android.permission.WRITE_SETTINGS權限的設置和驗證代碼主要Settings應用和SettingsProvider裡面

WriteSettingsDetails中進行設置,方法如下:

private void setCanWriteSettings(boolean newState) {

mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS,

mPackageInfo.applicationInfo.uid, mPackageName, newState

? AppOpsManager.MODE_ALLOWED: AppOpsManager.MODE_ERRORED);

}

SettingsProvider的讀寫方法中進行檢驗

可以看看這個類中見的幾個mutate****Setting的方法,針對其中的Global和Secure表都需要權限Manifest.permission.WRITE_SECURE_SETTINGS,這個如果沒有權限就直接異常了。

private voidenforceWritePermission(String permission) {

if (getContext().checkCallingOrSelfPermission(permission)

!= PackageManager.PERMISSION_GRANTED){

throw new SecurityException("Permissiondenial: writing to settings requires:"

+ permission);

}

}

對於System表的寫操作,先檢測是否有WRITE_SECURE_SETTINGS權限,如果沒有則進行檢查是否已授權WRITE_SETTINGS權限,後一個檢查過程就是可以動態控制的了(稍後再看),檢查完權限之後接下來做一個操作的限制。

if (!hasWriteSecureSettingsPermission()) {

// If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whetherthis

// operation is allowed for the calling package through appops.

if (!Settings.checkAndNoteWriteSettingsOperation(getContext(),

Binder.getCallingUid(),getCallingPackage(), true)) {

return false;

}

}

下面這個方法做限制其大意就是,如果是systemUID或者shell或者root不受限制,其他進程插入和更新操作都只能操作PUBLIC_SETTINGS,對其他表的數據如果應用的targetsdkversion>=23都會異常,對於刪除操作則都不能進行(例外的是targetsdkversion<23的可以刪除自定義的字段)

private voidenforceRestrictedSystemSettingsMutationForCallingPackage(int operation,

String name) {

// System/root/shell can mutate whatever secure settings they want.

final int callingUid= Binder.getCallingUid();

if (callingUid == android.os.Process.SYSTEM_UID

|| callingUid == Process.SHELL_UID

|| callingUid == Process.ROOT_UID) {

return;

}

switch (operation) {

case MUTATION_OPERATION_INSERT:

// Insert updates.

case MUTATION_OPERATION_UPDATE: {

if (Settings.System.PUBLIC_SETTINGS.contains(name)) {

return;

}

// The calling package is already verified.

PackageInfo packageInfo =getCallingPackageInfoOrThrow();

// Privileged apps can do whatever they want.

if ((packageInfo.applicationInfo.privateFlags

& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)!= 0) {

return;

}

warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(

packageInfo.applicationInfo.targetSdkVersion, name);

} break;

case MUTATION_OPERATION_DELETE: {

if (Settings.System.PUBLIC_SETTINGS.contains(name)

|| Settings.System.PRIVATE_SETTINGS.contains(name)) {

throw new IllegalArgumentException("You cannotdelete system defined"

+ " secure settings.");

}

// The calling package is already verified.

PackageInfo packageInfo =getCallingPackageInfoOrThrow();

// Privileged apps can do whatever they want.

if ((packageInfo.applicationInfo.privateFlags &

ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)!= 0) {

return;

}

warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(

packageInfo.applicationInfo.targetSdkVersion, name);

} break;

}

}

private voidwarnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(

int targetSdkVersion, String name){

// If the app targets Lollipop MR1 or older SDK we warn, otherwise crash.

if (targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {

if (Settings.System.PRIVATE_SETTINGS.contains(name)) {

Slog.w(LOG_TAG, "You shouldn't not change private systemsettings."

+ " This will soon become an error.");

} else {

Slog.w(LOG_TAG, "You shouldn't keep your settings in the securesettings."

+ " This will soon become an error.");

}

} else {

if (Settings.System.PRIVATE_SETTINGS.contains(name)) {

throw new IllegalArgumentException("You cannotchange private system settings.");

} else {

throw new IllegalArgumentException("You cannot keepyour settings in"

+ " the secure settings. oppo app can requestoppo.permission.OPPO_COMPONENT_SAFE permission!");

}

}

}

綜合上面的描述得出的結論是,targetsdkversion>=23也就是6.0之後,只有系統應用才可以寫Global和Secure表,對於System表則第三方應用在授權WRITE_SETTINGS的前提下也只能寫其中的PUBLIC_SETTINGS字段。

然後看動態檢驗過程,檢驗都是動態的,這裡指的是檢驗的值可以動態變化,變化的途徑前面將了是通過AppOpsManager.setMode,為什麼會生效呢就得看剛剛留下的一個方法。在framework的Settings中

public static booleancheckAndNoteDrawOverlaysOperation(Context context, int uid, String

callingPackage, boolean throwException) {

returnisCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,

callingPackage, throwException, AppOpsManager.OP_SYSTEM_ALERT_WINDOW,

PM_SYSTEM_ALERT_WINDOW, true);

}



/**

* Helper method toperform a general and comprehensive check of whether an operation that is

* protected by appopscan be performed by a caller or not. e.g. OP_SYSTEM_ALERT_WINDOW and

* OP_WRITE_SETTINGS

* @hide

*/

public static booleanisCallingPackageAllowedToPerformAppOpsProtectedOperation(Context context,

int uid,String callingPackage, boolean throwException, int appOpsOpCode, String[]

permissions,boolean makeNote) {

if (callingPackage== null) {

return false;

}



AppOpsManagerappOpsMgr = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);

int mode =AppOpsManager.MODE_DEFAULT;

if (makeNote) {

mode =appOpsMgr.noteOpNoThrow(appOpsOpCode, uid, callingPackage);

} else {

mode =appOpsMgr.checkOpNoThrow(appOpsOpCode, uid, callingPackage);

}



switch (mode) {

caseAppOpsManager.MODE_ALLOWED:

returntrue;



case AppOpsManager.MODE_DEFAULT:

// this isthe default operating mode after an app's installation

// In thiscase we will check all associated static permission to see

// if itis granted during install time.

for (String permission :permissions) {

if(context.checkCallingOrSelfPermission(permission) == PackageManager

.PERMISSION_GRANTED) {

//if either of the permissions are granted, we will allow it

return true;

}

}



default:

// this isfor all other cases trickled down here...

if(!throwException) {

returnfalse;

}

}



// prepare stringto throw SecurityException

StringBuilderexceptionMessage = new StringBuilder();

exceptionMessage.append(callingPackage);

exceptionMessage.append(" was not granted ");

if(permissions.length > 1) {

exceptionMessage.append(" either of these permissions: ");

} else {

exceptionMessage.append(" this permission: ");

}

for (int i = 0; i< permissions.length; i++) {

exceptionMessage.append(permissions[i]);

exceptionMessage.append((i == permissions.length - 1) ? "." :", ");

}



throw newSecurityException(exceptionMessage.toString());

}

這個方法的做法一目了然,就是直接從AppOpsManager去讀取對應的OpCode的模式,如果是允許的那就判斷為授予,如果是默認狀態則使用之前權限是否靜態授予的狀態(其實這個默認狀態對於其他未修改的動態權限也是有類似操作的在PackageManagerService. GrantPermissionsLPw()方法中)。

 

最後,android.permission.SYSTEM_ALERT_WINDOW這個的設置也在設置應用中(DrawOverlayDetails這個類中設置,同樣也是通過AppOpsManager.setMode來進行控制),但是檢驗過程在framework的window管理部分本篇暫不涉及。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved