Android 6.0 執行時許可權檢查分析
一、許可權控制組:
以上的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<String> 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<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);
}
}
}
}
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, Set<String>permissions,
boolean systemFixed, booleanoverrideUserChoice, int userId) {
if (pkg.requestedPermissions.isEmpty()){
return;
}
List<String> requestedPermissions= pkg.requestedPermissions;
Set<String> 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 <grantablePermissionCount; 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
List<PackageParser.Package>calendarSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
calendarSyncAdapterPackages, userId);
final int calendarSyncAdapterCount= calendarSyncAdapters.size();
for (int i = 0; i <calendarSyncAdapterCount; 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
List<PackageParser.Package>contactsSyncAdapters = 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);
}
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(booleannewState) {
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)) {
returnfalse;
}
}
下面這個方法做限制其大意就是,如果是systemUID或者shell或者root不受限制,其他程序插入和更新操作都只能操作PUBLIC_SETTINGS,對其他表的資料如果應用的targetsdkversion>=23都會異常,對於刪除操作則都不能進行(例外的是targetsdkversion<23的可以刪除自定義的欄位)
private voidenforceRestrictedSystemSettingsMutationForCallingPackage(intoperation,
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)) {
thrownew IllegalArgumentException("You cannotdelete system defined"
+ " secure settings.");
}
Android版本升級到6.0之後,為了一改往日安全受人詬病的形象,將許可權授權的安裝時授予的基礎上,對於一部分危險的許可權採用動態控制授權的方式。類似國內手機安全助手許可權控制的功能。一、許可權控制組:
以上的9組許可權在本次修改的範圍之內,可能有人要問了,明明有一些許可
Android 6.0執行時許可權勾選不再詢問後該如何處理?
當第一次請求許可權申請被拒絕後再進行第二次申請時,對話方塊中會多出一個 不再詢問 的複選框。如果勾選了該複選框並且拒絕請求,那麼以後將無法再申請該許可權。也就是說在呼叫 requestPermissions() 後,onRequest
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
  一、概述ok,本篇文章目的之一就是對執行時許可權處理的一個介紹,以及對目前許可權相關的庫的一些瞭解。當然非常推薦閱讀官網許可權相關文章:本文也是在上述文章基礎上理解、實驗以及封裝。二、執行時許可權的變化及特點對於6.0以下的許可權及在安裝的時候,根據許可權宣告產生一個
