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的权限,就实现了产品中的项目需求