Android 12.0 根据包名授予WRITE_SETTINGS权限

Posted 安卓兼职framework应用工程师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12.0 根据包名授予WRITE_SETTINGS权限相关的知识,希望对你有一定的参考价值。

1.概述

在12.0的系统产品开发中,对于在项目中授予权限功能也是常见的功能,在首次开机中默认授权运行时权限,还有就是特殊权限,比如悬浮窗权限,WRITE_SETTINGS权限,安装第三方

app等等特殊权限的授予等等,在最近的项目中,就是需要根据包名默认授权WRITE_SETTINGS权限,接下来就分析下系统Settings中的授权WRITE_SETTINGS权限

的方法,来授予授权WRITE_SETTINGS权限功能实现

2.根据包名授予WRITE_SETTINGS权限的核心类

packages\\apps\\Settings\\src\\com\\android\\settings\\applications\\appinfo\\WriteSettingsDetails.java
frameworks\\base\\services\\core\\java\\com\\android\\server\\policy\\PhoneWindowManager.java

3.根据包名授予WRITE_SETTINGS权限的核心功能分析和实现

在关于系统的特殊权限,比如悬浮窗权限,WRITE_SETTINGS权限,安装第三方app等等特殊权限的授予的相关方法中,在系统Settings中会在每个app的详情页会

出现高级设置的选项中,对于申请悬浮窗权限,WRITE_SETTINGS权限,安装第三方app的权限等会需要手动打开相关的特殊权限,所以可以从这里

查找相关的源码来实现设置这些特殊权限的功能,接下来看下WriteSettingsDetails.java的相关源码,分析下功能的实现

3.1WriteSettingsDetails.java中关于授予WRITE_SETTINGS权限相关源码的分析和实现

public class WriteSettingsDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
          OnPreferenceClickListener 
  
      private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
      private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
      private static final String LOG_TAG = "WriteSettingsDetails";
  
      private static final int [] APP_OPS_OP_CODE = 
              AppOpsManager.OP_WRITE_SETTINGS
      ;
  
      // Use a bridge to get the overlay details but don't initialize it to connect with all state.
      // TODO: Break out this functionality into its own class.
      private AppStateWriteSettingsBridge mAppBridge;
      private AppOpsManager mAppOpsManager;
      private SwitchPreference mSwitchPref;
      private Intent mSettingsIntent;
      private WriteSettingsState mWriteSettingsState;
  
      @Override
      public void onCreate(Bundle savedInstanceState) 
          super.onCreate(savedInstanceState);
  
          Context context = getActivity();
          mAppBridge = new AppStateWriteSettingsBridge(context, mState, null);
          mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
  
          addPreferencesFromResource(R.xml.write_system_settings_permissions_details);
          mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
  
          mSwitchPref.setOnPreferenceChangeListener(this);
  
          mSettingsIntent = new Intent(Intent.ACTION_MAIN)
                  .addCategory(Settings.INTENT_CATEGORY_USAGE_ACCESS_CONFIG)
                  .setPackage(mPackageName);
      

在上述的WriteSettingsDetails.java的相关方法中,mSwitchPref;就是开启关闭WRITE_SETTINGS权限的开关,根据开关的打开关闭情况

来设置相关的代码,而在mAppOpsManager就是调用相关的方法来实现功能,在onCreate(Bundle savedInstanceState)中

通过实例化mAppOpsManager等参数和实例化mSwitchPref 这个功能开关来实现对WRITE_SETTINGS权限的开关,

