实战Android Framework: 新增一个系统权限

系统版本: Ubuntu 22.04 lts

AOSP分支: android-16.0.0_r4

本文将介绍如何在AOSP16中新增并使用一个运行时系统权限。

注意: 本文章将基于 实战Android Framework: 新增一个系统服务 进行修改。

一 新增权限声明

首先我们要在系统的AndroidManifest.xml中新增一个权限声明,打开AOSP16/frameworks/base/core/res/AndroidManifest.xml,新增:

xml 复制代码
    <!-- Permission required to use JYCBatteryService
         <p>Protection level: dangerous
         @FlaggedApi(android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED)
    -->
    <permission android:name="android.permission.JYC_BATTERY_PERMISSION"
        android:protectionLevel="dangerous"
        android:featureFlag="android.os.jyc_battery_service_enabled" />

注意: 这里使用的featureFlag是在 实战Android Framework: 新增一个系统服务 中新增的Aconfig标志,并且我们将protectionLevel标记为了dangerous,这意味着第三方应用不光需要在自己的AndroidManifest.xml中声明这个权限,并且需要在运行时申请这个权限,想知道这个protectionLevel可以标记为哪些并且它们有什么区别可以查看 Android权限。

之后我们需要进行编译:

Shell 复制代码
m update-api

二 在系统服务中检查权限

打开在 实战Android Framework: 新增一个系统服务 中写好的JYCBatteryService,在getBattery方法中,增加权限需求:

Java 复制代码
@Override
public int getBattery() {
    mContext.enforceCallingOrSelfPermission(
            "android.permission.JYC_BATTERY_PERMISSION",
            "Need JYC_BATTERY_PERMISSION to access Jyc Battery Service");

    BatteryManagerInternal bmi = LocalServices.getService(BatteryManagerInternal.class);
    if (bmi != null) {
        return bmi.getBatteryLevel();
    }
    return -1;
}

之后进行编译即可:

Shell 复制代码
m

三 验证权限是否新增成功

编译完成之后,我们可以运行模拟器,然后输入ADB命令查看权限是否已经生效:

Shell 复制代码
adb shell pm list permissions | grep JYC_BATTERY_PERMISSION

之后我们可以看到输出:

Shell 复制代码
permission:android.permission.JYC_BATTERY_PERMISSION

这意味着我们已经成功新增了一个权限

四 在第三方App中申请权限

首先如果我们没有进行任何修改,直接使用对应方法,应用则会崩溃,Logcat中可以看到报错信息:

Log 复制代码
E  FATAL EXCEPTION: main (Fix with AI)
Process: com.jyc.testapplication, PID: 4492
java.lang.SecurityException: Need JYC_BATTERY_PERMISSION to access Jyc Battery Service: Neither user 10120 nor current process has android.permission.JYC_BATTERY_PERMISSION.

我们先修改AndroidManifest.xml,增加权限声明:

xml 复制代码
<uses-permission android:name="android.permission.JYC_BATTERY_PERMISSION" />

之后修改应用的源码:

Kotlin 复制代码
package com.jyc.testapplication

import android.content.pm.PackageManager
import android.os.Bundle
import android.os.jycservice.JycBatteryManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.content.ContextCompat
import com.jyc.testapplication.ui.theme.TestApplicationTheme

class MainActivity : ComponentActivity() {
    private val batteryLevelText = mutableStateOf("Loading...")

    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            readBatteryLevel()
        } else {
            batteryLevelText.value = "Permission Denied"
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        checkPermissionAndRead()

        enableEdgeToEdge()
        setContent {
            TestApplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = batteryLevelText.value,
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }

    private fun checkPermissionAndRead() {
        val permission = "android.permission.JYC_BATTERY_PERMISSION"
        when {
            ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
                readBatteryLevel()
            }
            else -> {
                requestPermissionLauncher.launch(permission)
            }
        }
    }

    private fun readBatteryLevel() {
        try {
            val jycManager = getSystemService("jyc_battery") as JycBatteryManager
            batteryLevelText.value = jycManager.batteryLevel.toString()
        } catch (e: Exception) {
            batteryLevelText.value = "Error: ${e.message}"
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Battery is $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    TestApplicationTheme {
        Greeting("Android")
    }
}

生成并安装应用,打开之后可以看到权限申请:

允许之后,可以看到调用成功:

相关推荐
三少爷的鞋8 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Dovis(誓平步青云)12 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码14 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程14 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程17 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程19 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程19 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程19 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓19 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程19 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer