Android系统 权限组管理和兼容性

简介: Android系统 权限组管理和兼容性

Android系统 文件访问权限笔记

Android系统 理解/sys/目录权限和UID和GID?

Android系统 应用存储路径与权限

Android系统 自定义系统和应用权限

Android系统 AppOps默认授予应用相应的权限

Android系统 权限组管理和兼容性


参考学习: Android 中的权限

Android权限的目的是为了保护用户数据和设备。Android系统中有两种类型的权限普通权限和危险权限 , 为了方便用户管理危险权限又分为不同的权限组 从Android 6.0开始,又引入了运行时权限机制。除了普通权限和危险权限之外 存在系统级别或签名级别的特殊权限。反正不同Android版本间的权限都有差异 越新版本权限越严格(S/B)。

核心点 详细描述
权限目的 保护用户的数据和设备安全。
权限类型 - 普通权限: 不会对用户隐私或设备造成大的影响。如访问网络。
- 危险权限: 可能对用户隐私或设备造成大的影响。如读取联系人。
权限组 危险权限被分为不同的权限组,如电话、存储、位置等。授予组内任一权限会自动授予该组其他权限。
运行时权限 从Android 6.0开始,应用在使用危险权限时需在运行时请求用户授权,而非安装时。
特殊权限 系统级别或签名级别的权限,如修改系统设置。通常只由系统应用或相同签名的应用使用,但有例外情况。
版本差异 不同Android版本间的权限有差异,比如从6.0引入运行时权限,10.0开始限制后台位置访问。

权限组

权限组的概念和作用

权限组是一种将相关的危险权限进行分类和分组的方式,每个危险权限都属于一个或多个权限组,如存储、相机等。权限组的作用是方便用户管理危险权限,减少用户频繁地处理授权请求,也让用户更清楚地知道应用需要什么样的功能。

Android系统中有以下几个权限组:

  • 存储:允许应用访问外部存储空间,如读取和写入文件、获取存储信息等。
  • 相机:允许应用使用设备的相机功能,如拍照、录像、获取相机参数等。

每个权限组中包含了一个或多个危险权限,可以在这里查看每个权限组中具体包含哪些危险权限。

如何定义和使用权限组

在开发应用时,需要在AndroidManifest.xml文件中声明需要的权限,无论是普通权限还是危险权限。可以使用<uses-permission>标签来声明一个单独的权限,也可以使用<uses-permission-group>标签来声明一个整个的权限组。例如:

<!-- 声明单独的危险权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 声明整个的权限组 -->
<uses-permission-group android:name="android.permission-group.CAMERA" />
<uses-permission-group android:name="android.permission-group.MICROPHONE" />

如果声明了一个整个的权限组,那么不需要再声明该组中的单独的危险权限了。但是如果只声明了该组中的某些危险权限,那么就只能使用这些声明过的危险权限,而不能使用该组中其他没有声明过的危险权限。例如:

<!-- 声明存储组中的写入外部存储危险权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 不能使用存储组中其他没有声明过的危险权限,如读取外部存储 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

在运行时,需要向用户请求授权需要的危险权限,可以使用ActivityCompat.requestPermissions()方法来请求一个或多个危险权限,也可以使用ActivityCompat.requestPermissionsFromGroup()方法来请求一个整个的权限组。例如:

// 请求单独的危险权限
ActivityCompat.requestPermissions(this,
        new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
        REQUEST_CODE);
// 请求整个的权限组
ActivityCompat.requestPermissionsFromGroup(this,
        Manifest.permission-group.CAMERA,
        REQUEST_CODE);

如果请求了一个整个的权限组,那么用户只需要一次授权就可以授予该组中的所有危险权限。但是如果只请求了该组中的某些危险权限,那么用户只会授予这些请求过的危险权限,而不会授予该组中其他没有请求过的危险权限。例如:

// 请求存储组中的写入外部存储危险权限
ActivityCompat.requestPermissions(this,
        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
        REQUEST_CODE);
// 用户只会授予写入外部存储危险权限,而不会授予存储组中其他没有请求过的危险权限,如读取外部存储

在请求权限时,需要提供一个请求码,用于标识请求。在用户做出授权选择后,可以在onRequestPermissionsResult()方法中接收用户的选择结果,并根据结果进行相应的处理。例如:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    // 判断请求码是否匹配
    if (requestCode == REQUEST_CODE) {
        // 判断授权结果是否成功
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 授权成功,执行相应的操作
            Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show();
        } else {
            // 授权失败,提示用户或者退出应用
            Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
        }
    }
}

