需求
有第三方应用启动会请求悬浮窗权限,因为代码中没有做规避,会弹出请求窗口,点击确认后还要跳到设置中进行授权确认.有点麻烦,想去掉请求窗口,默认授予悬浮窗权限给APP.
悬浮窗默认实现原理
AppOpsManager 是Google在Android4.3里面引进的应用程序操作(权限)的管理类,核心实现类为AppOpsService,这里主要是修改AppOpsService.
在framework中,将某一权限称为Op,即operation,
原理是在上层APP启动请求权限时,判定是请求悬浮窗,如果未授权则强制设置为可修改,并把悬浮窗权限写入到Op中.
int checkop(String op,int uid,String packageName)判断应用是否含有某个权限
AppOpsManager.checkOp()最终会调用到AppOpsService.checkOperationUnchecked(),
先看下此函数的实现
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
boolean raw) {
RestrictionBypass bypass;
try {
bypass = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
Slog.e(TAG, "checkOperation", e);
return AppOpsManager.opToDefaultMode(code);
}
if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
return AppOpsManager.MODE_IGNORED;
}
synchronized (this) {
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null && uidState.opModes != null
&& uidState.opModes.indexOfKey(code) >= 0) {
final int rawMode = uidState.opModes.get(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, bypass, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
return raw ? op.mode : op.evalMode();
}
}
可以看到这句Op op = getOpLocked(code, uid, packageName, null, bypass, false)
类似noteOperationUnchecked函数也会调到这句getOpLocked,我们主要是修改这个getOpLocked函数.
根据包名授予悬浮窗权限
在Android11上实测通过,理论上安卓7到13都是可以的,Android13代码与11一样,只是行数不一样.
修改
frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
ff --git a/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java b/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
index c81a306b27..0d2a29a20c 100644
--- a/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3960,6 +3960,14 @@ public class AppOpsService extends IAppOpsService.Stub {
return ops;
}
+ // 权限白名单应用,用包名做判断
+ private boolean isInWhiteList(String pkg) {
+ if ("com.test".equals(pkg)){
+ return true;
+ }
+ return false;
+ }
+
private void scheduleWriteLocked() {
if (!mWriteScheduled) {
mWriteScheduled = true;
@@ -3990,10 +3998,39 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
@Nullable String attributionTag, @Nullable RestrictionBypass bypass, boolean edit) {
+
+ boolean isWhiteList = false;
+ if (AppOpsManager.OP_SYSTEM_ALERT_WINDOW == code && isInWhiteList(packageName)) {
+ isWhiteList = true;
+ edit = true; // 设置为op可修改,否则后面会返回null
+ }
+
Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, edit);
if (ops == null) {
return null;
}
+
+ if (isWhiteList) {
+ boolean write = false;
+ Op op = ops.get(code);
+ if (op == null) { // 为null则写
+ if (ops.uidState != null) {
+ op = new Op(ops.uidState, ops.packageName, code, uid);
+ write = true;
+ }
+ } else if (op.mode != AppOpsManager.MODE_ALLOWED) { // 未授权则写
+ write = true;
+ }
+
+ if (write) {
+ op.mode = AppOpsManager.MODE_ALLOWED; // 强制设置为授权了
+ ops.put(code, op);
+ if (ops.uidState != null && ops.uidState.pkgOps != null) {
+ ops.uidState.pkgOps.put(packageName, ops);
+ }
+ }
+ }
+
return getOpLocked(ops, code, uid, edit);
}
完整代码如下
private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
@Nullable String attributionTag, @Nullable RestrictionBypass bypass, boolean edit) {
boolean isWhiteList = false;
if (AppOpsManager.OP_SYSTEM_ALERT_WINDOW == code && isInWhiteList(packageName)) {
isWhiteList = true;
edit = true;
}
Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, edit);
if (ops == null) {
return null;
}
if (isWhiteList) {
boolean write = false;
Op op = ops.get(code);
if (op == null) {
if (ops.uidState != null) {
op = new Op(ops.uidState, ops.packageName, code, uid);
write = true;
}
} else if (op.mode != AppOpsManager.MODE_ALLOWED) {
write = true;
}
if (write) {
op.mode = AppOpsManager.MODE_ALLOWED;
ops.put(code, op);
if (ops.uidState != null && ops.uidState.pkgOps != null) {
ops.uidState.pkgOps.put(packageName, ops);
}
}
}
return getOpLocked(ops, code, uid, edit);
}
private boolean isInWhiteList(String pkg) {
if ("com.test".equals(pkg)){
return true;
}
return false;
}
// 作者 帅得不敢出门
安卓悬浮窗权限
安卓悬浮窗权限是允许应用在其他应用之上展示内容的特殊权限,在 Android 开发中具有重要作用
权限简介
- 相关权限:主要涉及 SYSTEM_ALERT_WINDOW 权限,在 Android 6.0(API Level 23)及以上版本中,应用需要请求用户授权才能使用该权限来显示悬浮窗。
- 作用:使得应用能够在不影响其他应用正常运行的情况下,以悬浮的形式在屏幕上展示信息或提供交互界面,例如实现聊天悬浮窗、工具悬浮窗、视频小窗播放等功能
申请步骤
- 添加权限声明:在 AndroidManifest.xml 文件中添加 <uses - permission android:name=“android.permission.SYSTEM_ALERT_WINDOW”/>,声明应用需要使用悬浮窗权限。
- 检查权限:在代码中通过 Settings.canDrawOverlays(context) 方法检查应用是否已获得悬浮窗权限,该方法在 Android 6.0 及以上版本可用。对于低于 Android 6.0 的版本,通常默认应用具有该权限。
- 请求权限:如果检查发现应用没有悬浮窗权限,可使用 Intent 引导用户前往设置页面进行授权。例如,使用 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) 创建意图,并通过 startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION) 启动设置页面,其中 REQUEST_CODE_OVERLAY_PERMISSION 是自定义的请求码,用于在 onActivityResult 方法中处理用户返回的结果。
作者:帅得不敢出门 原创文章谢绝转载及收录