Android13 授予特殊应用权限代码
文章目录
一、前言
Android 权限类别有啥,网上很多是说有普通权限和危险权限,其实是不准确的。
Android 权限分为普通权限,动态权限,特殊权限,私有权限。
几种权限的简单说明:
权限的使用都要在AndroidManifest.xml中声明就行,即使是系统应用也是要声明后才能获取到。
1、普通权限: 不用申请直接能获取到;
比如:INTERNET 网络权限
2、动态权限:普通应用需要动态申请,系统应用直接能获取到;
比如:WRITE_EXTERNAL_STORAGE 读写权限
3、私有权限:也叫特殊权限,声明包名和权限(系统应用才能使用),如果不声明系统无法正确启动
在 frameworks\base\data\etc\privapp-permissions-platform.xml
比如:RECEIVE_WIFI_CREDENTIAL_CHANGE wifi凭证更改监听 和CHANGE_OVERLAY_PACKAGES overlay配置权限
4、特殊应用权限:普通应用需要向系统申请(和动态申请不一样),系统应用直接能获取到;
比如:SYSTEM_ALERT_WINDOW 悬浮框权限 和 FINE_LOCATION 定位权限
私有权限和特殊应用权限比较容易弄混,
私有权限影响比较大会导致系统一直重启,特殊应用权限最多导致应用崩溃。
原生设置Settings上只有动态权限和特殊应用权限的管理显示。
二、授予特殊权限代码
Android中最常用的特殊应用权限就是悬浮框权限;
1、系统签名应用代码中设置某个应用获取悬浮框权限
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
private void initOppPermission(Context context) {
try {
setPackageAppOpsPermission(context, "com.android.bluetooth", AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
} catch (Exception e) {
e.printStackTrace();
}
}
//设置特殊权限通过
private void setPackageAppOpsPermission(Context context, String packageName, String opsString) {
boolean isNeedSetPermission = SystemProperties.getBoolean("persist.sys.debug.need_ops_permission", true);
DebugLog.debug("isNeedSetPermission = " + isNeedSetPermission);
if (!isNeedSetPermission) {
return;
}
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
PackageManager manager = context.getPackageManager();
int uid = 1;
try {
ApplicationInfo packageInfo = manager.getApplicationInfo(packageName, 0);
uid = packageInfo.uid;
} catch (Exception e) {
e.printStackTrace();
return;
}
DebugLog.debug("uid = " + uid);
mAppOps.setUidMode(opsString, uid, AppOpsManager.MODE_ALLOWED);
}
上面是设置蓝牙应用获取悬浮框权限;
Android13 之后,蓝牙签名应用不再是系统应用,
标识是android.uid.bluetooth,不拥有系统签名权限。
开发的方案中系统是去除通知栏的,想要自己接收到蓝牙文件,就要自己弹框接收处理文件。
所以需要悬浮框权限必须手动申请通过系统应用授予权限或者系统代码给它权限。
这个给与权限的代码是从Android13的Settings中研究后抽取出来的,
Settings中权限管理的代码是比较复杂的,特别是特殊应用权限的管理更加复杂;
Android14 的Settings权限管理相关的很多代码都是用kt文件写的,现在估计都看不太明白了。
2、普通应用申请悬浮框权限
Android11 之后不一定行了。
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class PermissionHelper {
// 悬浮窗权限的请求码
private static final int REQUEST_FLOAT_WINDOW_PERMISSION = 1;
// 检查悬浮窗权限是否被授予
public static boolean checkFloatWindowPermission(Context context) {
return ActivityCompat.checkSelfPermission(context,
android.Manifest.permission.SYSTEM_ALERT_WINDOW) == PackageManager.PERMISSION_GRANTED;
}
// 请求悬浮窗权限
public static void requestFloatWindowPermission(Activity activity) {
if (!checkFloatWindowPermission(activity)) {
// 如果系统小于6.0,直接返回false
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
// 此处处理低于6.0版本的系统
return;
}
// 请求权限
ActivityCompat.requestPermissions(activity,
new String[]{android.Manifest.permission.SYSTEM_ALERT_WINDOW},
REQUEST_FLOAT_WINDOW_PERMISSION);
}
}
}
你可以在需要的地方调用requestFloatWindowPermission(Activity activity)方法,例如在Activity的onCreate方法中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PermissionHelper.requestFloatWindowPermission(this);
}
在Activity中重写onRequestPermissionsResult方法来处理权限被授予的情况:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermissionHelper.REQUEST_FLOAT_WINDOW_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予,可以创建悬浮窗
Toast.makeText(this,"权限被授予",Toast.LENGTH_LONG).show();
} else {
// 权限被拒绝,不能创建悬浮窗
Toast.makeText(this,"权限被拒绝",Toast.LENGTH_LONG).show();
}
}
}
上面代码看起来就是动态权限身体代码的处理!好像没啥用!
上面这些代码都是搜索得到的,测试了一下Android11 和Android13 aml方案的代码,都是权限授予失败的,直接提示 “权限被拒绝”.
这里的授予权限其实就是通过系统监听到权限申请后给与的权限。
估计是系统或者原生Settings未处理这个请求导致。
再搜索发现还有其他方式:
// 悬浮窗权限的请求码
private static final int REQUEST_FLOAT_WINDOW_PERMISSION = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestFloatWindowPermission(this);
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "向Settings申请权限",Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_FLOAT_WINDOW_PERMISSION);
} else {
Toast.makeText(this, "以获取到权限",Toast.LENGTH_LONG).show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_FLOAT_WINDOW_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予,可以创建悬浮窗
Toast.makeText(this,"权限被授予",Toast.LENGTH_LONG).show();
} else {
// 权限被拒绝,不能创建悬浮窗
Toast.makeText(this,"权限被拒绝",Toast.LENGTH_LONG).show();
}
}
}
上面的代码在界面启动后是会跳转到悬浮框权限管理界面。
但是我发现Android Studio的应用声明了悬浮框权限,在Settings设置中还是没有看到可以授予悬浮框权限的选项。
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
tools:ignore="ReservedSystemPermission" />
dumpsys命令查看应用的权限信息:
console:/ # dumpsys package com.demo.listdemo| grep -i permission
installPermissionsFixed=true
requested permissions:
android.permission.POST_NOTIFICATIONS
runtime permissions:
android.permission.POST_NOTIFICATIONS: granted=true, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
com.android.permissioncontroller:
console:/ #
console:/ #dumpsys package com.demo.listdemo| grep -i SYSTEM_ALERT_WINDOW
console:/ #
从过滤的信息查看是没有悬浮框权限声明的!
其他系统签名应用,申请后,是查看到正常权限授予的。
比如:
console:/ # dumpsys package com.skg.filemanager | grep SYSTEM_ALERT_WINDOW
android.permission.SYSTEM_ALERT_WINDOW
android.permission.SYSTEM_ALERT_WINDOW: granted=true
android.permission.SYSTEM_ALERT_WINDOW: granted=true
console:/ #
第一行是应用申请的权限,后面是显示是否授予的情况。
难道要系统应用或系统签名应用才能申请到悬浮框权限了??
后面有验证测试情况说明。
三、其他
1、Android13 授予特殊权限代码小结:
系统签名android.uid.system应用,声明了特殊权限就可以获取到。
其他应用,就要手动授予;或者系统流程中授予,或者系统类用代码授予。
系统代码或者系统应用赋予某个apk 特殊权限的代码:
// opsString 是特殊权限在AppOpsManager 里面定义的对应的字符串
// uid 应用的uid值
AppOpsManager.setUidMode(opsString, uid, AppOpsManager.MODE_ALLOWED);
//在系统源码中,有的特殊权限是用 setMode 方法设置的
mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid,
packageName, AppOpsManager.MODE_ALLOWED);
有的方案遇到使用 setMode 未生效,使用 setUidMode 才生效的情况。
所以这两个方法的第一个参数是不同的,setMode第一个参数是一个int值;
setMode 方法需要设置packageName。
2、有些应用授予了权限但是dumpsys还是显示授予情况为 false
Android14 的蓝牙应用在原生Settings上显示已获取到悬浮框权限,但是dumpsy未查询到
console:/ # dumpsys package com.android.bluetooth | grep -i SYSTEM_ALERT_WINDO
android.permission.SYSTEM_ALERT_WINDOW
android.permission.SYSTEM_ALERT_WINDOW: granted=false
android.permission.SYSTEM_ALERT_WINDOW: granted=false
console:/ #
console:/ # dumpsys package com.android.bluetooth | grep -i appid
appId=1002
appId=1002
console:/ #
蓝牙应用已经被我在系统代码中授予了悬浮框权限的!
真奇怪,Android13 上是显示授予 true的,Android14中却显示为false。
蓝牙应用虽然是系统应用,但是不是系统签名应用,是蓝牙签名应用。
所以蓝牙应用权限并不是很大,但是应该比普通系统应用权限大,
但是没有系统签名应用的权限大,蓝牙签名权限具体有什么什么差别还未研究过。
Android 蓝牙应用签名区别:
//Android 的蓝牙应用是蓝牙签名
android:sharedUserId="android.uid.bluetooth"
//Android 系统签名应用
android:sharedUserId="android.uid.system"
如果强制把蓝牙应用的蓝牙签名改成系统签名,
有些需要EDLA(谷歌需求)认证的项目就会有问题。
源码和生成apk 的目前区别:
//Android11 的蓝牙应用 :
源码目录在: packages/apps/Bluetooth
生成的apk在:system/app/Bluetooth
//Android14 的蓝牙应用
源码目录在: packages/modules/Bluetooth
生成的apk在:/apex/com.android.btservices/app/Bluetooth@UP1A.231105.001.B2/Bluetooth.apk
3、普通、系统应用和系统签名应用声明悬浮框权限的区别
(1)Android14 上:
普通应用上面提到了,声明了也没法给到悬浮框权限。
adb查看系统应用的悬浮框权限情况:
130|console:/ # //(1)查看当前应用的权限情况
130|console:/ # dumpsys package com.demo.listdemo | grep -i permission
installPermissionsFixed=false
declared permissions:
android.permission.SYSTEM_ALERT_WINDOW: prot=normal
requested permissions:
android.permission.POST_NOTIFICATIONS
runtime permissions:
android.permission.POST_NOTIFICATIONS: granted=true, flags=[ SYSTEM_FIXED|GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
com.android.permissioncontroller:
console:/ #
console:/ # //(2)apk路径
console:/ # dumpsys package com.demo.listdemo | grep -i path
codePath=/system/priv-app/Demo
resourcePath=/system/priv-app/Demo
path: /system/priv-app/Demo/Demo.apk
console:/ # //(3)应用的appid,可以查看到是否是系统签名,1000是系统签名
console:/ # dumpsys package com.demo.listdemo | grep -i id
android.intent.action.MAIN:
Action: "android.intent.action.MAIN"
Category: "android.intent.category.LAUNCHER"
appId=10042
console:/ #
console:/ #
已经把apk放到 系统目录:priv-app/Demo,成为了系统应用;
但是发现只有声明那里有悬浮框权限,授予情况是没有显示的;
代码中弹框也是无法授予悬浮框权限的。
给这个应用加上系统签名(android.uid.system)试试。
系统签名应用的权限情况:
和上面同一个应用,只是添加了system签名权限。
//(1)查看该应用的appId,1000 表示系统签名应用
console:/ # dumpsys package com.demo.listdemo | grep -i appid
appId=1000
appId=1000
console:/ #
console:/ #// (2)查看是否获取到悬浮框权限
console:/ #dumpsys package com.demo.listdemo | grep -i SYSTEM_ALERT_WINDOW
android.permission.SYSTEM_ALERT_WINDOW: prot=normal
android.permission.SYSTEM_ALERT_WINDOW: granted=true
android.permission.SYSTEM_ALERT_WINDOW: granted=true
console:/ #
console:/ # //(3)查看该应用的所有权限
console:/ # dumpsys package com.demo.listdemo | grep -i permission
installPermissionsFixed=true
declared permissions:
android.permission.SYSTEM_ALERT_WINDOW: prot=normal
requested permissions:
android.permission.POST_NOTIFICATIONS
install permissions:
android.permission.REAL_GET_TASKS: granted=true
android.permission.REMOTE_AUDIO_PLAYBACK: granted=true
android.permission.DOWNLOAD_WITHOUT_NOTIFICATION: granted=true
android.permission.ACCESS_GPU_SERVICE: granted=true
android.permission.REGISTER_WINDOW_MANAGER_LISTENERS: granted=true
android.permission.WRITE_SETTINGS: granted=true
android.permission.CONTROL_KEYGUARD: granted=true
android.permission.SYSTEM_ALERT_WINDOW: granted=true // SYSTEM_ALERT_WINDOW 被授予
...
console:/ #
可以看到给这个应用赋予了系统签名后,就可以获取到悬浮框权限和其他很多权限。
主界面代码也是吐司提示已获取到悬浮框权限的。
我这边不能100% 确定:只有某些系统应用或者系统签名应用才可以申请到悬浮框权限;
因为目前开发的系统对权限的控制是有一定的适配的;
所以不太确定是Android14系统原生会这样还是适配后导致的。
(2)Android11 上:
Android11 上应用uid查看的是userId,而不是 appId。
普通应用:
console:/ #
console:/ # dumpsys package com.demo.listdemo | grep path
path: /data/app/~~ZCTL3S1kYt7BgSa9-x1M9g==/com.demo.listdemo-0vU4jx3t6Vwv55tTtGuOgg==/base.apk
console:/ # dumpsys package com.demo.listdemo | grep -i permission
installPermissionsFixed=true
runtime permissions:
com.android.permissioncontroller:
console:/ # dumpsys package com.demo.listdemo | grep -i userid
userId=10052
console:/ #
从上面信息可以看到是普通的安装目录,没看到什么权限信息,userId也是普通应用的uid。
一样的未显示悬浮框权限,原生设置权限管理也看不到这个应用的悬浮框权限,真奇怪,我还以为可以申请的!
系统应用:
apk试了几次,push到 /system/priv-app/Demo/下面
但是重启后,系统未安装这个应用!无法查看具体情况。
估计也是没有默认给权限的。
我记得之前的一些非系统签名应用是可以授予悬浮框权限的。
系统签名应用:
console:/ #
130|console:/ # dumpsys package com.demo.listdemo | grep path
path: /data/app/~~ODurfPp13MvbmaDOrNMu5A==/com.demo.listdemo-iNQy6MjPURB4V3F1clRDig==/base.apk
console:/ #
console:/ # dumpsys package com.demo.listdemo | grep -i userid
userId=1000
userId=1000
console:/ # dumpsys package com.demo.listdemo | grep -i SYSTEM_ALERT_WINDOW
android.permission.SYSTEM_ALERT_WINDOW: prot=normal
android.permission.SYSTEM_ALERT_WINDOW: granted=true
android.permission.SYSTEM_ALERT_WINDOW: granted=true
console:/ #
console:/ #
系统签名应用,不用放到system/priv-app下,安装上去也是可以获取系统签名权限的。
从上面信息看,确实是系统签名应用,并且获取到了悬浮框权限的。
所以有可能是要系统签名才能获取到悬浮框权限了。
上面的信息提供做一个参考吧。
在不同的Android版本和系统环境下可能会出现不同的情况。
4、Android 应用添加系统签名权限的几种方式实现介绍
Android 应用添加系统签名就能获取到系统权限调用一些系统接口,
添加系统签名的方式主要包括:
在Android Studio中配置签名文件生成apk 和 在源码目录编译添加系统签名生成apk。
下面介绍的都是一些基础的签名知识,后续延伸介绍相关权限内容。
https://blog.csdn.net/wenzhi20102321/article/details/136024007