Android Native Service 示例 第二篇
在安卓里面实现一个 native 的系统服务,不仅可以通过定义 Interface ,还可以通过直接继承 BBinder 来实现。本篇文章即讲述如何通过继承 Bbinder 来实现一个系统服务,并且讲述如何在 native 上使用 binder 的 linktoDeath 机制
首先列出需要包含的头文件
// CommonHeader.h
#ifndef _COMMON_HEADER_H_
#define _COMMON_HEADER_H_
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionController.h>
#include <binder/ProcessInfoService.h>
#include <binder/IResultReceiver.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <log/log_main.h>
#include "Cmdid.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "NativeService"
#define NATIVESERVICE_NAME "nativeservice"
#endif
这里为了方便,我将可能用到的头文件都聚在一起了。
服务端
然后服务端的声明
//NativeServiceSampleService.h
#ifndef _NATIVE_SERVICE_SAMPLE_SERVICE_H_
#define _NATIVE_SERVICE_SAMPLE_SERVICE_H_
#include "CommonHeader.h"
#include <map>
namespace android
{
enum STATUS {
SUCCESS = NO_ERROR,
FAILED = UNKNOWN_TRANSACTION
};
typedef status_t (*local_method)(const Parcel &data, Parcel *reply);
class NativeServiceSampleService : public BBinder {
public:
NativeServiceSampleService();
virtual status_t onTransact(uint32_t code,const Parcel& data, Parcel* reply,uint32_t flags = 0);
status_t dump(int fd, const Vector<String16> &args);
int onShellCommand(int in, int out, int err, const Vector<String16> &args);
virtual status_t invoke(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
virtual const String16& getInterfaceDescriptor() const{return this->interface;};
private:
int shellCommand(int in, int out, int err, const Vector<String16> &args);
int getCallingPid(){return IPCThreadState::self()->getCallingPid();}
int getCallingUid(){return IPCThreadState::self()->getCallingUid();}
std::map<uint32_t,local_method> methods;
String16 interface = String16(NATIVESERVICE_NAME);
};
} // namespace android
#endif
这里最重要的依然是 onTransact ,Binder 通信的第一站就是这里,来看看服务端的实现
//NativeServiceSampleService.cpp
#include "NativeServiceSampleService.h"
#include "Cmdid.h"
namespace android
{
status_t sampleCallFunction(const Parcel&data, Parcel* reply){
String8 str = String8(data.readString16());
ALOGD("call funcion called with \"%s\"",str.string());
return SUCCESS;
}
status_t sampleGetFunction(const Parcel&data, Parcel* reply){
ALOGD("get function called");
String16 hello = String16("this is message from service");
reply->writeString16(hello);
return SUCCESS;
}
status_t getMacByInterfaceName(const Parcel &data, Parcel *reply)
{
String16 ifname = data.readString16();
ALOGV("get mac of ifname: %s",String8(ifname).string());
String8 path = String8("/sys/class/net/");
path += String8(ifname);
path += String8("/address");
FILE* fp = fopen(path.string(),"ro");
if(fp == NULL){
ALOGE("open %s failed(%d): %s",path.string(),errno,strerror(errno));
return FAILED;
}
char mac[18] = {0};
size_t readsize = fread(mac,1,17,fp);
if(readsize != 17){
ALOGE("read mac failed");
fclose(fp);
return FAILED;
}
String16 result = String16(mac);
reply->writeString16(result);
fclose(fp);
return SUCCESS;
}
status_t SampleExitService(const Parcel &data,Parcel *reply)
{
ALOGI("service exit now");
printf("service received exit message\n");
exit(0);
return SUCCESS;
}
NativeServiceSampleService::NativeServiceSampleService()
{
methods[SAMPLE_CALL] = &sampleCallFunction;
methods[SAMPLE_GET] = &sampleGetFunction;
methods[SAMPLE_GET_MAC] = &getMacByInterfaceName;
methods[SAMPLE_EXIT_SERVER] = &SampleExitService;
}
status_t NativeServiceSampleService::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags)
{
if (code == SHELL_COMMAND_TRANSACTION)
{
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
int argc = data.readInt32();
Vector<String16> args;
for (int i = 0; i < argc && data.dataAvail() > 0; i++)
{
args.add(data.readString16());
}
status_t result = 0;
sp<IBinder> unusedCallback;
sp<IResultReceiver> resultReceiver;
if ((result = data.readNullableStrongBinder(&unusedCallback)) != NO_ERROR)
{
return result;
}
if ((result = data.readNullableStrongBinder(&resultReceiver)) != NO_ERROR)
{
return result;
}
result = shellCommand(in, out, err, args);
if (resultReceiver != nullptr)
{
resultReceiver->send(result);
}
return result;
}
else if (IBinder::FIRST_CALL_TRANSACTION <= code && code <= IBinder::LAST_CALL_TRANSACTION)
{
CHECK_INTERFACE(INativeServiceSampleService, data, reply);
return invoke(code, data, reply, flags);
}
else
{
return BBinder::onTransact(code, data, reply, flags);
}
}
status_t NativeServiceSampleService::shellCommand(int in, int out, int err, const Vector<String16> &args)
{
return onShellCommand(in, out, err, args);
}
status_t NativeServiceSampleService::onShellCommand(int in, int out, int err, const Vector<String16> &args)
{
ALOGI("onShellCommand not implementation yet");
write(out, "onShellCommand not implementation yet\n", 38);
size_t size = args.size();
for (size_t i = 0; i < size; i++)
{
ALOGI("Received No.%zu arg: %s", i, String8(args[i]).string());
}
return SUCCESS;
}
status_t NativeServiceSampleService::dump(int fd, const Vector<String16> &args)
{
ALOGI("dump not implementation yet");
write(fd, "dump not implementation yet\n", 28);
size_t size = args.size();
for (size_t i = 0; i < size; i++)
{
ALOGI("Received No.%zu arg: %s", i, String8(args[i]).string());
}
return SUCCESS;
}
status_t NativeServiceSampleService::invoke(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
std::map<uint32_t, local_method>::iterator iter = methods.find(code);
ALOGV("uid:%d pid:%d called method %u",getCallingUid(),getCallingPid(),code);
if (iter != methods.end())
{
return (*(iter->second))(data, reply);
}
else
{
ALOGE("can't find such a method whose cmdid is %u", code);
return -1;
}
}
}
同样的,这里用一个 map 来保存了不同code和对应的函数的指针,服务端接收到调用时,可以判断 code 来直接从 map 里面取到相应的函数指针来调用并返回。
dump 和 shellcommand 是通过 dumpsys servicename 和 cmd servicename 这两种方式,会接收到的特殊 code ,这里同样封装成了接口,以供实现。
这个时候,服务端的实现已经完成了,可以参考下面的办法来启动一个服务端
//server.cpp
#include "NativeServiceSampleService.h"
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <utils/String8.h>
#include <utils/String16.h>
int main(int argc, char **argv)
{
printf("start %s\n",NATIVESERVICE_NAME);
ALOGI("start %s",NATIVESERVICE_NAME);
android::defaultServiceManager()->addService(android::String16(NATIVESERVICE_NAME), new android::NativeServiceSampleService());
android::ProcessState::self()->startThreadPool();
android::IPCThreadState::self()->joinThreadPool();
return 0;
}
addService 见名知意,android::ProcessState::self()->startThreadPool(); 则是监听Binder通信的各种消息。android::IPCThreadState::self()->joinThreadPool(); 会阻塞在这里,通常我们是希望服务端保持运行的。
客户端
客户端是调用方,调用方最后调用的都是 Binder 的 transact ,当一个服务没有通过 interface 来声明的时候,我们可以直接调用 Binder 的 transact 来调用服务端。这里我们为客户端加上监听服务端是否退出的机制,这样客户端在调用过程中,如果服务端推出了,那么后续的调用则不再接收。
首先是 DeathRecipient ,与 java 层的类似,native 的也是继承相应的类
//NativeServiceSampleDeathRecipient.h
#ifndef _NATIVE_SERVICE_SAMPLE_DEATH_RECIPIENT_H_
#define _NATIVE_SERVICE_SAMPLE_DEATH_RECIPIENT_H_
#include "CommonHeader.h"
#include <mutex>
#include <atomic>
namespace android{
class NativeServiceSampleDeathRecipient : public IBinder::DeathRecipient
{
public:
NativeServiceSampleDeathRecipient(){mValidVar.store(invalid,std::memory_order_relaxed);};
bool setValid(){
return mValidVar.compare_exchange_strong(invalid,valid);
}
bool setInvalid(){
return mValidVar.compare_exchange_strong(valid,invalid);
}
bool isValid(){
return mValidVar.load();
}
private:
virtual void binderDied(const wp<IBinder>& who) {
ALOGE("service died. set service invalid");
setInvalid();
};
std::atomic_bool mValidVar;
bool valid = true;
bool invalid = false;
};
}
#endif
定义一个类,继承于 IBinder::DeathRecipient ,并覆写 binderDied 。
来看客户端的声明
//NativeServiceSampleClient.h
#ifndef _NATIVE_SERVICE_SAMPLE_CLIENT_H_
#define _NATIVE_SERVICE_SAMPLE_CLIENT_H_
#include "CommonHeader.h"
#include "NativeServiceSampleDeathRecipient.h"
namespace android{
class NativeServiceSampleClient{
public:
NativeServiceSampleClient();
~NativeServiceSampleClient();
status_t invoke(uint32_t code, const Parcel &data, Parcel *reply,int flag);
status_t invoke(uint32_t code, const Parcel &data, Parcel *reply);
private:
sp<IBinder> service;
sp<NativeServiceSampleDeathRecipient> mdeathRecipient;
};
}
#endif
看客户端的实现
//NativeServiceSampleClient.cpp
#include "NativeServiceSampleClient.h"
namespace android{
NativeServiceSampleClient::NativeServiceSampleClient(){
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL)
{
ALOGE("can not get service manager");
return;
}
service = sm->getService(String16(NATIVESERVICE_NAME));
if (service == NULL)
{
ALOGE("can not get service");
return;
}
mdeathRecipient = new NativeServiceSampleDeathRecipient();
if(service->linkToDeath(mdeathRecipient) != NO_ERROR)
{
ALOGE("link to death failed");
return;
}
ALOGI("client link to death success");
if(!mdeathRecipient->setValid()){
ALOGE("set death recipient valid failed");
}
if(mdeathRecipient->isValid()){
ALOGI("service is running");
}else{
ALOGE("service is not running and why ?");
}
}
NativeServiceSampleClient::~NativeServiceSampleClient()
{
if(service != nullptr)
{
if(mdeathRecipient->isValid())
{
if(service->unlinkToDeath(mdeathRecipient) == NO_ERROR)
ALOGD("unlinktodeath success");
else
ALOGE("unlinktodeath failed");
}
else
{
ALOGE("service is not running. need unlinkToDeath ?");
}
}
}
status_t NativeServiceSampleClient::invoke(uint32_t code, const Parcel &data, Parcel *reply)
{
return invoke(code,data,reply,0);
}
status_t NativeServiceSampleClient::invoke(uint32_t code, const Parcel &data, Parcel *reply,int flags)
{
if (!mdeathRecipient->isValid())
{
ALOGE("service is not running");
return -1;
}
Parcel send;
send.writeInterfaceToken(android::String16(NATIVESERVICE_NAME));
send.appendFrom(&data, 0, data.dataSize());
return service->transact(code, send, reply,flags);
}
}
客户端主要干两个事情,定义一个对外提供 transact 调用的接口,以及在构造的时候,为服务端的 binder 对象 linktoDeath ,这样服务端中途退出时,客户端可以知道相应的消息
//Cmdid.h
#ifndef _CMDID_H_
#define _CMDID_H_
#include "binder/IBinder.h"
namespace android{
enum ENUME_CMD{
SAMPLE_CALL = IBinder::FIRST_CALL_TRANSACTION,
SAMPLE_GET,
SAMPLE_GET_MAC,
SAMPLE_EXIT_SERVER,
LAST_COMMAND = IBinder::LAST_CALL_TRANSACTION
};
}
#endif
cmdid 依旧是服务端和客户端共同约定的通信 code。
调用服务端可以参考下面的实现
//client.cpp
#include "CommonHeader.h"
#include "NativeServiceSampleClient.h"
#include "cmdline.h"
#include <string>
#include <unistd.h>
using namespace android;
void sampleSendStringToServer(NativeServiceSampleClient &service)
{
Parcel data, reply;
printf("send \"sample message\" to service\n");
String16 str = String16("sample message to service\"");
data.writeString16(str);
int ret = service.invoke(android::SAMPLE_CALL, data, &reply);
if (ret == NO_ERROR)
{
printf("service has received message\n");
}
else
{
printf("send msg to service failed\n");
}
}
void sampleGetStringFromServer(NativeServiceSampleClient &service)
{
Parcel data, reply;
int ret = service.invoke(android::SAMPLE_GET, data, &reply);
if (ret == NO_ERROR)
{
String8 str = String8(reply.readString16());
printf("received msg from server: \"%s\"\n",str.string());
}
}
void sampleExitServer(NativeServiceSampleClient &service)
{
Parcel data,reply;
// no wait reply;
int ret = service.invoke(android::SAMPLE_EXIT_SERVER, data, &reply,1);
if(ret == NO_ERROR)
{
printf("call service exit function success\n");
}else{
printf("call service exit function failed\n");
}
}
void sampleGetmacByInterfaceName(NativeServiceSampleClient &service,std::string& ifname)
{
Parcel data, reply;
data.writeString16(String16(ifname.c_str()));
int ret = service.invoke(android::SAMPLE_GET_MAC, data, &reply);
if (ret == NO_ERROR)
{
printf("get mac %s's mac address is %s\n",ifname.c_str(), String8(reply.readString16()).string());
}
else
{
printf("call get mac function failed\n");
}
}
int main(int argc, char *argv[])
{
cmdline::parser args;
args.add("call",'\0',"call sample send msg to service");
args.add("get",'\0',"call sample get msg from service");
args.add<std::string>("ifname",'i',"get supplied interface name",false,"");
args.add("exit",'\0',"call function make service exit and link death recipient");
args.parse_check(argc,argv);
NativeServiceSampleClient service;
ProcessState::self()->startThreadPool();
if(args.exist("call")){
sampleSendStringToServer(service);
}
if(args.exist("get")){
sampleGetStringFromServer(service);
}
if(args.exist("exit")){
sampleExitServer(service);
// listen to wait service died msg;
sleep(5);
}
if(args.exist("ifname")){
std::string ifname = args.get<std::string>("ifname");
sampleGetmacByInterfaceName(service,ifname);
}
// printf("press any key exit\n");
// getchar();
return 0;
}
cmdline.h
是一个解析命令行参数的库,见 https://github.com/tanakh/cmdline
在安卓的源码环境中,可以参考以下 Android.bp 来将服务端和客户端的可执行文件都编译出来
cc_defaults{
name: "nativeservicesample_defaults",
shared_libs: [
"libutils",
"libcutils",
"libbinder",
"libhardware",
"liblog"
],
include_dirs: [
"frameworks/native/include",
"system/core/include",
"system/libhwbinder/include"
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-ignored-qualifiers",
"-Wno-unused-parameter",
],
}
cc_binary{
name: "server",
defaults: [
"nativeservicesample_defaults",
],
srcs: [
"server.cpp",
"NativeServiceSampleService.cpp",
],
}
cc_library{
name: "libnativeservicesampleclient",
srcs: [
"NativeServiceSampleClient.cpp",
],
defaults: [
"nativeservicesample_defaults",
],
}
cc_binary{
name: "client",
defaults: [
"nativeservicesample_defaults",
],
cflags: [
"-fexceptions",
],
static_libs: [
"libnativeservicesampleclient",
],
rtti: true,
srcs: [
"client.cpp",
],
}
服务端直接执行即可,客户端可以通过 --help 参数来查看帮助。
客户端想要监听服务端退出的消息,除了需要 linktodeath 之外,还需要 ProcessState::self()->startThreadPool();
如何在 java 层调用
通过jni调用
我们可以通过 jni 调用 NativeServiceSampleClient::invoke 来获取返回值,参考实现如下
#include <stdio.h>
#include <android_os_Parcel.h>
#include <binder/Parcel.h>
#include "jni.h"
#include "NativeServiceSampleClient.h"
static android::NativeServiceSampleClient* g_client = nullptr;
static int
invoke(JNIEnv* env , jobject /* thiz */,jint code, jobject dataObj, jobject replyObj, jint flags) {
ALOGI("invoke called");
if (dataObj == NULL) {
// jniThrowNullPointerException(env, NULL);
return -1;
}
android::Parcel* data = android::parcelForJavaObject(env, dataObj);
if (data == NULL) {
return -1;
}
android::Parcel* reply = android::parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return -1;
}
if(g_client == nullptr){
return -1;
}
return g_client->invoke(code, *data,reply,flags);
}
static const char *classPathName = "com/example/NativeServiceSample";
static const JNINativeMethod methods[] = {
{"invoke","(ILandroid/os/Parcel;Landroid/os/Parcel;I)I",(void*)invoke},
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
// ----------------------------------------------------------------------------
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
ALOGI("Jni_OnLoad called");
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
ALOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
ALOGE("ERROR: registerNatives failed");
goto bail;
}
if(g_client == nullptr){
g_client = new android::NativeServiceSampleClient();
}
result = JNI_VERSION_1_4;
bail:
return result;
}
我们新建一个全局的静态变量,在 jni_onload 的时候初始化它,定义了一个用于与 java 层交互的 invoke 函数,此函数的再调用 NativeServiceSampleClient 的 invoke 并获取返回结果
p.s: 需要在源码环境下 link liblog libbinder libandroid_runtime,这种方法需要系统开放私有的库,对应用开放的库见 /system/etc/public.libraries.txt 。
通过安卓的 IBinder 调用
安卓的 android.os.IBinder 本身已经封装成对应的 java api了,系统应用可以通过 ServiceManager.getService 拿到对应的binder对象,参考实现如下
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
public class NativeService {
private static final String SERVICE_NAME = "nativeservice";
private boolean valid = false;
IBinder service = null;
public NativeService(){
valid = false;
service = ServiceManager.getService(SERVICE_NAME);
if(service != null){
try{
service.linkToDeath(()->{
Log.e("shell","service died");
setInvlaid();
},0);
setValid();
}catch (RemoteException e){
Log.e("shell","link to death failed",e);
setInvlaid();
}
}
}
public boolean invoke(int code,Parcel data,Parcel reply) throws RemoteException {
if(isValid()){
Parcel send = Parcel.obtain();
send.writeInterfaceToken(SERVICE_NAME);
send.appendFrom(data,0,data.dataSize());
boolean result = service.transact(code,send,reply,0);
send.recycle();
return result;
}
Log.e("shell","service not running");
return false;
};
private void setInvlaid(){
synchronized(this){
valid = false;
}
}
private void setValid(){
synchronized(this){
valid = true;
}
}
private boolean isValid(){
synchronized(this){
return valid;
}
}
}
p.s: 此方式需要应用为系统应用或反射 ServiceManager 或在源码环境下编译