实战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")
    }
}

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

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

相关推荐
alexhilton6 小时前
使用Android Archive进行打包
android·kotlin·android jetpack
badhope8 小时前
做了几年安卓开发,这些坑我帮你踩过了
android·android studio
逐光老顽童2 天前
Java 与 Kotlin 混合开发避坑指南:30 个真实案例实录
android·kotlin
爱勇宝3 天前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
Yeyu3 天前
刷新一帧的艺术:invalidate / postInvalidate / postInvalidateOnAnimation全解析
android
潘潘潘3 天前
Android OTA 升级原理和流程介绍
android
plainGeekDev3 天前
null 判断 → Kotlin 可空类型
android·java·kotlin
plainGeekDev3 天前
getter/setter → Kotlin 属性
android·java·kotlin
YXL1111YXL3 天前
Handler 消息回收与协程异步执行的时序陷阱
android