"一. java层系统服务 以添加一个 KKTVService 为例, 这个service 提供一个方法,获取设备唯一序列号。 1. 添加aidl 文件,定义service 提供的接口 在目录frameworks/base/core/java/android/app下增加中文件 IKKTVServic ...."

android 系统服务 - 添加 java-service 和 native-service

一. java层系统服务

以添加一个 KKTVService 为例,
这个service 提供一个方法,获取设备唯一序列号。

1. 添加aidl 文件,定义service 提供的接口

在目录frameworks/base/core/java/android/app下增加中文件 IKKTVService.aidl,

package android.app;
interface IKKTVService {
    String getSeriesNumber();
}

在 frameworks/base/Android.mk 中LOCAL_SRC_FILES += \ 下增加一行

core/java/android/app/IKKTVService.aidl

进入 framework/base 目录执行 mm 命令编译framework.jar 模块
这个aidl 文件在编译的时候会生成 IKKTVService.java,这里封装了与binder驱动通信的相关细节,这里就不展开了。
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app/IKKTVService.java

/*
 * This file is auto-generated. DO NOT MODIFY.
 * Original file: frameworks/base/core/java/android/app/IKKTVService.aidl
 */
package android.app;
public interface IKKTVService extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements android.app.IKKTVService
    {
        private static final java.lang.String DESCRIPTOR = "android.app.IKKTVService";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an android.app.IKKTVService interface,
         * generating a proxy if needed.
         */
        public static android.app.IKKTVService asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof android.app.IKKTVService))) {
                return ((android.app.IKKTVService)iin);
            }
            return new android.app.IKKTVService.Stub.Proxy(obj);
        }
        @Override public android.os.IBinder asBinder()
        {
            return this;
        }
        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code)
            {
                case INTERFACE_TRANSACTION:
                    {
                        reply.writeString(DESCRIPTOR);
                        return true;
                    }
                case TRANSACTION_getSeriesNumber:
                    {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _result = this.getSeriesNumber();
                        reply.writeNoException();
                        reply.writeString(_result);
                        return true;
                    }
            }
            return super.onTransact(code, data, reply, flags);
        }
        private static class Proxy implements android.app.IKKTVService
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
            @Override public android.os.IBinder asBinder()
            {
                return mRemote;
            }
            public java.lang.String getInterfaceDescriptor()
            {
                return DESCRIPTOR;
            }
            @Override public java.lang.String getSeriesNumber() throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getSeriesNumber, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        static final int TRANSACTION_getSeriesNumber = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    public java.lang.String getSeriesNumber() throws android.os.RemoteException;
}

2. 实现 IKKTVService 接口

在目录 frameworks/base/services/core/java/com/android/server 下增加 KKTVService.java 文件

package com.android.server;
import android.content.Context;
import android.util.Slog;
import android.app.IKKTVService;
class KKTVService extends IKKTVService.Stub {
    private String TAG = "KKTVService";
    private Context mContext;
    public KKTVService(Context context) {
        super();
        this.mContext = context;
        Slog.w(TAG, "create KKTVService success");
    }

    public String getSeriesNumber() {
        Slog.d(TAG, "called KKTVService.getSeriesNumber");
        return "EWFDTRETQWET-SFDSDF";
    }
}

这里实现的 getSeriesNumber 接口只是随便返回的一个字符串。

3. 将 KKTVService 添加到 ServiceManager

修改 frameworks/base/core/java/android/content/Context.java 增加一个常量,对应新增服务的名字。

public static final String KKTVSERVICE = "kktvservice";

修改 frameworks/base/services/java/com/android/server/SystemServer.java 文件。
在 startOtherServices 方法里面增加以下代码

        try {
                Slog.i(TAG, "add KKTVService");
                KKTVService kktvservice = new KKTVService(context);
                ServiceManager.addService(Context.KKTVSERVICE, kktvservice);
            } catch (Throwable e) {
                reportWtf("starting kktvservice service", e);
            }

以上两个文件的修改,如果有overlay的话,要修改overlay下面的文件。

4. 调用服务

添加完服务后,就可以在应用里面调用了

        try {
            IKKTVService kktvservice = IKKTVService.Stub.asInterface(
                    ServiceManager.getService(Context.KKTVSERVICE));
            String seriesNumber = kktvservice.getSeriesNumber();
            Log.d(TAG, "seriesNumber = " + seriesNumber);
        } catch(Exception e) {
            e.printStackTrace();
        }

