Android输入系统(1)—— 必备Linux知识: inotify epoll socketpair binder_fd 双向binder通信
一、inotify 和 epoll
1.Android不使用hotplug机制,使用的是inotify机制。inotify监听的是/dev/input目录。
2.使用inotify来监听文件的创建与删除,使用epoll来监听设备文件句柄的变化,包括inotify的fd。
3.epoll支持管道,FIFO,套接字,POSIX消息队列,终端,设备等,但是就是不支持普通文件或目录的fd。
测试验证,使用fifo测试epoll是可以的,但是使用普通文件来测试epoll的并发监听是不行的。
3.参考代码: frameworks\native\services\inputflinger\EventHub.cpp
参考文章:《深入理解Android 卷III》第五章 深入理解Android输入系统: http://blog.csdn.net/innost/article/details/47660387
4.测试遇到问题:使用O_RDONLY|O_NONBLOCK打开的FIFO让epoll去监听,echo一个数据到fifo中后,epoll_wait()一直无法阻塞住。
tmpFd = open(argv[i], O_RDONLY|O_NONBLOCK);
解答: epoll,fifo : http://stackoverflow.com/questions/15055065/o-rdwr-on-named-pipes-with-poll
使用fifo,epoll程序是reader
echo aa > tmp/1 是writer
a.
如果reader以O_RDONLY|O_NONBLOCK打开FIFO文件,
当writer写入数据时, epoll_wait会立刻返回;
当writer关闭FIFO之后, reader再次调用epoll_wait, 它也会立刻返回(原因是EPPLLHUP, 描述符被挂断(这个可以从epoll_event结构中看到EPPLLHUP码))
b.
如果reader以O_RDWR打开FIFO文件
当writer写入数据时, epoll_wait会立刻返回;
当writer关闭FIFO之后, reader再次调用epoll_wait, 它并不会立刻返回, 而是继续等待有数据
5.使用分号隔开可以实现在命令行中输入多条语句:eg:# echo 1 > tmp/5; echo 2 > tmp/2
6.试验Demo
inotify.c
#include <unistd.h> #include <stdio.h> #include <sys/inotify.h> #include <string.h> #include <errno.h> /* *²Î¿¼: frameworks\native\services\inputflinger\EventHub.cpp */ /*Usage: inotify <dir> */ int read_process_inotify_fd(int fd) { int res; char event_buf[512]; int event_size; int event_pos = 0; struct inotify_event *event; /* block read */ res = read(fd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; printf("could not get event, %s\n", strerror(errno)); return -1; } /* process * ¶Áµ½µÄÊý¾ÝÊÇ1¸ö»ò¶à¸öinotify_event * ËüÃǵij¤¶È²»Ò»Ñù * Öð¸ö´¦Àí */ while(res >= (int)sizeof(*event)) { event = (struct inotify_event *)(event_buf + event_pos); //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); if(event->len) { if(event->mask & IN_CREATE) { printf("create file: %s\n", event->name); } else { printf("delete file: %s\n", event->name); } } event_size = sizeof(*event) + event->len; // buf[0] with no length res -= event_size; event_pos += event_size; } return 0; } int main(int argc, char **argv) { int mINotifyFd; int result; if (argc != 2) { printf("Usage: %s <dir>\n", argv[0]); return -1; } /* inotify_init */ mINotifyFd = inotify_init(); /* add watch */ result = inotify_add_watch(mINotifyFd, argv[1], IN_DELETE | IN_CREATE); /* read */ while (1) { read_process_inotify_fd(mINotifyFd); } return 0; }
epoll.c
#include <sys/epoll.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #if 0 typedef union epoll_data { // it is a union. void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; #endif #define DATA_MAX_LEN 500 /* usage: epoll <file1> [file2] [file3] ... */ int add_to_epoll(int fd, int epollFd) { int result; struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.fd = fd; result = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem); return result; } void rm_from_epoll(int fd, int epollFd) { epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL); } int main(int argc, char **argv) { int mEpollFd; int i; char buf[DATA_MAX_LEN]; // Maximum number of signalled FDs to handle at a time. static const int EPOLL_MAX_EVENTS = 16; // The array of pending epoll events and the index of the next event to be handled. struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; if (argc < 2) { printf("Usage: %s <file1> [file2] [file3] ...\n", argv[0]); return -1; } /* epoll_create */ mEpollFd = epoll_create(8); // man epoll said that 8 not used. /* for each file: * open it * add it to epoll: epoll_ctl(...EPOLL_CTL_ADD...) */ for (i = 1; i < argc; i++) { //int tmpFd = open(argv[i], O_RDONLY|O_NONBLOCK); int tmpFd = open(argv[i], O_RDWR); add_to_epoll(tmpFd, mEpollFd); } /* epoll_wait */ while (1) { int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1); for (i = 0; i < pollResult; i++) { printf("Event code is: 0x%x\n", mPendingEventItems[i].events); int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN); buf[len] = '\0'; printf("get data: %s\n", buf); //sleep(3); } } return 0; }
inotify_epoll.c
#include <sys/epoll.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <sys/inotify.h> #include <stdlib.h> #include <errno.h> #define DATA_MAX_LEN 500 #define MAX_FILES 1000 static char *base_dir; static char *epoll_files[MAX_FILES]; #if 0 typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; #endif /* usage: epoll <file1> [file2] [file3] ... */ int add_to_epoll(int fd, int epollFd) { int result; struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.fd = fd; result = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem); return result; } void rm_from_epoll(int fd, int epollFd) { epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL); } int get_epoll_fd_for_name(char *name) { int i; char name_to_find[500]; sprintf(name_to_find, "%s/%s", base_dir, name); for (i = 0; i < MAX_FILES; i++) { if (!epoll_files[i]) continue; if (!strcmp(epoll_files[i], name_to_find)) return i; } return -1; } /* *²Î¿¼: frameworks\native\services\inputflinger\EventHub.cpp */ /*Usage: inotify <dir> */ int read_process_inotify_fd(int mINotifyFd, int mEpollFd) { int res; char event_buf[512]; int event_size; int event_pos = 0; struct inotify_event *event; /* read */ res = read(mINotifyFd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; printf("could not get event, %s\n", strerror(errno)); return -1; } /* process * ¶Áµ½µÄÊý¾ÝÊÇ1¸ö»ò¶à¸öinotify_event * ËüÃǵij¤¶È²»Ò»Ñù * Öð¸ö´¦Àí */ while(res >= (int)sizeof(*event)) { event = (struct inotify_event *)(event_buf + event_pos); //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); if(event->len) { if(event->mask & IN_CREATE) { printf("create file: %s\n", event->name); char *name = malloc(512); sprintf(name, "%s/%s", base_dir, event->name); int tmpFd = open(name, O_RDWR); printf("add to epoll: %s\n", name); add_to_epoll(tmpFd, mEpollFd); epoll_files[tmpFd] = name; } else { printf("delete file: %s\n", event->name); int tmpFd = get_epoll_fd_for_name(event->name); if (tmpFd >= 0) { printf("remove from epoll: %s/%s\n", base_dir, event->name); rm_from_epoll(tmpFd, mEpollFd); free(epoll_files[tmpFd]); } } } event_size = sizeof(*event) + event->len; res -= event_size; event_pos += event_size; } return 0; } int main(int argc, char **argv) { int mEpollFd; int i; char buf[DATA_MAX_LEN]; int mINotifyFd; int result; // Maximum number of signalled FDs to handle at a time. static const int EPOLL_MAX_EVENTS = 16; // The array of pending epoll events and the index of the next event to be handled. struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; if (argc != 2) { printf("Usage: %s <tmp>\n", argv[0]); return -1; } base_dir = argv[1]; /* epoll_create */ mEpollFd = epoll_create(8); /* inotify_init */ mINotifyFd = inotify_init(); /* add watch */ result = inotify_add_watch(mINotifyFd, base_dir, IN_DELETE | IN_CREATE); add_to_epoll(mINotifyFd, mEpollFd); /* epoll_wait */ while (1) { int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1); for (i = 0; i < pollResult; i++) { if (mPendingEventItems[i].data.fd == mINotifyFd) { read_process_inotify_fd(mINotifyFd, mEpollFd); } else { printf("Reason: 0x%x\n", mPendingEventItems[i].events); int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN); buf[len] = '\0'; printf("get data: %s\n", buf); //sleep(3); } } } return 0; }
二、双向线程间通信(socketpair)
参考: frameworks/native/libs/input/InputTransport.cpp
1.binder只能单向由Client发起请求,而Service无法主动传输数据,所以单binder是不行的。
2.socketpair可以实现双向通信,但是缺点非常明显,只能在线程间通信或有亲缘关系的父子进程之间的通信。
3.socketpair是基于网络的,fd[0]和fd[1]都关联了一个接受和发送缓冲区。
4.使用socketpair进行任意两个进程间通信的方法
socketpair产生两个fd,fd[0]和fd[1],fd[1]通过binder与另一个进程进行通信。
5.试验Demo
#include <pthread.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #define SOCKET_BUFFER_SIZE (32768U) /* ²Î¿¼: * frameworks\native\libs\input\InputTransport.cpp */ void *function_thread1 (void *arg) { int fd = (int)arg; char buf[500]; int len; int cnt = 0; while (1) { /* Ïò mainÏ̷߳¢³ö: Hello, main thread */ len = sprintf(buf, "Hello, main thread, cnt = %d", cnt++); write(fd, buf, len); /* ¶ÁÈ¡Êý¾Ý(mainÏ̷߳¢»ØµÄÊý¾Ý) */ len = read(fd, buf, 500); buf[len] = '\0'; printf("%s\n", buf); sleep(2); } return NULL; } int main(int argc, char **argv) { int sockets[2]; socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets); int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); /* ´´½¨Ïß³Ì1 */ pthread_t threadID; pthread_create(&threadID, NULL, function_thread1, (void *)sockets[1]); char buf[500]; int len; int cnt = 0; int fd = sockets[0]; while(1) { /* ¶ÁÊý¾Ý: Ïß³Ì1·¢³öµÄÊý¾Ý */ len = read(fd, buf, 500); buf[len] = '\0'; printf("%s\n", buf); /* main threadÏòthread1 ·¢³ö: Hello, thread1 */ len = sprintf(buf, "Hello, thread1, cnt = %d", cnt++); write(fd, buf, len); } }
三、socketpair借助binder fd实现任意进程之间的双向通信
1.参考代码: frameworks\base\core\jni\android_view_InputChannel.cpp (用binder传文件句柄)
server端写fd: android_view_InputChannel_nativeWriteToParcel parcel->writeDupFileDescriptor client端读fd: android_view_InputChannel_nativeReadFromParcel int rawFd = parcel->readFileDescriptor(); int dupFd = dup(rawFd); frameworks\native\libs\binder\Parcel.cpp
2.进程对fd的管理
struct task_struct中有一个struct files_struct *files成员表示进程打开的文件,files_struct中有一个struct fdtable __rcu *fdt成员,
它表示打开的文件表结构,其内部有一个struct file __rcu **fd,这个fd成员是一个指针数组,进程打开文件获得的句柄就是这个数组的下标,
每一个打开的文件使用一个struct file结构表示(也就是说这个struct file实体不是某一个进程的,每个进程只不过是对其持有一个引用)。
若一个进程想要使用另一个进程的fd的话,那么这个进程必须要在自己的file数组上找到一个空闲项,然后指向这个打开的文件。
3.进程的文件描述符fd是per进程的,不能直接传输给另一个进程使用,binder驱动做了处理,见binder驱动BINDER_TYPE_FD
4.ls -l /proc/$(pid)/fd 可以查看打开进程打开了哪些文件。
5.Parcell.cpp的141行case BINDER_TYPE_FD会去关闭文件句柄,因此需要dup()句柄。
6.调试技巧
(1)若是概率地执行到某处会出问题,可以在此处之前加while(1)usleep(100);来查看进程的状态。
7.实现Demo
BnGoodbyeService.cpp
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #define LOG_TAG "HelloService" #include "IHelloService.h" namespace android { BnHelloService::BnHelloService() { } BnHelloService::BnHelloService(int fd) { this->fd = fd; } status_t BnHelloService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { /* 解析数据,调用sayhello/sayhello_to */ switch (code) { case HELLO_SVR_CMD_SAYHELLO: { sayhello(); reply->writeInt32(0); /* no exception */ return NO_ERROR; } break; case HELLO_SVR_CMD_SAYHELLO_TO: { /* 从data中取出参数 */ int32_t policy = data.readInt32(); String16 name16_tmp = data.readString16(); /* IHelloService */ String16 name16 = data.readString16(); String8 name8(name16); int cnt = sayhello_to(name8.string()); /* 把返回值写入reply传回去 */ reply->writeInt32(0); /* no exception */ reply->writeInt32(cnt); return NO_ERROR; } break; case HELLO_SVR_CMD_GET_FD: { int fd = this->get_fd(); reply->writeInt32(0); /* no exception */ /* 参考: * frameworks\base\core\jni\android_view_InputChannel.cpp * android_view_InputChannel_nativeWriteToParcel */ reply->writeDupFileDescriptor(fd); /*关键是这个函数,驱动会做对应的处理*/ return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } void BnHelloService::sayhello(void) { static int cnt = 0; ALOGI("say hello : %d\n", ++cnt); } int BnHelloService::sayhello_to(const char *name) { static int cnt = 0; ALOGI("say hello to %s : %d\n", name, ++cnt); return cnt; } int BnHelloService::get_fd(void) { return fd; } }
BpHelloService.cpp
#include "IHelloService.h" namespace android { class BpHelloService: public BpInterface<IHelloService> { public: BpHelloService(const sp<IBinder>& impl) : BpInterface<IHelloService>(impl) { } void sayhello(void) { /* 构造/发送数据 */ Parcel data, reply; data.writeInt32(0); data.writeString16(String16("IHelloService")); remote()->transact(HELLO_SVR_CMD_SAYHELLO, data, &reply); } int sayhello_to(const char *name) { /* 构造/发送数据 */ Parcel data, reply; int exception; data.writeInt32(0); data.writeString16(String16("IHelloService")); data.writeString16(String16(name)); remote()->transact(HELLO_SVR_CMD_SAYHELLO_TO, data, &reply); exception = reply.readInt32(); if (exception) return -1; else return reply.readInt32(); } int get_fd(void) { /* 构造/发送数据 */ Parcel data, reply; int exception; data.writeInt32(0); data.writeString16(String16("IHelloService")); remote()->transact(HELLO_SVR_CMD_GET_FD, data, &reply); exception = reply.readInt32(); if (exception) return -1; else { /* 参考: * frameworks\base\core\jni\android_view_InputChannel.cpp * android_view_InputChannel_nativeReadFromParcel */ /* * 也可以收到这个fd,但是这个fd还是需要dup()一下, * 因为parcel的析构函数中会释放它. */ int rawFd = reply.readFileDescriptor(); return dup(rawFd); } } }; IMPLEMENT_META_INTERFACE(HelloService, "android.media.IHelloService"); }
IHelloService.h
/* 参考: frameworks\av\include\media\IMediaPlayerService.h */ #ifndef ANDROID_IHELLOERVICE_H #define ANDROID_IHELLOERVICE_H #include <utils/Errors.h> // for status_t #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define HELLO_SVR_CMD_SAYHELLO 1 #define HELLO_SVR_CMD_SAYHELLO_TO 2 #define HELLO_SVR_CMD_GET_FD 3 namespace android { class IHelloService: public IInterface { public: DECLARE_META_INTERFACE(HelloService); virtual void sayhello(void) = 0; virtual int sayhello_to(const char *name) = 0; virtual int get_fd(void) = 0; }; class BnHelloService: public BnInterface<IHelloService> { private: int fd; public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual void sayhello(void); virtual int sayhello_to(const char *name); virtual int get_fd(void); BnHelloService(); BnHelloService(int fd); }; } #endif
test_client.cpp
#define LOG_TAG "TestService" //#define LOG_NDEBUG 0 #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <unistd.h> #include "IHelloService.h" #include "IGoodbyeService.h" using namespace android; /* ./test_client <hello|goodbye> * ./test_client <readfile> * ./test_client <hello|goodbye> <name> */ int main(int argc, char **argv) { int cnt; if (argc < 2){ ALOGI("Usage:\n"); ALOGI("%s <readfile>\n", argv[0]); ALOGI("%s <hello|goodbye>\n", argv[0]); ALOGI("%s <hello|goodbye> <name>\n", argv[0]); return -1; } /* getService */ /* 打开驱动, mmap */ sp<ProcessState> proc(ProcessState::self()); /* 获得BpServiceManager */ sp<IServiceManager> sm = defaultServiceManager(); if (strcmp(argv[1], "hello") == 0) { sp<IBinder> binder = sm->getService(String16("hello")); if (binder == 0) { ALOGI("can't get hello service\n"); return -1; } /* service肯定是BpHelloServie指针 */ sp<IHelloService> service = interface_cast<IHelloService>(binder); /* 调用Service的函数 */ if (argc < 3) { service->sayhello(); ALOGI("client call sayhello"); } else { cnt = service->sayhello_to(argv[2]); ALOGI("client call sayhello_to, cnt = %d", cnt); } } else if (strcmp(argv[1], "readfile") == 0) { sp<IBinder> binder = sm->getService(String16("hello")); if (binder == 0) { ALOGI("can't get hello service\n"); return -1; } /* service肯定是BpHelloServie指针 */ sp<IHelloService> service = interface_cast<IHelloService>(binder); /* 调用Service的函数 */ /* * 此时binder驱动已经为进程处理了task_struct的struct file, 当前进程 * 可以直接操作这个文件fd了。 */ int fd = service->get_fd(); ALOGI("client call get_fd = %d", fd); char buf[500]; int len; int cnt = 0; while (1) { /* 向 test_server 进程发出: Hello, test_server */ len = sprintf(buf, "Hello, test_server, cnt = %d", cnt++); /*目前这个write、read就没有再经过binder了。*/ write(fd, buf, len); /* 读取数据(test_server进程发回的数据) */ len = read(fd, buf, 500); buf[len] = '\0'; ALOGI("%s\n", buf); sleep(3); } } else { sp<IBinder> binder = sm->getService(String16("goodbye")); if (binder == 0) { ALOGI("can't get goodbye service\n"); return -1; } /* service肯定是BpGoodbyeServie指针 */ sp<IGoodbyeService> service = interface_cast<IGoodbyeService>(binder); /* 调用Service的函数 */ if (argc < 3) { service->saygoodbye(); ALOGI("client call saygoodbye"); } else { cnt = service->saygoodbye_to(argv[2]); ALOGI("client call saygoodbye_to, cnt = %d", cnt); } } return 0; }
test_server.cpp
/* 参考: frameworks\av\media\mediaserver\Main_mediaserver.cpp */ #define LOG_TAG "TestService" //#define LOG_NDEBUG 0 #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include "IHelloService.h" #include "IGoodbyeService.h" #define SOCKET_BUFFER_SIZE (32768U) using namespace android; /* 参考: * http://blog.csdn.net/linan_nwu/article/details/8222349 */ class MyThread: public Thread { private: int fd; public: MyThread() {} MyThread(int fd) { this->fd = fd; } //如果返回true,循环调用此函数,返回false下一次不会再调用此函数 bool threadLoop() { char buf[500]; int len; int cnt = 0; while(1) { /* 读数据: test_client发出的数据 */ len = read(fd, buf, 500); buf[len] = '\0'; ALOGI("%s\n", buf); /* 向 test_client 发出: Hello, test_client */ len = sprintf(buf, "Hello, test_client, cnt = %d", cnt++); write(fd, buf, len); } return true; } }; /* usage : test_server */ int main(void) { int sockets[2]; socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets); int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); /* 创建一个线程, 用于跟test_client使用socketpiar通信 */ sp<MyThread> th = new MyThread(sockets[0]); th->run(); /* addService */ /* while(1){ read data, 解析数据, 调用服务函数 } */ /* 打开驱动, mmap */ sp<ProcessState> proc(ProcessState::self()); /* 获得BpServiceManager */ sp<IServiceManager> sm = defaultServiceManager(); sm->addService(String16("hello"), new BnHelloService(sockets[1])); /*将sockets[1]传给远端进程*/ sm->addService(String16("goodbye"), new BnGoodbyeService()); /*这个是无关的*/ /* 循环体 */ ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }
IGoodbyeService.h
/* 参考: frameworks\av\include\media\IMediaPlayerService.h */ #ifndef ANDROID_IGOODBYEERVICE_H #define ANDROID_IGOODBYEERVICE_H #include <utils/Errors.h> // for status_t #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define GOODBYE_SVR_CMD_SAYGOODBYE 1 #define GOODBYE_SVR_CMD_SAYGOODBYE_TO 2 namespace android { class IGoodbyeService: public IInterface { public: DECLARE_META_INTERFACE(GoodbyeService); virtual void saygoodbye(void) = 0; virtual int saygoodbye_to(const char *name) = 0; }; class BnGoodbyeService: public BnInterface<IGoodbyeService> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual void saygoodbye(void); virtual int saygoodbye_to(const char *name); }; } #endif
BnGoodbyeService.cpp
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #define LOG_TAG "GoodbyeService" #include "IGoodbyeService.h" namespace android { status_t BnGoodbyeService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { /* 解析数据,调用saygoodbye/saygoodbye_to */ switch (code) { case GOODBYE_SVR_CMD_SAYGOODBYE: { saygoodbye(); reply->writeInt32(0); /* no exception */ return NO_ERROR; } break; case GOODBYE_SVR_CMD_SAYGOODBYE_TO: { /* 从data中取出参数 */ int32_t policy = data.readInt32(); String16 name16_tmp = data.readString16(); /* IGoodbyeService */ String16 name16 = data.readString16(); String8 name8(name16); int cnt = saygoodbye_to(name8.string()); /* 把返回值写入reply传回去 */ reply->writeInt32(0); /* no exception */ reply->writeInt32(cnt); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } void BnGoodbyeService::saygoodbye(void) { static int cnt = 0; ALOGI("say goodbye : %d\n", ++cnt); } int BnGoodbyeService::saygoodbye_to(const char *name) { static int cnt = 0; ALOGI("say goodbye to %s : %d\n", name, ++cnt); return cnt; } }
BpGoodbyeService.cpp
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #include "IGoodbyeService.h" namespace android { class BpGoodbyeService: public BpInterface<IGoodbyeService> { public: BpGoodbyeService(const sp<IBinder>& impl) : BpInterface<IGoodbyeService>(impl) { } void saygoodbye(void) { /* 构造/发送数据 */ Parcel data, reply; data.writeInt32(0); data.writeString16(String16("IGoodbyeService")); remote()->transact(GOODBYE_SVR_CMD_SAYGOODBYE, data, &reply); } int saygoodbye_to(const char *name) { /* 构造/发送数据 */ Parcel data, reply; int exception; data.writeInt32(0); data.writeString16(String16("IGoodbyeService")); data.writeString16(String16(name)); remote()->transact(GOODBYE_SVR_CMD_SAYGOODBYE_TO, data, &reply); exception = reply.readInt32(); if (exception) return -1; else return reply.readInt32(); } }; IMPLEMENT_META_INTERFACE(GoodbyeService, "android.media.IGoodbyeService"); }
posted on 2019-05-13 10:16 Hello-World3 阅读(894) 评论(0) 编辑 收藏 举报