实战Android Framework: 新增一个系统服务

系统版本: Ubuntu 22.04 lts

AOSP分支: android-16.0.0_r4

本文将介绍如何在AOSP16中新增一个Java层的系统服务

一 创建功能发布标志

在最新的AOSP开发中,Google推荐使用功能发布标志来进行新功能的开发和迭代,具体可以查看这里:

功能发布标志概览

首先我们先创建一个功能发布标志,由于我们的服务将创建在android.os包下,所以我们使用现有的aconfig文件即可,如果希望使用自定义的aconfig文件,可以在上面的链接中查看如何创建自定义文件并修改Android BP文件使自定义aconfig文件生效。

打开AOSP16/frameworks/base/core/java/android/os/flags.aconfig,声明aconfig标志:

Aconfig 复制代码
flag {
    name: "jyc_battery_service_enabled"
    namespace: "jyc_power"
    description: "check battery status"
    bug: "none"
    is_exported: true
}

之后打开AOSP16/build/release/aconfig/trunk_staging/android.os目录,创建新文件jyc_battery_service_enabled_flag_values.textproto,并写入如下内容:

Aconfig 复制代码
flag_value {
  package: "android.os"
  name: "jyc_battery_service_enabled"
  state: ENABLED
  permission: READ_WRITE
}

最后由于我们新增了aconfig,所以需要部分编译一下:

shell 复制代码
. build/envsetup.sh
lunch aosp_cf_x86_64_phone-trunk_staging-userdebug
m update-api

这时,在Java代码中,就已经可以android.os包下找到对应方法,并且能看到声明的发布标志:

Java 复制代码
android.os.Flags.jycBatteryServiceEnabled()
android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED

二 新建AIDL文件

打开/AOSP16/frameworks/base/core/java/android/os,新建文件夹jycservice,并创建文件IJYCBatteryService.aidl:

Java 复制代码
package android.os.jycservice;

/** @hide **/
interface IJYCBatteryService {
    int getBattery();
}

之后进行编译:

Shell 复制代码
m framework-minus-apex

三 创建Service

打开AOSP16/frameworks/base/core/java/android/content/Context.java,新增:

Java 复制代码
/**
 * @hide
 */
@FlaggedApi(android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED)
@SystemApi
public static final String JYC_BATTERY_SERVICE = "jyc_battery";

打开AOSP16/frameworks/base/services/core/java/com/android/server,创建文件夹jycservice,并在此文件夹下创建JYCBatteryService.java:

Java 复制代码
package com.android.server.jycservice;

import android.annotation.FlaggedApi;
import android.content.Context;
import android.os.BatteryManagerInternal;
import android.os.jycservice.IJYCBatteryService;

import com.android.server.LocalServices;
import com.android.server.SystemService;

@FlaggedApi(android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED)
public class JYCBatteryService extends IJYCBatteryService.Stub {
    public static final String SERVICE_TAG = "jyc_battery";

    private Context mContext;

    public JYCBatteryService(Context context) {
        this.mContext = context;
    }

    public static final class Lifecycle extends SystemService {
        private JYCBatteryService mService;

        public Lifecycle(Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            mService = new JYCBatteryService(getContext());
            publishBinderService(Context.JYC_BATTERY_SERVICE, mService);
        }
    }

    @Override
    public int getBattery() {
        BatteryManagerInternal bmi = LocalServices.getService(BatteryManagerInternal.class);
        if (bmi != null) {
            return bmi.getBatteryLevel();
        }
        return -1;
    }
}

四 注册Service

打开AOSP16/frameworks/base/services/java/com/android/server/SystemServer.java,在startOtherServices方法中,添加:

Java 复制代码
/* Add JYCBatteryService */
if (android.os.Flags.jycBatteryServiceEnabled()) {
    t.traceBegin("StartJYCBatteryService");
    mSystemServiceManager.startService(JYCBatteryService.Lifecycle.class);
    t.traceEnd();
}
/* End Add JYCBatteryService */

五 创建对应的Manager

如果我们的Service需要开放给系统App或第三方App使用,那么与其他系统服务一样,需要一个Service对应的ServiceManager。 在AOSP16/frameworks/base/core/java/android/os下,创建文件夹jycservice,并新增文件JycBatteryManager.java:

