Android 11 关于app的权限重置
Android 11 Google针对权限系统又加了更多的限制,比如1.权限的单次授权;2.某个app长时间不使用,权限会被自动更新/重置!
关于app的权限重置
Android Framework权限篇四之AppOps机制
Settings里面关于app权限重置的switch 流程
./packages/apps/Settings/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
...
Intent.ACTION_AUTO_REVOKE_PERMISSIONS, INVALID_SESSION_ID
...
./packages/apps/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
private void addAutoRevokePreferences(PreferenceScreen screen) {
Context context = screen.getPreferenceManager().getContext();
PreferenceCategory autoRevokeCategory = new PreferenceCategory(context);
autoRevokeCategory.setKey(AUTO_REVOKE_CATEGORY_KEY);
screen.addPreference(autoRevokeCategory);
SwitchPreference autoRevokeSwitch = new SwitchPreference(context);
autoRevokeSwitch.setOnPreferenceClickListener((preference) -> {
mViewModel.setAutoRevoke(autoRevokeSwitch.isChecked());
return true;
});
...
./packages/apps/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
fun setAutoRevoke(enabled: Boolean) {
GlobalScope.launch(IPC) {
val aom = app.getSystemService(AppOpsManager::class.java)!!
val uid = LightPackageInfoLiveData[packageName, user].getInitializedValue()?.uid
if (uid != null) {
Log.i(LOG_TAG, "sessionId $sessionId setting auto revoke enabled to $enabled for" +
"$packageName $user")
val tag = if (enabled) {
APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
} else {
APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
}
PermissionControllerStatsLog.write(
APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, sessionId, uid, packageName,
tag)
val mode = if (enabled) {
MODE_ALLOWED
} else {
MODE_IGNORED
}
aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, mode)
}
}
}
./frameworks/base/core/java/android/app/AppOpsManager.java
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(int code, int uid, @Mode int mode) {
try {
mService.setUidMode(code, uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//或者
/** @hide */
@UnsupportedAppUsage
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setMode(int code, int uid, String packageName, @Mode int mode) {
try {
mService.setMode(code, uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//关于 op code 都是大写OP为首
/** @hide Auto-revoke app permissions if app is unused for an extended period */
public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
AppProtoEnums.APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
//adb 语法
AppOps service (appops) commands:
help
Print this help text.
start [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> <OP>
Starts a given operation for a particular application.
stop [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> <OP>
Stops a given operation for a particular application.
set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>
Set the mode for a particular application and operation.
get [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> [<OP>]
Return the mode for a particular application and optional operation.
query-op [--user <USER_ID>] <OP> [<MODE>]
Print all packages that currently have the given op in the given mode.
reset [--user <USER_ID>] [<PACKAGE>]
Reset the given application or all applications to default modes.
write-settings
Immediately write pending changes to storage.
read-settings
Read the last written settings, replacing current state in RAM.
options:
<PACKAGE> an Android package name or its UID if prefixed by --uid
<OP> an AppOps operation.
<MODE> one of allow, ignore, deny, or default
<USER_ID> the user id under which the package is installed. If --user is
not specified, the current user is assumed.
//给予安装位置来源apk权限
adb shell appops set com.android.settings REQUEST_INSTALL_PACKAGES allow
不过只能给Manifest关联的权限, AppOpsManager的sOpPerms[]数组
./frameworks/base/core/java/android/app/AppOpsManager.java
关于权限的几种修改方案,都是套路:
最先是要找到你需要操作的权限是啥,可以在AppOpsManager的数组里面查询!
方案一:源头操作
./frameworks/base/core/java/android/app/AppOpsManager.java
/**
* This specifies the default mode for each operation.
*/
private static int[] sOpDefaultMode = new int[] {
AppOpsManager.MODE_ALLOWED, // COARSE_LOCATION
AppOpsManager.MODE_ALLOWED, // FINE_LOCATION
AppOpsManager.MODE_ALLOWED, // GPS
...
- AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ AppOpsManager.MODE_IGNORED, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
...
方案二:系统服务拦截
./frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
private Op getOpLocked(Ops ops, int code, int uid, boolean edit) {
Op op = ops.get(code);
if (op == null) {
if (!edit) {
return null;
}
op = new Op(ops.uidState, ops.packageName, code, uid);
ops.put(code, op);
}
//add text
if(ops.packageName.equals("com.xxx")){
Op op1 = new Op(ops.uidState, ops.packageName, AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,uid);
op1.mode = AppOpsManager.MODE_IGNORED;//MODE_ALLOWED
ops.put(op1.op, op1);
}
//add text
if (edit) {
scheduleWriteLocked();
}
return op;
}
在WriteSettingsDetails.java中的setCanWriteSettings(boolean newState)可以发现
主要是通过mAppOpsManager.setMode来设置权限AppOpsManager.OP_WRITE_SETTINGS的
根据newState的值设置AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED
来开启和关闭WRITE_SETTINGS的权限
PhoneWindowManager.java中的systemReady()是在开机完成后就会调用的,所以可以在这里
根据包名设置WRITE_SETTINGS的权限。
public void systemReady() {、
...
if (mSystemBooted) {
mKeyguardDelegate.onBootCompleted();
}
}
+ allowAppWriteSettingsPermission("xxxx");
}
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);
}
}
方案三:参考
private void setSomeAppPermission(){
int uid = 0;
//大部分系统app才能读到
/*PackageManager packageManager = mContext.getPackageManager();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo("com.xxx",0);
uid = applicationInfo.uid;
}catch (Exception e){
}*/
AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
appOpsManager.setUidMode(AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, 10136, AppOpsManager.MODE_IGNORED);
/*appOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
10136, "com.xxx", AppOpsManager.MODE_IGNORED);*/
}
sdcard 默认读写权限 (Android R)
让 system_server 可以去读写 SDCARD
OsConstants.CAP_CHOWN是Linux系统中的一个权限常量,用于表示改变文件所有者的能力。在Linux系统中,权限管理是通过能力(capabilities)来实现的,每个进程都有一组能力,这些能力控制了进程可以执行的操作。CAP_CHOWN是这些能力之一,允许进程改变文件的所有者。
在Linux内核中,CAP_CHOWN通常与以下能力一起使用:
CAP_SYS_PTRACE:允许进程进行系统跟踪。
CAP_SYS_TIME:允许进程设置系统时间。
CAP_SYS_TTY_CONFIG:允许进程配置终端。
CAP_WAKE_ALARM:允许进程唤醒闹钟。
CAP_BLOCK_SUSPEND:允许进程阻止系统挂起。
CAP_DAC_OVERRIDE:允许进程忽略文件访问控制列表(DAC)。
CAP_DAC_READ_SEARCH:允许进程读取和搜索文件,即使它们通常不可访问
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
OsConstants.CAP_SYS_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM,
OsConstants.CAP_BLOCK_SUSPEND
OsConstants.CAP_BLOCK_SUSPEND,
+ OsConstants.CAP_CHOWN,
+ OsConstants.CAP_DAC_OVERRIDE,
+ OsConstants.CAP_DAC_READ_SEARCH
);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1015,1018,1021,1023,"
- + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
+ + "1024,1028,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
kernel/fs/namei.c
* If the DACs are ok we don't need any capability check.
*/
if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
return 0;
- return -EACCES;
return 0;
}