0%

Android 7.1 AppOpsManager默认允许三方应用浮窗权限

需求
在高版本的SDK中, 第三方应用申请悬浮窗的权限受到了过一步的限制.
除了要在应用中声明对权限的申请:

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

还需要打开设置中的权限:

应用可以通过代码检测权限是否已获取:

1
2
3
4
5
AppOpsManager opsMgr = (AppOpsManager)getSystemService(APP_OPS_SERVICE);
int res = opsMgr.checkOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, Process.myUid(), getPackageName());
if(res != AppOpsManager.MODE_ALLOWED){
showToast(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW + " not allowed");
}

有可能会抛出异常:
1
java.lang.SecurityException: com.android.myapp from uid 10066 not allowed to perform SYSTEM_ALERT_WINDOW

若需要默认打开, 需要修改相关代码

修改
frameworks/base/services/core/java/com/android/server/AppOpsService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
//判断是否包含在白名单中, 并将其置为edit 置为 true.
//否则, 默认情况下, 则返回空, 导致在应用或其它服务获取packageName时, 发现其并未获取任何操作权限
edit |= checkIfInWhitelist(packageName);
UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) {
return null;
}
if (uidState.pkgOps == null) {
if (!edit) {
return null;
}
uidState.pkgOps = new ArrayMap<>();
}

Ops ops = uidState.pkgOps.get(packageName);
if (ops == null) {
if (!edit) {
return null;
}
boolean isPrivileged = false;
// This is the first time we have seen this package name under this uid,
// so let's make sure it is valid.
if (uid != 0) {
final long ident = Binder.clearCallingIdentity();
try {
int pkgUid = -1;
try {
ApplicationInfo appInfo = ActivityThread.getPackageManager()
.getApplicationInfo(packageName,
PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.getUserId(uid));
if (appInfo != null) {
pkgUid = appInfo.uid;
isPrivileged = (appInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
} else {
if ("media".equals(packageName)) {
pkgUid = Process.MEDIA_UID;
isPrivileged = false;
} else if ("audioserver".equals(packageName)) {
pkgUid = Process.AUDIOSERVER_UID;
isPrivileged = false;
} else if ("cameraserver".equals(packageName)) {
pkgUid = Process.CAMERASERVER_UID;
isPrivileged = false;
}
}
} catch (RemoteException e) {
Slog.w(TAG, "Could not contact PackageManager", e);
}
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
// under. Abort.
RuntimeException ex = new RuntimeException("here");
ex.fillInStackTrace();
Slog.w(TAG, "Bad call: specified package " + packageName
+ " under uid " + uid + " but it is really " + pkgUid, ex);
return null;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
ops = new Ops(packageName, uidState, isPrivileged);
if(checkIfInWhitelist(packageName)){
//添加默认权限并设置为允许, 这里只加了SYTEM_ALERT_WINDOW
Op op = new Op(ops.uidState.uid, ops.packageName, AppOpsManager.OP_SYSTEM_ALERT_WINDOW);
op.mode = AppOpsManager.MODE_ALLOWED;
ops.put(op.op, op);
}
uidState.pkgOps.put(packageName, ops);
}
return ops;
}

//allow special package for some permission
//把需要添加默认权限的应用包名加入到白名单中.
private boolean checkIfInWhitelist(String pkg){
if("com.android.testapp".equals(pkg)){
return true;
}
//....更多应用
return false;
}

相关代码
APP安装后, 权限为默认值, 即未变更, 默认值:
frameworks/base/core/java/android/app/AppOpsManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public static final int OP_NONE = -1;
//..........
/** @hide */
public static final int OP_WRITE_SETTINGS = 23;
/** @hide */
public static final int OP_SYSTEM_ALERT_WINDOW = 24;

/**
* This specifies the default mode for each operation.
*/
private static int[] sOpDefaultMode = new int[] {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_WRITE_SMS
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_DEFAULT, // OP_WRITE_SETTINGS
AppOpsManager.MODE_DEFAULT, // OP_SYSTEM_ALERT_WINDOW
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ERRORED, // OP_MOCK_LOCATION
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED, // OP_RUN_IN_BACKGROUND
};

AppOpsManager.MODE_DEFAULT, // OP_SYSTEM_ALERT_WINDOW
设置中获取应用的权限
packages/apps/Settings/src/com/android/settings/applications/AppStateAppOpsBridge.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public PermissionState getPermissionInfo(String pkg, int uid) {
PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle
.getUserId(uid)));
try {
permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg,
PackageManager.GET_PERMISSIONS | PackageManager.MATCH_UNINSTALLED_PACKAGES,
permissionState.userHandle.getIdentifier());
// Check static permission state (whatever that is declared in package manifest)
String[] requestedPermissions = permissionState.packageInfo.requestedPermissions;
int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (doesAnyPermissionMatch(requestedPermissions[i], mPermissions)) {
permissionState.permissionDeclared = true;
if ((permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
permissionState.staticPermissionGranted = true;
break;
}
}
}
}
// Check app op state.
List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes);
if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
permissionState.appOpMode = ops.get(0).getOps().get(0).getMode();
}
} catch (RemoteException e) {
Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
}
return permissionState;
}

若未变更设置项, mAppOpsManager.getOpsForPackage将返回空
若已变更, 则返回变更后的值

Android权限管理与AppOpsManager
————————————————
版权声明:本文为CSDN博主「ansondroider」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ansondroider/article/details/106758688