在使用危险权限时,需要先检查是否已经获得了该权限,可以使用ContextCompat.checkSelfPermission()方法来检查某个单独的危险权限,也可以使用ContextCompat.checkPermissionGroup()方法来检查某个整个的权限组。例如:

// 检查单独的危险权限
int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
int audioPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);
// 检查整个的权限组
int cameraGroup = ContextCompat.checkPermissionGroup(this, Manifest.permission-group.CAMERA);
int microphoneGroup = ContextCompat.checkPermissionGroup(this, Manifest.permission-group.MICROPHONE);

如果检查结果为PackageManager.PERMISSION_GRANTED,则表示已经获得了该权限或者该组中的所有权限,可以直接使用。如果检查结果为PackageManager.PERMISSION_DENIED,则表示没有获得了该权限或者该组中的任何一个权限,需要向用户请求授权。例如:

// 检查存储组中的写入外部存储危险权限
int writePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (writePermission == PackageManager.PERMISSION_GRANTED) {
    // 已经获得了写入外部存储危险权限,可以直接使用
    saveFileToExternalStorage();
} else {
    // 没有获得了写入外部存储危险权限,需要向用户请求授权
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
            REQUEST_CODE);
}

权限管理

Android系统的权限管理机制

Android的权限管理机制涉及应用如何请求、获得和使用权限,以及用户如何授予和管理这些权限。早期版本(6.0以下)在安装时请求权限,而6.0及以上版本在运行时请求危险权限。从11.0开始,用户可以授予应用一次性的使用权,特定于当前会话。而从10.0开始,应用要在后台访问位置信息,除了需要精确位置权限外,还需要一个特殊的后台位置访问权限。

权限管理类型 描述 适用版本
安装时权限 应用在安装时展示所需权限,用户选择是否接受。权限可以在系统设置中启用或禁用,但不能单独控制。 6.0以下
运行时权限 应用在运行时请求危险权限。用户可以在系统设置中查看或修改,可以单独或整组控制。 6.0及以上
一次性权限 用户可以授予应用当前会话的一次性使用权。权限在应用退出或设备锁屏后失效。 11.0及以上
后台位置访问 应用需要特殊的后台位置访问权限才能在后台获取位置信息,否则只能在前台访问。 10.0及以上

如何在应用中实现权限管理

在开发Android应用时,为了提高功能、安全性和用户体验,开发者应仅请求必要的权限,明确告知用户为何需要某权限,尊重用户的选择和隐私,并适配不同版本的权限特性和规则。

建议 描述
只请求必要的权限 根据应用功能和需求确定所需权限,避免请求不必要或冗余的权限,以增加用户信任。
解释权限需求 在请求危险权限前,明确告知用户为何需要该权限及其用途,以减少用户疑虑。
尊重用户选择和隐私 接受用户的授权决策,无论同意或拒绝。避免强迫、诱导或反复请求。确保不滥用或泄露用户数据。
适配版本的权限特性 考虑到Android不同版本间的权限差异,如6.0的运行时权限和10.0的后台位置访问限制,确保应用在各版本上正常运行。

以确保应用在各个版本上都能正常运行。可以使用Build.VERSION.SDK_INT来判断当前设备的系统版本,并根据版本进行相应的处理。例如:

// 判断当前设备是否为6.0及以上版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    // 是6.0及以上版本,使用运行时权限机制
    // 检查是否已经获得某个危险权限
    int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
    if (cameraPermission == PackageManager.PERMISSION_GRANTED) {
        // 已经获得了危险权限,可以直接使用
        openCamera();
    } else {
        // 没有获得了危险权限,需要向用户请求授权
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CAMERA},
                REQUEST_CODE);
    }
} else {
    // 是6.0以下版本,使用安装时权限机制
    // 不需要检查或请求危险权限,可以直接使用
    openCamera();
}

权限兼容性

Android不同版本间的权限差异

Android系统在不同版本中对权限有所变化。从6.0开始,引入了运行时权限机制,要求应用在运行时请求危险权限。11.0版本引入了一次性权限,允许用户为敏感权限授予一次性使用权。从10.0开始,应用在后台访问位置信息需要特殊的权限,并引入了分区存储模式,限制应用直接访问外部存储。

