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

182 阅读4分钟

系统版本: 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标志:

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,并写入如下内容:

flag_value {
  package: "android.os"
  name: "jyc_battery_service_enabled"
  state: ENABLED
  permission: READ_WRITE
}

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

. build/envsetup.sh
lunch aosp_cf_x86_64_phone-trunk_staging-userdebug
m update-api

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

android.os.Flags.jycBatteryServiceEnabled()
android.os.Flags.FLAG_JYC_BATTERY_SERVICE_ENABLED

二 新建AIDL文件

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

package android.os.jycservice;

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

之后进行编译:

m framework-minus-apex

三 创建Service

打开AOSP16/frameworks/base/core/java/android/content/Context.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:

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方法中,添加:

/* 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:

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方法中,新增注册逻辑:

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,所以我们需要更新:

m update-api

七 配置Selinux

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

###
### JYC Services
###
type jyc_battery_service, app_api_service, system_api_service, system_server_service, service_manager_type;

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

# JYC Service
jyc_battery                               u:object_r:jyc_battery_service:s0

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

# Add JYC Service
add_service(system_server, jyc_battery_service)

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

# Allow Other App Can Find JYCBatteryService
allow untrusted_app jyc_battery_service:service_manager find;

最后进行编译:

m

八 验证服务

编译成功之后,运行:

launch_cvd --daemon

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

adb shell service list | grep jyc_battery

之后我们会看到输出:

190	jyc_battery: [android.os.jycservice.IJYCBatteryService]

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

adb shell service call jyc_battery 1

之后可以看到输出:

Result: Parcel(	00000000 00000055   '....U...')

九 在App中使用服务

在第三方应用开发时,如果使用标准sdk,我们新增的服务是没办法感知到的,所以我们需要生成一个sdk,但是生成SDK时间过于缓慢,由于我们的修改主要在framework/base中,所以我们提供一个framework.jar给app开发者让他们在编译时依赖即可。

当编译成功之后,打开AOSP16/out/soong/.intermediates/frameworks/base/framework-minus-apex/android_common/combined,可以找到生成的jar包framework.jar

然后将framework.jar提供给app开发者即可。

在app开发中,将framwork.jar,添加到本地app/libs下面,然后在app的Gradle配置中添加:

dependencies {
    compileOnly(files("libs/framework.jar"))
}

allprojects {
    tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
        compilerOptions {
            freeCompilerArgs.add("-Xjvm-default=all")
        }
    }
}

gradle.projectsEvaluated {
    tasks.withType<JavaCompile>().all {
        options.compilerArgs.add("-Xbootclasspath/p:${project.rootDir}/app/libs/framework.jar")
    }
}

编译成功之后,就可以看到代码提示,然后我们可以像其它的Service那样使用我们的ServiceManager,下面是示例代码:

package com.jyc.testapplication

import android.os.Bundle
import android.os.jycservice.JycBatteryManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
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.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.jyc.testapplication.ui.theme.TestApplicationTheme

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

        val jycManager = getSystemService("jyc_battery") as JycBatteryManager

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

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

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

生成apk打开之后,就可以看到效果:

自定义服务.png