上面的三步就是添加一个java层系统服务的最少步骤了。

二. Native 层系统服务

native service 是使用c++ 语言编写的,实现起来相对 java service 要麻烦一些,但有时候出于性能方面的考虑,需要我们使用执行效率更高的c++来实现服务功能。
下面我们用 native service 重新实现一遍 KKTVService,为了区分开来就叫做 NativeKKTVService。
这个service 提供了两个接口:

static const int GET_SERIES_NUMBER = 0; //获取设备唯一序列号
static const int GET_PROGRAM_COUNT = 1; //根据频道号获取频道的节目数量

先新建一个目录 NativeKKTVService 放工程代码。

1. NativeKKTVService.h 头文件定义

#ifndef __NATIVE_KKTV_SERVICE__
#define __NATIVE_KKTV_SERVICE__
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
using namespace android;
static const int GET_SERIES_NUMBER = 0;
static const int GET_PROGRAM_COUNT = 1;
//继承BBinder类,从而提供IBinder 接口
class NativeKKTVService : public BBinder {
public:
    NativeKKTVService();
    virtual ~NativeKKTVService();
    static int instantiate(); //单例
    virtual status_t onTransact(uint32_t, const Parcel &, Parcel *, uint32_t);
};
#endif /* __NATIVE_KKTV_SERVICE__ */

2. NativeKKTVService.cpp 实现

#include <binder/IServiceManager.h>
#include "log.h"
#include "NativeKKTVService.h"
using namespace android;
int NativeKKTVService::instantiate() {
    LOGI("NativeKKTVService instantiate");
    int r = defaultServiceManager()->addService(String16("NativeKKTVService"),
                                                new NativeKKTVService());
    LOGI("NativeKKTVService r = %d/n", r);
    return r;
}

NativeKKTVService::NativeKKTVService() {
    LOGI("NativeKKTVService created");
}

NativeKKTVService::~NativeKKTVService() {
    LOGI("NativeKKTVService destroyed");
}

static status_t getSeriesNumber(const Parcel &data, Parcel *reply) {
     reply->writeString16(String16("QWERPASDFXBVAERG-SFETBG"));
    return NO_ERROR;
}

static status_t getProgramCount(const Parcel &data, Parcel *reply) {
    int channelId = data.readInt32();
    reply->writeInt32(channelId * 2);
    return NO_ERROR;
}

status_t
NativeKKTVService::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
    LOGI("NativeKKTVService::onTransact code = %d", code);
    switch (code) {
        case GET_SERIES_NUMBER:
            return getSeriesNumber(data, reply);
        case GET_PROGRAM_COUNT:
            return getProgramCount(data, reply);
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
    return NO_ERROR;
}

log.h

#ifndef __NATIVE_KKTV_SERVICE_LOG__
#define __NATIVE_KKTV_SERVICE_LOG__
#include <android/log.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "NativeKKTVService", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "NativeKKTVService", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "NativeKKTVService", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , "NativeKKTVService", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "NativeKKTVService", __VA_ARGS__)
#endif

3. 实现 server 进程

前面已经实现了 service 相关的逻辑了,但一个服务要跑起来还是要依赖具体的进程的,
server 的目的在于: 创建一个NativeKKTVService 对象,然后将该对象存入 Binder Driver 里。
main.cpp