特性/变化 描述 适用版本
运行时权限机制 应用在运行时请求危险权限,而非安装时。 6.0及以上
一次性权限 用户可以为敏感权限(如位置、相机)授予一次性使用权,仅在当前会话有效。 11.0及以上
后台位置访问 应用在后台访问位置需要ACCESS_BACKGROUND_LOCATION权限,而非仅ACCESS_FINE_LOCATION 10.0及以上
分区存储 应用只能访问自己的沙盒存储,访问其他存储需通过特定API或权限。 10.0及以上

如何处理权限的兼容性问题

开发Android应用时,为确保在各版本正常运行,开发者需考虑权限差异。建议设置最新的目标SDK版本,根据设备系统版本判断权限需求,并使用Jetpack兼容库简化权限处理。

建议 描述 示例/备注
设置目标SDK版本 在AndroidManifest.xml中设置应用的目标SDK版本,尽量选择最新版本以适配新权限特性。 若目标SDK低于6.0,应用使用安装时权限机制,可能降低透明度和信任度。
判断当前系统版本 在请求/使用权限前,检查设备系统版本以确定权限需求。 使用Build.VERSION.SDK_INT判断。例如,10.0及以上版本需请求后台位置访问权限。
使用兼容库 利用Android Jetpack的兼容库简化不同版本间的权限处理。 使用PermissionCompat请求危险权限,无需判断版本。

可以使用Build.VERSION.SDK_INT来判断当前设备的系统版本,并根据版本进行相应的处理。例如:

// 判断当前设备是否为10.0及以上版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    // 是10.0及以上版本,需要请求后台位置访问危险权限
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
            REQUEST_CODE);
} else {
    // 是10.0以下版本,不需要请求后台位置访问危险权限
    // 可以直接使用位置信息
    getLocation();
}

可以使用Android Jetpack中提供的一些兼容库来简化和统一对于不同版本间权限差异的处理。例如,可以使用PermissionCompat类来请求一个或多个危险权限,而不需要判断当前设备是否为6.0及以上版本。例如:

// 使用PermissionCompat类来请求危险权限
PermissionCompat.requestPermissions(this,
        new String[]{Manifest.permission.CAMERA},
        REQUEST_CODE);

如何请求和使用特殊权限

在开发应用时,如果需要使用某些特殊权限来实现一些高级或者特殊的功能,需要以下几个步骤:

  • 声明特殊权限:需要在AndroidManifest.xml文件中声明需要使用的特殊权限,使用<uses-permission>标签来声明一个单独的特殊权限。例如:
<!-- 声明修改系统设置特殊权限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
  • 检查特殊权限:需要在使用某个特殊权限之前,检查是否已经获得了该特殊权限,可以使用Settings.System.canWrite()方法来检查修改系统设置特殊权限,使用Settings.canDrawOverlays()方法来检查悬浮窗特殊权限,使用DevicePolicyManager.isAdminActive()方法来检查设备管理器特殊权限,使用NotificationManager.isNotificationPolicyAccessGranted()方法来检查通知监听特殊权限,使用AccessibilityManager.isEnabled()方法来检查辅助功能特殊权限,使用PackageManager.canRequestPackageInstalls()方法来检查安装未知来源应用特殊权限。例如:
// 检查修改系统设置特殊权限
boolean canWrite = Settings.System.canWrite(this);
// 检查悬浮窗特殊权限
boolean canDraw = Settings.canDrawOverlays(this);
// 检查设备管理器特殊权限
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(this, MyDeviceAdminReceiver.class);
boolean isAdminActive = devicePolicyManager.isAdminActive(componentName);
// 检查通知监听特殊权限
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
boolean isNotificationAccessGranted = notificationManager.isNotificationPolicyAccessGranted();
// 检查辅助功能特殊权限
AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
boolean isAccessibilityEnabled = accessibilityManager.isEnabled();
// 检查安装未知来源应用特殊权限
PackageManager packageManager = getPackageManager();
boolean canRequestPackageInstalls = packageManager.canRequestPackageInstalls();

如果检查结果为true,则表示已经获得了该特殊权限,可以直接使用。如果检查结果为false,则表示没有获得了该特殊权限,需要向用户请求授权。

  • 请求特殊权限:需要在运行时向用户请求授权需要的特殊权限,以使用startActivityForResult()方法来启动一个系统提供的设置界面,让用户在该界面中授予或拒绝请求。需要提供一个请求码,用于标识请求。在用户做出授权选择后,可以在onActivityResult()方法中接收用户的选择结果,并根据结果进行相应的处理。例如:
// 请求修改系统设置特殊权限
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
// 请求悬浮窗特殊权限
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
// 请求设备管理器特殊权限
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "请授予设备管理器权限");
startActivityForResult(intent, REQUEST_CODE);
// 请求通知监听特殊权限
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivityForResult(intent, REQUEST_CODE);
// 请求辅助功能特殊权限
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivityForResult(intent, REQUEST_CODE);
// 请求安装未知来源应用特殊权限
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    // 判断请求码是否匹配
    if (requestCode == REQUEST_CODE) {
        // 判断授权结果是否成功
        if (canWrite || canDraw || isAdminActive || isNotificationAccessGranted || isAccessibilityEnabled || canRequestPackageInstalls) {
            // 授权成功,执行相应的操作
            Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show();
        } else {
            // 授权失败,提示用户或者退出应用
            Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
        }
    }
}

希望这篇文章能对您有所帮助。如果还有其他问题或建议,请留言与私信。

相关文章
|
2月前
|
Android开发
Android 如何将定制的Launcher成为系统中唯一的Launcher
Android 如何将定制的Launcher成为系统中唯一的Launcher
46 2
|
14天前
|
存储 监控 调度
Android系统服务:WMS、AMS相关知识
参考文献 Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析 Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析 Android窗口管理服务WindowManagerService对输入法窗口(Input Method Window)的管理分析 Android窗口管理服务WindowManagerService显示窗口动画的原理分析
|
17天前
|
Java Linux Android开发
Android面试题之说说系统的启动流程(总结)
这篇文章概述了Android系统的启动流程,从Boot Rom到Zygote进程和SystemServer的启动。init进程作为用户级别的第一个进程,负责创建文件目录、初始化服务并启动Zygote。Zygote通过预加载资源和创建Socket服务,使用fork函数生成SystemServer进程。fork过程中,子进程继承父进程大部分信息但具有独立的进程ID。Zygote预加载资源以减少后续进程的启动时间,而SystemServer启动众多服务并最终开启Launcher应用。文中还讨论了为何从Zygote而非init或SystemServer fork新进程的原因。
25 2
|
24天前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
27 2
|
6天前
|
安全 搜索推荐 Android开发
探索安卓和iOS系统的优劣与特点
在移动操作系统领域,安卓和iOS一直是最热门的两个选择。本文将探讨安卓和iOS系统的优劣与特点,帮助读者更好地了解这两个操作系统,并为选择合适的移动设备提供参考。
12 0
|
2月前
|
Android开发
【通讯录教程】苹果安卓鸿蒙系统通用,如何大批量导入手机号码到手机的通讯录,下面教你方法,只需1分钟搞定几万个号码的导入手机电话本
该文介绍了一种快速批量导入手机通讯录的方法,适用于处理大量手机号的需求,如微商管理、客户资料整理等。在QQ同步助手开始收费后,提供了免费的替代方案。步骤包括:下载批量导入软件(链接提供腾讯云盘和百度网盘地址),清空通讯录(非必需),制作符合格式的通讯录文件,并按操作系统(苹果、安卓或鸿蒙)进行导入。整个过程只需1分钟,简便快捷。
|
17天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
11 0
|
2月前
|
安全 搜索推荐 物联网
构建未来:基于Android的智能物联网家居系统
【5月更文挑战第15天】 在快速发展的数字化时代,智能物联网(IoT)技术与移动操作系统的结合正在塑造未来家居的生活方式。特别是Android平台,以其开放性、灵活性和广泛的用户基础,成为智能家居创新的理想选择。本文将探讨如何利用Android系统构建一个高效、安全且易于扩展的智能家居控制系统,涵盖系统设计、关键技术实现以及可能面临的挑战。通过分析具体案例,我们旨在为开发者和企业提供一套可行的解决方案蓝图,以促进智能家居领域的进一步发展。
|
2月前
|
存储 缓存 Android开发
Android系统分区与升级
Android系统分区与升级
57 4
|
2月前
|
Java Shell Android开发
android 权限申请
android 权限申请
57 5