首先扯點別的,聽說這個週末是好天氣,想約她一起去公園賞賞梅花,只有我自己估計她也不一定去啊,哈哈。
在android6.0及以上系統,Android在安裝一個應用的時候不再需要列出一大堆許可權,讓使用者點選同意以後才可以安裝。Instead, 當應用在執行的時 前言
谷歌在2015年8月份時候,釋出了Android 6.0版本,代號叫做“棉花糖”(Marshmallow ),其中的很大的一部分變化,是在使用者許可權授權上,或許是感覺之前預設授權的不合理,現在6.0出來,使得使用者許可權授權變得合理。這可能也是參考IOS系統的,只有在使用者需要使用許可權的時候,才
1.概述
大家知道在Android6.0(Api23)之前,使用者許可權的申請都是在AndroidManifest.xml 宣告的,而在6.0之後,對某些涉及到使用者隱私的許可權可在執行時根據使用者的需要動態授予。如果你的應用設定targetSdkVersio
執行時許可權介紹
Android 6.0在我們原有的AndroidManifest.xml宣告許可權的基礎上,
又新增了執行時許可權動態檢測,以下許可權都需要在執行時判斷:
執行時許可權處理
Android6.0系統預設為targetSdkVersion小於23的應用預設授予了所申請的所有許可權,
這是一篇遲來的部落格,Android M已經發布一年多了(6.0的變化),在Android M中許可權系統被重新設計,發生了顛覆性的變化,很多人把握不好這個變化,一是對這個許可權策略和套路還沒有摸透,二是沒有一個很好的實踐來支撐,在我的技術開發群裡很多人問我
public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE=200;
public static final int REQUEST_C
一、概述
ok,本篇文章目的之一就是對執行時許可權處理的一個介紹,以及對目前許可權相關的庫的一些瞭解。
當然非常推薦閱讀官網許可權相關文章:
本文也是在上述文章基礎上理解、實驗以及封裝。
二、執行時許可權的變化及特點
對於6.0以下
執行時許可權的講解在前一篇部落格已經算是說的比較清楚了,這裡就不說了,如果對6.0這個新特性不是很瞭解的朋友建議先看看(地址:http://blog.csdn.net/qq_33923079/article/details/53428756),那麼本篇就直接說怎
什麼時候需要考慮執行時許可權
執行時許可權是Android6.0推出的安全機制,當你的app執行在 Android 6.0 及以上裝置上,使用危險許可權(下面介紹)的時候。
既targetSdkVer
目前的安卓手機系統版本普遍都在5.0、6.0以上甚至7.0、8.0。在Andorid 6.0系統之後開始引入了執行時許可權這個機制。許可權機制在android中原本就有的在manifest中編寫的permission標籤,而新增的執行時許可權則是將容易暴露使用者隱私的一些
最近在工作過程中會遇到,明明已經在AndroidManifest.xml中配置了許可權,但是就是沒有作用,百度了之後才發現現在在應用系統大於等於6.0的手機上面,需要動態的獲取許可權。就是當你需要這個許可權的時候,需要手機給使用者一個提示選擇是否同意開啟這個許
targetSDKVersion
簡單來說就代表著你的App能夠適配的系統版本,意味著你的App在這個版本的手機上做了充分的 前向 相容性處理和實際測試。其實我們寫程式碼時都是經常幹這麼一件事,就是 if(Build.VERSION.SDK_INT >= 23) {
許可權檢查一般放在APP的入口,比如說閃屏頁splashactivity中或者baseapplication中。
其中需要兩個類,一個是PermissionChecker,一個是PermissionsActivity。第一個是用來檢測當前是否已經獲取到了申請
Android 藍芽BLE開發需要位置許可權,不然掃描不到周圍的藍芽資訊;
位置許可權申請:
if (Build.VERSION.SDK_INT < 23){return;}
//判斷是否有許可權
if (ContextCompat.checkSelfPermis
Android許可權機制,在Android6.0之前,其實一直很簡單,在AndroidManifest.xml中進行許可權宣告,通過一個Intent呼叫即可。
以打電話為例:
<uses-permission android:name="andro
一.序
在之前的文章聊聊Android M 6.0 的執行時許可權曾提到過,Google Play開發者明年勢必要努力將targetsdkversion升級到26的過程中。官網的文件:向 Android 8.0 遷移應用 已經給大家了一個升級指南。當然動態許 相關推薦
Android 6.0 執行時許可權檢查分析
Android 6.0執行時許可權勾選不再詢問後該如何處理?
Android 6 0 執行時許可權處理
Android 6 0 執行時許可權處理完全解析
以呼叫系統相機拍照為例瞭解Android 6.0執行時許可權
談談Android 6.0執行時許可權理解
Android 6.0執行時許可權申請和一些坑
Android 6.0 執行時許可權處理
Android 6.0 執行時許可權管理最佳實踐
android 6.0執行時許可權應用之相簿圖片選取
Android 6.0 執行時許可權處理完全解析
Android 6.0執行時許可權第三方庫的使用-----RxPermissions
Android 6.0 執行時許可權
Android 6.0 執行時許可權機制瞭解與應用
Android 6.0執行時獲取許可權詳解
6.0執行時許可權與targetSdkVersion選擇
安卓6.0執行時許可權處理方案
Android 6.0動態申請許可權時,許可權框閃一下就消失的問題;
Android學習筆記-Android6.0執行時許可權
Android O 8.0 執行時許可權適配方案