通过mSwitchPref.setOnPreferenceChangeListener(this);来监听开关的变化情况,来实现功能

    @Override
     public boolean onPreferenceClick(Preference preference) 
         return false;
     
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) 
         if (preference == mSwitchPref) 
             if (mWriteSettingsState != null && (Boolean) newValue != mWriteSettingsState
                     .isPermissible()) 
                 setCanWriteSettings(!mWriteSettingsState.isPermissible());
                 refreshUi();
             
             return true;
         
         return false;
     
 
     private void setCanWriteSettings(boolean newState) 
         logSpecialPermissionChange(newState, mPackageName);
         mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS,
                 mPackageInfo.applicationInfo.uid, mPackageName, newState
                  ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
      
  
      void logSpecialPermissionChange(boolean newState, String packageName) 
          int logCategory = newState ? SettingsEnums.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW
                  : SettingsEnums.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY;
          FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
                  logCategory, packageName);
      
  
      private boolean canWriteSettings(String pkgName) 
          int result = mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_SETTINGS,
                  mPackageInfo.applicationInfo.uid, pkgName);
          if (result == AppOpsManager.MODE_ALLOWED) 
              return true;
          
  
          return false;
      

在上述的WriteSettingsDetails.java的相关方法中,onPreferenceChange(Preference preference, Object newValue) 就是

WRITE_SETTINGS权限的开关控件,通过监听开关情况,来实现相关的方法,setCanWriteSettings(!mWriteSettingsState.isPermissible());

通过这个方法来根据包名设置WRITE_SETTINGS的权限,然后通过refreshUi();来刷新UI布局

在setCanWriteSettings(!mWriteSettingsState.isPermissible());中,通过调用mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS

来根据包名来设置权限,最重要的就是这一段的功能

  @Override
      protected boolean refreshUi() 
          mWriteSettingsState = mAppBridge.getWriteSettingsInfo(mPackageName,
                  mPackageInfo.applicationInfo.uid);
  
          boolean canWrite = mWriteSettingsState.isPermissible();
          mSwitchPref.setChecked(canWrite);
          // you can't ask a user for a permission you didn't even declare!
          mSwitchPref.setEnabled(mWriteSettingsState.permissionDeclared);
  
          ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
                  PackageManager.GET_META_DATA, mUserId);
          return true;
      

在上述的WriteSettingsDetails.java的相关方法中, refreshUi()就是通过开关的打开和关闭情况来刷新WRITE_SETTINGS权限的开关控件的

主要就是更新WRITE_SETTINGS权限的开关控件的布局

3.2 PhoneWindowManager.java中根据包名来授权WRITE_SETTINGS权限

   @Override
    public void allowAppWriteSettingsPermission(String pkg) throws RemoteException 
        final long ident = Binder.clearCallingIdentity();
        try 
            if (!TextUtils.isEmpty(pkg)) 
                AppOpsManager mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
                PackageManager pm = mContext.getPackageManager();
                ApplicationInfo ai = pm.getApplicationInfo(pkg, PackageManager.GET_ACTIVITIES);
                mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS, ai.uid, pkg, AppOpsManager.MODE_ALLOWED);
            
         catch (PackageManager.NameNotFoundException e) 
            e.printStackTrace();
         finally 
            Binder.restoreCallingIdentity(ident);
        
    

首选在PhoneWindowManager.java中的方法中,增加allowAppWriteSettingsPermission(String pkg) 这个根据包名

授予WRITE_SETTINGS权限的方法,主要是调用mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS

来授予权限

   @Override
      public void systemReady() 
          // In normal flow, systemReady is called before other system services are ready.
          // So it is better not to bind keyguard here.
          mKeyguardDelegate.onSystemReady();
  
          mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
          if (mVrManagerInternal != null) 
              mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
          
  
          readCameraLensCoverState();
          updateUiMode();
          mDefaultDisplayRotation.updateOrientationListener();
          synchronized (mLock) 
              mSystemReady = true;
              mHandler.post(new Runnable() 
                  @Override
                  public void run() 
                      updateSettings();
                  
              );
              // If this happens, for whatever reason, systemReady came later than systemBooted.
              // And keyguard should be already bound from systemBooted
              if (mSystemBooted) 
                  mKeyguardDelegate.onBootCompleted();
              
          
  + allowAppWriteSettingsPermission("包名");
          mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class);
          mGestureLauncherService = LocalServices.getService(GestureLauncherService.class);
      