#include <sys/types.h>
#include <unistd.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "NativeKKTVService.h"
using namespace android;
int main(int argc, char *argv[]) {
    sp < ProcessState > proc(ProcessState::self());
    NativeKKTVService::instantiate();
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

4. Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= libnative_kktvservice
LOCAL_SRC_FILES:= NativeKKTVService.cpp
LOCAL_SHARED_LIBRARIES:= liblog libutils libbinder
LOCAL_C_INCLUDES := $(TOP)/frameworks/base/include
LOCAL_PRELINK_MODULE:= false
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE:= NativeKKTVServer
LOCAL_SRC_FILES:= main.cpp
LOCAL_SHARED_LIBRARIES:= liblog libutils libnative_kktvservice libbinder
include $(BUILD_EXECUTABLE)

5. 开机启动服务

修改init.rc 文件,增加以下配置

service NativeKKTVServer /system/bin/NativeKKTVServer
    class main
    user root
    group root inet net_bt net_bt_admin net_bw_acct
    ioprio rt 4

6. 测试

经过以上几步,已经可以在开机时将服务跑起来了,接下来我们写一个测试程序,来调用这个服务的接口,看看是否正常。
NativeKKTVServiceTest.cpp

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "log.h"
#include "NativeKKTVService.h"
using namespace android;
int main(int argc, char *argv[]) {
    sp <IBinder > kktvserviceBinder;
    Parcel data, reply;
    LOGI("NativeKKTVServiceTest main is call...");
    sp < IServiceManager > sm = defaultServiceManager();
    while (1) {
        kktvserviceBinder = sm->getService(String16("NativeKKTVService"));
        LOGI("kktvserviceBinder = %p\n", sm.get());
        if (kktvserviceBinder == 0) {
            LOGE("NativeKKTVService not published, waiting...");
            usleep(1000000);
            continue;
        } else {
            LOGI("get NativeKKTVService success...");
            break;
        }
    }

    kktvserviceBinder->transact(GET_SERIES_NUMBER, data, &reply);
    String16 seriesNumber = reply.readString16();
    String8 str8(seriesNumber);
    LOGI("seriesNumber = %s", str8.string());

    data.writeInt32(2);
    kktvserviceBinder->transact(GET_PROGRAM_COUNT, data, &reply);
    int count = reply.readInt32();
    LOGI("programCount = %d", count);
    return 0;
}

Android.mk 里面增加

include $(CLEAR_VARS)
LOCAL_MODULE:= NativeKKTVServerTest
LOCAL_SRC_FILES:= NativeKKTVServiceTest.cpp
LOCAL_SHARED_LIBRARIES:= liblog libutils libbinder
include $(BUILD_EXECUTABLE)

修改 device.mk
把 NativeKKTVServer 和 NativeKKTVServerTest 编译进系统

PRODUCT_PACKAGES += \
    libnative_kktvservice \
    NativeKKTVServer \
    NativeKKTVServerTest

编译系统升级,执行 NativeKKTVServerTest,然后看看 logcat

130|root@KKHi3751V551:/ # NativeKKTVServerTest                                 
root@KKHi3751V551:/ # logcat -s NativeKKTVService                              
--------- beginning of crash
--------- beginning of system
--------- beginning of main
I/NativeKKTVService( 6069): NativeKKTVServiceTest main is call...
I/NativeKKTVService( 6069): kktvserviceBinder = 0x7fb3c20640
I/NativeKKTVService( 6069): get NativeKKTVService success...
I/NativeKKTVService( 1555): NativeKKTVService::onTransact code = 0
I/NativeKKTVService( 6069): seriesNumber = QWERPASDFXBVAERG-SFETBG
I/NativeKKTVService( 1555): NativeKKTVService::onTransact code = 1
I/NativeKKTVService( 6069): programCount = 4

从log可以看到我们成功的从 NativeKKTVService 获取到了 seriesNumber 和 programCount

三. 从 java 层直接调用 native service 服务

新建apk工程,在apk中调用以下代码即可。
因为在app中无法直接访问 ServiceManager 接口,因此使用了反射。
若是在系统源码中使用的话,就可以直接使用 ServiceManager.getService() 了。

    private void serviceTest() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> claz = Class.forName("android.os.ServiceManager");
        Method getService = claz.getMethod("getService", String.class);
        IBinder remote = (IBinder)getService.invoke(null, "NativeKKTVService");
        Print.d("NativeKKTVService = " + remote);

        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            request.writeInt(2);
            remote.transact(1, request, reply, 0);
            int programCount = reply.readInt();
            Print.d("native programCount = " + programCount);

            remote.transact(0, request, reply, 0);
            String seriesNumber = reply.readString();
            Print.d("native seriesNumber = " + seriesNumber);
        } catch (Exception e) {
            Print.d("call nativekktvservice exception");
        } finally {
            request.recycle();
            reply.recycle();
        }
    }

四. client 与 server 的双向通信

前面的例子类似于udp通信,只能client 端主动请求 server,server 返回请求结果。
有时候我们是需要server端在发生某些事件的时候主动通知client的。就像 tcp 通信一样,是全双工的。
我们在客户端也创建一个binder, 客户端获取server端的binder后,把客户端的binder传给server端保存下来。
这样server端就可以通过这个client 的binder主动通知client了。
这里面有个疑问 client 端的binder 是怎么通过 writeStrongBinder 传送到server端去的。这里面得去看binder 的驱动实现。
目前我们只需要知道可以这么用就行了,具体细节后续再分析。

