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接口。
Android 6.0 运行时权限检查分析_第1张图片

来看看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(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.");

                }

                // 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(

            inttargetSdkVersion, 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 {

                    thrownew 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来进行控制),但是检验过程在frameworkwindow管理部分本篇暂不涉及。


更多相关文章

  1. Android之Rxjava2.X 2————Rxjava 创建操作符
  2. Android 开发——'Android Pre Compiler'空指针问题的解决方法
  3. Android中屏幕相关的操作
  4. android TextView的字体颜色设置的多种方法(续)
  5. android 中的url操作
  6. android中怎样声明操作通话记录的权利
  7. Android获得系统(system)权限
  8. Android通知权限相关工具类
  9. Android 仿微信TabHost使用方法详解

随机推荐

  1. Dart及Flutter杂记
  2. Android小項目之ListView实现论坛管理效
  3. Android(安卓)Intent的几种用法全面总结
  4. Android(安卓)滑动效果高级篇(七)—— 华丽
  5. Android之Adapter用法总结
  6. 图说Android开机画面和开机动画
  7. Android(安卓)USB下的Accessory模式
  8. Android入门前言(一)之------Android应用
  9. 专访丰生强:Android软件安全与逆向分析
  10. Android(安卓)MediaPlayer的核心原理