Android 6.0 运行时权限检查分析

来源:互联网 发布:za的护肤品怎么样知乎 编辑:程序博客网 时间:2024/04/29 05:34
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, Stringname){

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


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孕妇感冒流鼻涕打喷嚏怎么办 孕妇感冒咳嗽流鼻涕怎么办 鼻子痒流鼻涕流眼泪怎么办 天冷鼻子流鼻涕怎么办 感冒流水样鼻涕怎么办 一直有清水鼻涕怎么办 孩子总打喷嚏鼻塞怎么办 4岁儿童流鼻涕怎么办 孕妇清鼻涕不停怎么办 感冒流鼻水怎么办速效办法 一岁宝宝流清涕怎么办 咳嗽喉咙痛有痰怎么办 冻感冒了流鼻涕怎么办 吸烟经常嗓子疼怎么办 擦鼻涕擦破了怎么办 感冒鼻水流不停怎么办 流鼻涕鼻子都擦红了怎么办 鼻子不停的流水怎么办 擦鼻涕耳朵疼怎么办 鼻涕跟水一样怎么办 鼻涕水多打喷嚏怎么办 买来的鼻涕泥是水怎么办? 鼻炎鼻涕跟水怎么办 出门忘记带钥匙怎么办 总留鼻涕水怎么办 流像水一样的鼻涕怎么办 出门忘记带洗面奶怎么办 一侧鼻子流清水怎么办 慢性肠胃炎犯了怎么办 儿子拉肚肚子疼怎么办 皮肤太容易过敏怎么办 过敏脸一直不好怎么办 脸上突然过敏怎么办急救 脸过敏发红疼怎么办 脸上突然过敏了怎么办 脸过敏总反复怎么办 用蜂蜜洗脸过敏怎么办 脸上涂蜂蜜过敏怎么办 孕晚期脸过敏怎么办 吃芒果脸过敏怎么办 脸上老反复过敏怎么办