平台
RK3288 + Android 7.1
需求
在高版本的SDK中, 第三方应用申请悬浮窗的权限受到了过一步的限制.
除了要在应用中声明对权限的申请:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
还需要打开设置中的权限:
应用可以通过代码检测权限是否已获取:
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"); }
有可能会抛出异常:
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
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
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
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