Android 6.0 运行时权限检查分析

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<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);

            }

 

            // 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管理部分本篇暂不涉及。


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值