Java 复制代码
package android.os.jycservice;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;

@SystemService(Context.JYC_BATTERY_SERVICE)
@FlaggedApi(android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED)
public class JycBatteryManager {
    private static final String TAG = "JYCBatteryManager";
    private final Context mContext;
    private final IJYCBatteryService mService;

    /** @hide **/
    @FlaggedApi(android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED)
    public JycBatteryManager(@NonNull Context context, @NonNull IJYCBatteryService service) {
        mContext = context;
        mService = service;
    }

    /**
     * 获取当前电量
     */
    @FlaggedApi(android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED)
    public int getBatteryLevel() {
        try {
            return mService.getBattery();
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in getBatteryLevel", e);
            return -1;
        }
    }
}

六 注册Manager

打开AOSP16/frameworks/base/core/java/android/app/SystemServiceRegistry.java,在registerServices方法中,新增注册逻辑:

Java 复制代码
if (android.os.Flags.jycBatteryServiceEnabled()) {
    registerService(Context.JYC_BATTERY_SERVICE, JycBatteryManager.class,
        new CachedServiceFetcher<JycBatteryManager>() {
            @Override
            public JycBatteryManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                IBinder b = ServiceManager.getServiceOrThrow(Context.JYC_BATTERY_SERVICE);
                IJYCBatteryService service = IJYCBatteryService.Stub.asInterface(b);
                return new JycBatteryManager(ctx.getOuterContext(), service);
            }});
}

由于我们新增了Service并创建了对应的manager,所以我们需要更新:

shell 复制代码
m update-api

七 配置Selinux

在Android系统中,我们的系统服务必须配置相应的selinux权限,才能生效。 打开AOSP16/system/sepolicy/private/service.te,新增如下代码:

shell 复制代码
###
### JYC Services
###
type jyc_battery_service, app_api_service, system_api_service, system_server_service, service_manager_type;

接着打开AOSP16/system/sepolicy/private/service_contexts,新增:

bash 复制代码
# JYC Service
jyc_battery                               u:object_r:jyc_battery_service:s0

然后打开AOSP16/system/sepolicy/private/system_server.te,新增:

scss 复制代码
# Add JYC Service
add_service(system_server, jyc_battery_service)

最后打开AOSP16/system/sepolicy/private/untrusted_app_all.te,新增配置允许第三方App访问我们的服务:

ini 复制代码
# Allow Other App Can Find JYCBatteryService
allow untrusted_app jyc_battery_service:service_manager find;

最后进行编译:

复制代码
m

八 验证服务

编译成功之后,运行:

Shell 复制代码
launch_cvd --daemon

等待启动成功之后,运行命令查找服务:

Shell 复制代码
adb shell service list | grep jyc_battery

之后我们会看到输出:

Shell 复制代码
190	jyc_battery: [android.os.jycservice.IJYCBatteryService]

然后我们可以尝试调用Service中的方法:

Shell 复制代码
adb shell service call jyc_battery 1

之后可以看到输出:

Shell 复制代码
Result: Parcel(	00000000 00000055   '....U...')
相关推荐
杉氧2 小时前
兼容与共生:如何在旧项目中优雅地引入 Compose?
android·架构·android jetpack
Flynt3 小时前
Room 3.0 包名重构 + KMP 迁移:我把项目升级踩了个遍
android·数据库·kotlin
杉氧3 小时前
性能优化实战:如何定位冗余重组并榨干 Compose 的每一帧性能?
android·架构·android jetpack
阿pin5 小时前
Android随笔-ATMS与AMS区别与联系
android·ams·atms
alexhilton15 小时前
将应用迁移到Navigation 3:痛点、加班和紧急修复
android·kotlin·android jetpack
杉氧20 小时前
Navigation Compose 深度实践:如何优雅地串联起你的全栈 App?
android·架构·android jetpack
雨白1 天前
指针与数组的核心机制
android
黄林晴1 天前
Room 3.0 正式发布!包名彻底重构,KMP 成为核心主线
android·android jetpack
三少爷的鞋1 天前
Kotlin 协程环境下的 DCL 懒加载:别把线程时代的经验直接搬过来
android
plainGeekDev1 天前
Gson → kotlinx.serialization
android·java·kotlin