在PhoneWindowManager.java中的上述方法中,通过allowAppWriteSettingsPermission(String pkg) 来根据包名设置WRITE_SETTINGS的权限

然后添加到systemReady()中就保证在开机后授予app的WRITE_SETTINGS的权限,就实现了产品中的项目需求

android.permission.WRITE_SETTINGS 是不是只授予系统应用程序?

【中文标题】android.permission.WRITE_SETTINGS 是不是只授予系统应用程序?【英文标题】:Is android.permission.WRITE_SETTINGS only granted to system apps?android.permission.WRITE_SETTINGS 是否只授予系统应用程序? 【发布时间】:2020-11-21 07:35:59 【问题描述】:

我们目前正在开发一个应用程序,我们希望在其中更改一些系统设置,当然需要用户许可。 android 文档说要做到这一点,你必须添加以下权限:

<uses-permission android:name="android.permission.WRITE_SETTINGS" />

此外,必须明确要求用户启用此权限。摘自https://developer.android.com/reference/android/Manifest.permission#WRITE_SETTINGS 说:

注意:如果应用以 API 级别 23 或更高级别为目标,应用用户必须 通过权限显式将此权限授予应用程序 管理画面。该应用程序通过发送一个请求用户的批准 意图行动Settings.ACTION_MANAGE_WRITE_SETTINGS。该应用程序可以 通过调用检查它是否具有此授权 Settings.System.canWrite().

到目前为止,一切都很清楚。但是,当将权限添加到 AndroidManifest.xml 文件时,Android Studio 会抱怨“权限仅授予系统应用程序”。现在,这令人困惑,因为我没有找到任何说明它确实只授予系统应用程序的文档。 所以,我想问一下是否有人遇到过这个问题,是否有某种文档可以详细解释这个问题?还是我只是错过了什么?

【问题讨论】:

检查这个。 ***.com/questions/32083410/… 【参考方案1】:

正如Brian 提供的*** question 中的用户passsy 所述,android.permission.WRITE_SETTINGSandroid:protectionLevel"signature",这使得该权限自API 级别23 起无法在用户应用程序中使用。 权限保护级别说明见https://developer.android.com/guide/topics/manifest/permission-element#plevel。

【讨论】:

【参考方案2】:

您需要专门从用户那里获得用户权限,我的意思是您必须将用户带到一个屏幕,用户可以在该屏幕上向您的应用授予 WRITE_SETTINGS 权限。

因此,当您想要更改系统设置时,您必须检查用户是否已授予权限:

private boolean checkSystemWritePermission() 
    boolean retVal = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
        retVal = Settings.System.canWrite(this);
        Log.d("TAG", "Can Write Settings: " + retVal);
        if(retVal)
            ///Permission granted by the user
        else
            //permission not granted navigate to permission screen
            openAndroidPermissionsMenu();
        
    
    return retVal;


private void openAndroidPermissionsMenu() 
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + this.getPackageName()));
    startActivity(intent);
 

【讨论】:

这个我很熟悉。请检查我自己对这个问题的回答。

以上是关于Android 12.0 根据包名授予WRITE_SETTINGS权限的主要内容,如果未能解决你的问题,请参考以下文章

Android 11及以上授予文件管理权限

高通Android 12/13 默认应用程序授予权限

Android 逆向应用安装目录 ( Android 应用的默认安装目录 | 查找 Android 应用的安装目录 | 查询当前正在运行的应用包名 | 根据包名查询应用安装路径 )(代码片

Android 逆向应用安装目录 ( Android 应用的默认安装目录 | 查找 Android 应用的安装目录 | 查询当前正在运行的应用包名 | 根据包名查询应用安装路径 )(代码片

Android 检查手机上是否安装了指定的软件(根据包名检测)

如何在 Android 10 中根据包名共享不同的文本?