Android 安卓自定义系统服务

最近有个需求,要增加系统服务,生成第三方 jar 包提供给第三方应用调用,而且 jar 包必须用特定的包名 ,最后生成的 jar 包不能包含 frameworks 相关代码。

网上搜索了很多资料,搜索结果都没有自定义包名的案例,导出的 jar 也包含了 frameworks
相关代码。最终搜到这篇 博客 ,结合其他博客,最终完成功能

Android 10 自定义系统服务 安卓自定义系统服务 输出jar包

  • 该案例基于 rockchip 芯片开发,在 PX30 Android10 上进行的测试,在其他设备上大同小异,请自行查找或替换为对应的路径。
  • 实操过程中,可以在 AndroidStudio 中先编写好代码,检查代码中是否有语法错误,然后将代码拷贝到 frameworks 中,最后进行编译。
  • 该示例包名为 com.test.customservice

1. 编写代码

1.1 编写 AIDL ITestService.aidl文件

frameworks/base/test/java/com/test/customservice/ITestService.aidl

package com.test.customservice;

import android.os.Bundle;

interface ITestService {

     void setValue(int value);

     int getValue();

     void setBundle( in Bundle bundle);

     Bundle getBundle();
}

1.2 编写服务管理 TestServiceManager.java 文件

frameworks/base/test/java/com/test/customservice/TestServiceManager.java

package com.test.customservice;

import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;

import com.test.customservice.ITestService;

public class TestServiceManager {
    private static final String TAG = "TestServiceManager";
    private ITestService iTestService;

    public TestServiceManager(Context context, ITestService iTestService) {
        this.iTestService = iTestService;
    }


    public void setValue(int value) {
        try {
            iTestService.setValue(value);
        } catch (RemoteException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }
    }

    public int getValue() {
        try {
            return iTestService.getValue();
        } catch (RemoteException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }
        return 0;
    }

    public void setBundle(Bundle bundle) {
        try {
            iTestService.setBundle(bundle);
        } catch (RemoteException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }
    }

    public Bundle getBundle() {
        try {
            return iTestService.getBundle();
        } catch (RemoteException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }
        return null;
    }
}

1.3 编写服务的实现 TestService.java 文件

frameworks/base/services/core/java/com/test/customservice/TestService.java

package com.test.customservice;

import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.content.Context;

import com.test.customservice.ITestService;

public class TestService extends ITestService.Stub {
    public static final String TAG = "TestService";
    private Context mContext;

    public TestService(Context context){
        this.mContext = context;
    }
    @Override
    public void setValue(int value) throws RemoteException {
        Log.i(TAG, "set value is " + value);
    }

    @Override
    public int getValue() throws RemoteException {
        Log.i(TAG, "get value is default value 0");
        return 0;
    }

    @Override
    public void setBundle(Bundle bundle) throws RemoteException {
        Log.i(TAG, "set bundle is " + bundle.toString());
    }

    @Override
    public Bundle getBundle() throws RemoteException {
        Bundle bundle = new Bundle();
        Log.i(TAG, "get bundle is " + bundle);
        return bundle;
    }
}

1.4 编写 Android.mk 文件

此文件用于将自定义的 service 生成jar包

frameworks/base/test/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, java)

LOCAL_MODULE_CLASS := JAVA_LIBRARIES
include $(BUILD_JAVA_LIBRARY)

1.5 拷贝文件到 frameworks 指定目录

该步骤仅针对先在 AndroidStudio 中创建好 AIDL 和 java 文件的情况;如果你是直接在 frameworks 源码中创建的,请忽略本步骤。

  1. 拷贝 ITestService.aidlTestServiceManager.java + 在 frameworks/base 目录下创建子目录 test/java/com/test/customservice,
    + 将 ITestService.aidlTestServiceManager.java 拷贝到 frameworks/base/test/java/com/test/customservice 路径下
  2. 拷贝 Android.mk + 将 Android.mk 拷贝到 frameworks/base/test 路径下,用来生成 jar 包,供第三方应用使用
  3. 拷贝 TestService.java + 在 frameworks/base/services/core/java/ 目录下创建子目录 com/test/customservice,
    由于 frameworks/base/services/core/java/com 目录已经创建,因此相当于在 frameworks/base/core/java/com 创建 test/customservice 目录即可。
    + 将 TestService.java 拷贝到 frameworks/base/core/java/com/test/customservice 路径下

2. 配置

2.1 frameworks 引入创建的 AIDL 和 java 代码

编辑 frameworks/base/Android.bp 或者 frameworks/base/Android.mk 文件,配置自定义的 aidl 和 java 文件

