实战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...')
相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
哈基米南北3 天前
ViewRootImpl 事件分发责任链分析
源码
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas