怎样为Android添加一个系统级服务

内容均来自于网络资料,源码以及自己的理解,如有错误的地方还请指出!示例源码可以随意使用。

我这里使用的环境如下

PLATFORM_VERSION_CODENAME=AOSP
PLATFORM_VERSION=4.0.9.99.999.9999.99999
TARGET_PRODUCT=full_panda
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release

Android提供了一些基础服务,但是如果你想给它添加一些特定的服务的话,也是可以的(比如在一直到某些特定的平台下,需要增加/裁剪一些服务)

比如我们都知道Android上用的最广泛的应该是多媒体服务,其对应的有一个mediaserver进程,然后比如我们的
AudioFlinger/MediaPlayerService/CameraService/AudioPolicyService
都是跑在这个进程里面的

那如果我们想自己增加一个类似的东西可不可以呢?答案当然是可以的。

很简单的几点就可以帮你完成

启动进程

/path/to/aosp/frameworks/base/customized_service/server

具体服务实现

/path/to/aosp/frameworks/base/customized_service/libcustomizedservice

系统开机启动

/path/to/aosp/system/core/rootdir/init.rc

添加如下内容到init.rc文件

service customized /system/bin/customizedserver
    class main
    user media
    group audio camera

我是用的Pandaboard
编译整个ROM,然后fastboot flashall

I/        ( 1080): ServiceManager: 0x40ce4f00
D/        ( 1080): CustomizedService instantiate
D/        ( 1080): CustomizedService created
E/ServiceManager(   94): add_service('test.customized',0x44) uid=xxxx - PERMISSION DENIED
D/        ( 1080): CustomizedService r = -1
D/        ( 1080): CustomizedService destroyed

或者你想从shell当中启动这个服务的进程也会出现类似权限不足的问题,追一下log就很快会发现问题

int do_add_service(struct binder_state *bs,
                   uint16_t *s, unsigned len,
                   void *ptr, unsigned uid, int allow_isolated)
{
    struct svcinfo *si;

    if (!ptr || (len == 0) || (len > 127))
        return -1;

    if (!svc_can_register(uid, s)) {
        ALOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n",
             str8(s), ptr, uid);
        return -1;
    }

    si = find_svc(s, len);
 
    ......

    binder_acquire(bs, ptr);
    binder_link_to_death(bs, ptr, &si->death);
    return 0;
}
int svc_can_register(unsigned uid, uint16_t *name)
{
    unsigned n;
    
    if ((uid == 0) || (uid == AID_SYSTEM))
        return 1;

    for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++)
        if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name))
            return 1;

    return 0;
}

可以看出能注册service的用户的uid只能是0即ROOT或者是AID_SYSTEM,或者是allowed列表当中的服务,最简单的就是把所要添加的服务加到列表当中

{ AID_MEDIA, "test.customized" },

而服务allowed列表位于

/path/to/aosp/frameworks/base/cmds/servicemanager/service_manager.c

重新编译ROM,烧完之后启动看到customizedservice这个进程起来的话,就多半表明服务加好了

App端怎么调用?

写个Activity利用android.os.ServiceManager的getService获取到指定的服务,然后调用就可以了。
这里需要说明下的就是目前android.os.ServiceManager对于app来说是hide掉的,我们暂且可以不管,用个反射先用着。
至于为什么要hide掉,难道Android不想让我们自己定义系统级别的service来使用?还是说用其他的方式可以完成,目前我也不清楚!

目前结果就是通过getService能获取到我们的服务,并且成功调用,但是因为我返回的参数有String有int,而String没有获取出来,都知道Parcel的参数顺序是很重要的,可能是这个的原因,后面再来看看到底是什么原因,目前就是把customized service这一套先跑通。

P.S. 后来一次发现可能是我每次编译的so档案没有进到ROM当中去,所以烧到设备的so档案不是最新的,就没有把后面加的String获取出来(之前是两个int型数据)。
另外我还做了个实验,就是在service端通过ServiceManager的getService(…)方法再次访问当前service,这个就相当于在当前代码中通过一个指针或者引用访问的效果是一样的,这就是Binder的强大之处吧!此改动可以参见以下commit:

commit 1875ad41d5f8e505cb4c26599e4626944005f26b
Date: Tue Jun 25 12:10:59 2013 +0800

Symptom: test invoking getService in same process with service
Issue ID: NA
Root Cause: NA
Solution: NA

完整代码参见
https://github.com/guohai/and-customized-service.git

这里是Java端调用native端的服务,其实也可以native调用native的服务,这里暂时就不再去具体实现了,需要的话,会再来写。

无图无真相
add-customized-service-for-android
add-customized-service-for-android-1

Leave a Reply

Your email address will not be published. Required fields are marked *