java_library {
name: "framework",

    srcs: [
        "packages/services/PacProcessor/com/android/net/IProxyService.aidl",
        "packages/services/Proxy/com/android/net/IProxyCallback.aidl",
        "packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
        "core/java/android/service/quicksettings/IQSService.aidl",
        "core/java/android/service/quicksettings/IQSTileService.aidl",
        
+        "test/java/**/*.java",
+        "test/java/com/test/customservice/ITestService.aidl",

    ],
    aidl: {
        export_include_dirs: [
            // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
            "core/java",
            "graphics/java",
            "location/java",
            "lowpan/java",
            "media/java",
            "media/mca/effect/java",
            "media/mca/filterfw/java",
            "media/mca/filterpacks/java",
            "drm/java",
            "opengl/java",
            "sax/java",
            "telecomm/java",
            "telephony/java",
            "wifi/java",
            "keystore/java",
            "rs/java",
+              "test/java",
        ],

}

2.2 编辑 device.mk 将 test 模块编译进 system 中

编辑 device/rockchip/rk3326/PX30_indpx30a/device-common.mk,增加以下内容

# add test service
PRODUCT_PACKAGES += \
    test
# add test service

2.3 将 test 模块加入到 framework

编辑 build/core/pathmap.mkFRAMEWORKS_BASE_SUBDIRS 中增加 test \

FRAMEWORKS_BASE_SUBDIRS := \
        $(addsuffix /java, \
            core \
            graphics \
            location \
            media \
            media/mca/effect \
            media/mca/filterfw \
            media/mca/filterpacks \
            drm \
            opengl \
            sax \
            telecomm \
            telephony \
            wifi \
            lowpan \
            keystore \
            rs \
+            test \
         )

2.4 编辑 Context.java

修改 frameworks/base/core/java/android/content/Context.java 增加 TEST_SERVICE 常量

/**
     * Use with {@link #getSystemService(String)} to retrieve a {@link com.test.customservice.TestServiceManager} for test by the system.
     *
     * @see #getSystemService(String)
     * @see com.test.customservice.TestServiceManager
     */
    public static final String TEST_SERVICE = "test";

2.5 编辑 SystemServer.java

frameworks/base/services/java/com/android/server/SystemServer.javastartOtherServices() 方法中中增加服务,注意别漏掉 import 语句

import com.test.customservice.TestService;

***
    private void startOtherServices() {
    
    
***
        traceBeginAndSlog("Start Test Service");
        try {
            ServiceManager.addService(Context.TEST_SERVICE, new TestService(context));
        } catch (Throwable e) {
            reportWtf("starting TestService", e);
        }
        traceEnd();
        
***        
    }

2.6 编辑 SystemServiceRegistry.java 注册服务

frameworks/base/core/java/android/app/SystemServiceRegistry.java 中注册服务,注意别漏掉 import 语句

import com.test.customservice.ITestService;
import com.test.customservice.TestServiceManager;

***
    static {
***
        registerService(Context.TEST_SERVICE, TestServiceManager.class,
                new CachedServiceFetcher<TestServiceManager>() {
                    @Override
                    public TestServiceManager createService(ContextImpl ctx)
                            throws ServiceNotFoundException {
                        IBinder b = ServiceManager.getService(Context.TEST_SERVICE);
                        return new TestServiceManager(ctx,
                                ITestService.Stub.asInterface(b));
                    }});

***
    }

2.7 编辑 package_whitelist.txt 添加白名单

build/make/core/tasks/check_boot_jars/package_whitelist.txt 中最后一行增加

com\.test\.customservice\..*

2.8 编辑 hiddenapi-greylist-packages.txt 添加白名单

frameworks/base/config/hiddenapi-greylist-packages.txt 中最后一行增加

***
org.apache.xpath
org.apache.xpath.axes
org.apache.xpath.compiler
org.apache.xpath.domapi
org.apache.xpath.functions
org.apache.xpath.jaxp
org.apache.xpath.objects
org.apache.xpath.operations
org.apache.xpath.patterns
org.apache.xpath.res
org.ccil.cowan.tagsoup
org.ccil.cowan.tagsoup.jaxp
+ com.test.customservice

2.9 配置SELinux

  • device/rockchip/common/sepolicy/private/service_contexts 中加一行,其中 test 为 frameworks/base/core/java/android/content/Context.java 中增加的 TEST_SERVICE 的值
test                                                   u:object_r:test_service:s0
  • device/rockchip/common/sepolicy/vendor/service.te 中加一行
type test_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;

3. 编译验证

先执行 make update-api ,然后编译新固件,刷机后开机验证 ,
windows : adb shell service list | findstr "test" linux : adb shell service list | grep test 如果显示有 test: [com.test.customservice.ITestService] 证明 service 启动成功

4. 集成到应用并调用

自定义服务生成的 jar 包路径为 out/target/common/obj/JAVA_LIBRARIES/test_intermediates/classes.jar ,将其拷贝到 AndroidStudio 工程的 libs 目录,然后右击Add As Library 集成到工程中。

调用示例如下

package com.test.tempserviceclient;

import android.annotation.SuppressLint;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.test.customservice.TestServiceManager;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        @SuppressLint("WrongConstant") TestServiceManager testServiceManager = (TestServiceManager) getSystemService("test");
        int value = testServiceManager.getValue();
        System.out.println(value);
    }
}