在前面的例子上修改

4.1 native 层实现client 与 server的双向通信

NativeKKTVService.cpp 加上onConnect函数。

static status_t onConnect(const Parcel &data, Parcel *reply, NativeKKTVService *server) {
    server->callback = data.readStrongBinder();
    if (server->callback != NULL) {
        LOGI("clientBinder != NULL, call it");
        Parcel _data, _reply;
        _data.writeString16(String16("msg from server send by client binder"));
        server->callback->transact(1, _data, &_reply, 0);
    } else {
        LOGI("clientBInder == NULL");
    }
    return NO_ERROR;
}
status_t
NativeKKTVService::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
    LOGI("NativeKKTVService::onTransact code = %d", code);
    switch (code) {
        case GET_SERIES_NUMBER:
            return getSeriesNumber(data, reply);
        case GET_PROGRAM_COUNT:
            return getProgramCount(data, reply);
        case CONNECT:
            return onConnect(data, reply, this);
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
    return NO_ERROR;
}

其中 callback 在头文件中的定义如下

sp<IBinder> callback;

NativeKKTVSericeTest.cpp 修改如下:

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "log.h"
#include "NativeKKTVService.h"
using namespace android;
class ClientCallback : public BBinder {
    status_t
    onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
        LOGI("ClientCallback::onTransact code = %d", code);
        switch (code) {
            case 1: {
                String16 str16 = data.readString16();
                String8 str8(str16);
                LOGI("ClientCallback is called!!!----------, msg = %s", str8.string());
                return NO_ERROR;
            }
            default:
                return BBinder::onTransact(code, data, reply, flags);
        }
        return NO_ERROR;
    }
};
int main(int argc, char *argv[]) {
    sp<IBinder> kktvserviceBinder;
    Parcel data, reply;
    LOGI("NativeKKTVServiceTest main is call...");
    sp<IServiceManager> sm = defaultServiceManager();
    while (1) {
        kktvserviceBinder = sm->getService(String16("NativeKKTVService"));
        LOGI("kktvserviceBinder = %p\n", sm.get());
        if (kktvserviceBinder == 0) {
            LOGE("NativeKKTVService not published, waiting...");
            usleep(1000000);
            continue;
        } else {
            LOGI("get NativeKKTVService success...");
            break;
        }
    }
    kktvserviceBinder->transact(GET_SERIES_NUMBER, data, &reply);
    String16 seriesNumber = reply.readString16();
    String8 str8(seriesNumber);
    LOGI("seriesNumber = %s", str8.string());

    data.writeInt32(2);
    kktvserviceBinder->transact(GET_PROGRAM_COUNT, data, &reply);
    int count = reply.readInt32();
    LOGI("programCount = %d", count);

    //需要新定义的 Parcel, 使用上面定义的Parcel 的话,不知道为什么会出错, binder 传不到server去。
    Parcel _data, _reply;
    ClientCallback *callback = new ClientCallback();
    _data.writeStrongBinder(sp<IBinder>(callback));
    kktvserviceBinder->transact(CONNECT, _data, &_reply, 0);
    //休眠一会,等待回调
    sleep(1);
    return 0;
}

4.2 java 层实现client 与 server的双向通信

这里所说的java 层是指client 是java层,server与4.1 节所实现的完全一样。
先定义 Callback类,需要继承 Binder 类

public class Callback extends Binder {
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Log.d("qiushao", "Callback onTransact code = " + code);
        switch (code) {
            case 1:
                String msg = data.readString();
                Log.d("qiushao", "java callback is called, msg = " + msg);
                break;
            default:
                break;
        }
        return super.onTransact(code, data, reply, flags);
    }
}

然后调用方式与 native 层的基本一致

    private void serviceTest() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> claz = Class.forName("android.os.ServiceManager");
        Method getService = claz.getMethod("getService", String.class);
        IBinder remote = (IBinder)getService.invoke(null, "NativeKKTVService");
        Log.d("qiushao", "NativeKKTVService = " + remote);
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            request.writeStrongBinder(callback);
            remote.transact(2, request, reply, 0);
        } catch (Exception e) {
            Log.d("qiushao", "call nativekktvservice exception");
        } finally {
            request.recycle();
            reply.recycle();
        }
    }

0     0     0     0     0    
0 回帖