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

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

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

相关推荐
漏刻有时2 小时前
PHP GeoJSON转PNG地图渲染程序开发笔记、源码解读、问题复盘与整改方案
android·笔记·php
-SOLO-3 小时前
解决VMware 显示比例被重置的问题
android
alexhilton4 小时前
探究Android Views、Flutter和Compose如何渲染你的UI
android·kotlin·android jetpack
Lesile7 小时前
Android:Hilt框架入门 · 在ViewUI和ComposeUI下的应用
android·android jetpack
用户423816229077 小时前
Android 16 WebView 页面顶部挖孔/通知栏不能显示UI问题排查
android
weixin_727535628 小时前
Loop 已死,Graph 新生:AI 工作流的范式革命
android·人工智能·rxjava
严同学正在努力9 小时前
从备份到恢复:我用 30 分钟恢复了误删的核心业务表
android·java·数据库·ai
梦想三三9 小时前
LangChain Output Parser 实战:从字符串到结构化数据的完整指南
android·服务器·langchain·github·uv
爱笑鱼11 小时前
Binder(八):远端进程死了,BinderProxy 为什么还能收到通知?
android
Android-Flutter12 小时前
Kotlin 冷流与热流详解
android·kotlin