注意,这里的参数reply = 0,表示这是一个BC_TRANSACTION命令。
        前面我们提到,传给驱动程序的handle值为0,即这里的tr->target.handle = 0,表示请求的目标Binder对象是Service Manager,因此有:

  1. target_node = binder_context_mgr_node;
  2. target_proc = target_node->proc;
  3. target_list = &target_proc->todo;
  4. target_wait = &target_proc->wait;

其中binder_context_mgr_node是在Service Manager通知Binder驱动程序它是守护过程时创建的。

接着创建一个待完成事项tcomplete,它的类型为struct binder_work,这是等一会要保存在当前线程的todo队列去的,表示当前线程有一个待完成的事务。紧跟着创建一个待处理事务t,它的类型为struct binder_transaction,这是等一会要存在到Service Manager的todo队列去的,表示Service Manager当前有一个事务需要处理。同时,这个待处理事务t也要存放在当前线程的待完成事务transaction_stack列表中去:

  1. t->from_parent = thread->transaction_stack;
  2. thread->transaction_stack = t;

这样表明当前线程还有事务要处理。

继续往下看,就是分别把tcomplete和t放在当前线程thread和Service Manager进程的todo队列去了:

  1. t->work.type = BINDER_WORK_TRANSACTION;
  2. list_add_tail(&t->work.entry, target_list);
  3. tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
  4. list_add_tail(&tcomplete->entry, &thread->todo);

最后,Service Manager有事情可做了,就要唤醒它了:

  1. wake_up_interruptible(target_wait);

前面我们提到,此时Service Manager正在等待Client的请求,也就是Service Manager此时正在进入到Binder驱动程序的binder_thread_read函数中,并且休眠在target->wait上,具体参考浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路一文。
        这里,我们暂时忽略Service Manager被唤醒之后的情景,继续看当前线程的执行。
        函数binder_transaction执行完成之后,就一路返回到binder_ioctl函数里去了。函数binder_ioctl从binder_thread_write函数调用处返回后,发现bwr.read_size大于0,于是就进入到binder_thread_read函数去了:

  1. static int
  2. binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
  3. void  __user *buffer, int size, signed long *consumed, int non_block)
  4. {
  5. void __user *ptr = buffer + *consumed;
  6. void __user *end = buffer + size;
  7. int ret = 0;
  8. int wait_for_proc_work;
  9. if (*consumed == 0) {
  10. if (put_user(BR_NOOP, (uint32_t __user *)ptr))
  11. return -EFAULT;
  12. ptr += sizeof(uint32_t);
  13. }
  14. retry:
  15. wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
  16. ......
  17. if (wait_for_proc_work) {
  18. ......
  19. } else {
  20. if (non_block) {
  21. if (!binder_has_thread_work(thread))
  22. ret = -EAGAIN;
  23. } else
  24. ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
  25. }
  26. ......
  27. while (1) {
  28. uint32_t cmd;
  29. struct binder_transaction_data tr;
  30. struct binder_work *w;
  31. struct binder_transaction *t = NULL;
  32. if (!list_empty(&thread->todo))
  33. w = list_first_entry(&thread->todo, struct binder_work, entry);
  34. else if (!list_empty(&proc->todo) && wait_for_proc_work)
  35. w = list_first_entry(&proc->todo, struct binder_work, entry);
  36. else {
  37. if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
  38. goto retry;
  39. break;
  40. }
  41. if (end - ptr < sizeof(tr) + 4)
  42. break;
  43. switch (w->type) {
  44. ......
  45. case BINDER_WORK_TRANSACTION_COMPLETE: {
  46. cmd = BR_TRANSACTION_COMPLETE;
  47. if (put_user(cmd, (uint32_t __user *)ptr))
  48. return -EFAULT;
  49. ptr += sizeof(uint32_t);
  50. binder_stat_br(proc, thread, cmd);
  51. if (binder_debug_mask & BINDER_DEBUG_TRANSACTION_COMPLETE)
  52. printk(KERN_INFO "binder: %d:%d BR_TRANSACTION_COMPLETE\n",
  53. proc->pid, thread->pid);
  54. list_del(&w->entry);
  55. kfree(w);
  56. binder_stats.obj_deleted[BINDER_STAT_TRANSACTION_COMPLETE]++;
  57. } break;
  58. ......
  59. }
  60. if (!t)
  61. continue;
  62. ......
  63. }
  64. done:
  65. ......
  66. return 0;
  67. }

函数首先是写入一个操作码BR_NOOP到用户传进来的缓冲区中去。

回忆一下上面的binder_transaction函数,这里的thread->transaction_stack != NULL,并且thread->todo也不为空,所以线程不会进入休眠状态。

进入while循环中,首先是从thread->todo队列中取回待处理事项w,w的类型为BINDER_WORK_TRANSACTION_COMPLETE,这也是在binder_transaction函数里面设置的。对BINDER_WORK_TRANSACTION_COMPLETE的处理也很简单,只是把一个操作码BR_TRANSACTION_COMPLETE写回到用户传进来的缓冲区中去。这时候,用户传进来的缓冲区就包含两个操作码了,分别是BR_NOOP和BINDER_WORK_TRANSACTION_COMPLETE。

binder_thread_read执行完之后,返回到binder_ioctl函数中,将操作结果写回到用户空间中去:

  1. if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
  2. ret = -EFAULT;
  3. goto err;
  4. }

最后就返回到IPCThreadState::talkWithDriver函数中了。

IPCThreadState::talkWithDriver函数从下面语句:

  1. ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

返回后,首先是清空之前写入Binder驱动程序的内容:

  1. if (bwr.write_consumed > 0) {
  2. if (bwr.write_consumed < (ssize_t)mOut.dataSize())
  3. mOut.remove(0, bwr.write_consumed);
  4. else
  5. mOut.setDataSize(0);
  6. }

接着是设置从Binder驱动程序读取的内容:

  1. if (bwr.read_consumed > 0) {
  2. mIn.setDataSize(bwr.read_consumed);
  3. mIn.setDataPosition(0);
  4. }

然后就返回到IPCThreadState::waitForResponse去了。IPCThreadState::waitForResponse函数的处理也很简单,就是处理刚才从Binder驱动程序读入内容了。从前面的分析中,我们知道,从Binder驱动程序读入的内容就是两个整数了,分别是BR_NOOP和BR_TRANSACTION_COMPLETE。对BR_NOOP的处理很简单,正如它的名字所示,什么也不做;而对BR_TRANSACTION_COMPLETE的处理,就分情况了,如果这个请求是异步的,那个整个BC_TRANSACTION操作就完成了,如果这个请求是同步的,即要等待回复的,也就是reply不为空,那么还要继续通过IPCThreadState::talkWithDriver进入到Binder驱动程序中去等待BC_TRANSACTION操作的处理结果。

这里属于后一种情况,于是再次通过IPCThreadState::talkWithDriver进入到Binder驱动程序的binder_ioctl函数中。不过这一次在binder_ioctl函数中,bwr.write_size等于0,而bwr.read_size大于0,于是再次进入到binder_thread_read函数中。这时候thread->transaction_stack仍然不为NULL,不过thread->todo队列已经为空了,因为前面我们已经处理过thread->todo队列的内容了,于是就通过下面语句:

  1. ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));

进入休眠状态了,等待Service Manager的唤醒。

现在,我们终于可以回到Service Manager被唤醒之后的过程了。前面我们说过,Service Manager此时正在binder_thread_read函数中休眠中:

  1. static int
  2. binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
  3. void  __user *buffer, int size, signed long *consumed, int non_block)
  4. {
  5. void __user *ptr = buffer + *consumed;
  6. void __user *end = buffer + size;
  7. int ret = 0;
  8. int wait_for_proc_work;
  9. if (*consumed == 0) {
  10. if (put_user(BR_NOOP, (uint32_t __user *)ptr))
  11. return -EFAULT;
  12. ptr += sizeof(uint32_t);
  13. }
  14. retry:
  15. wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
  16. ......
  17. if (wait_for_proc_work) {
  18. ......
  19. if (non_block) {
  20. if (!binder_has_proc_work(proc, thread))
  21. ret = -EAGAIN;
  22. } else
  23. ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
  24. } else {
  25. ......
  26. }
  27. ......
  28. while (1) {
  29. uint32_t cmd;
  30. struct binder_transaction_data tr;
  31. struct binder_work *w;
  32. struct binder_transaction *t = NULL;
  33. if (!list_empty(&thread->todo))
  34. w = list_first_entry(&thread->todo, struct binder_work, entry);
  35. else if (!list_empty(&proc->todo) && wait_for_proc_work)
  36. w = list_first_entry(&proc->todo, struct binder_work, entry);
  37. else {
  38. if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
  39. goto retry;
  40. break;
  41. }
  42. if (end - ptr < sizeof(tr) + 4)
  43. break;
  44. switch (w->type) {
  45. case BINDER_WORK_TRANSACTION: {
  46. t = container_of(w, struct binder_transaction, work);
  47. } break;
  48. ......
  49. }
  50. if (!t)
  51. continue;
  52. BUG_ON(t->buffer == NULL);
  53. if (t->buffer->target_node) {
  54. struct binder_node *target_node = t->buffer->target_node;
  55. tr.target.ptr = target_node->ptr;
  56. tr.cookie =  target_node->cookie;
  57. t->saved_priority = task_nice(current);
  58. if (t->priority < target_node->min_priority &&
  59. !(t->flags & TF_ONE_WAY))
  60. binder_set_nice(t->priority);
  61. else if (!(t->flags & TF_ONE_WAY) ||
  62. t->saved_priority > target_node->min_priority)
  63. binder_set_nice(target_node->min_priority);
  64. cmd = BR_TRANSACTION;
  65. } else {
  66. ......
  67. }
  68. tr.code = t->code;
  69. tr.flags = t->flags;
  70. tr.sender_euid = t->sender_euid;
  71. if (t->from) {
  72. struct task_struct *sender = t->from->proc->tsk;
  73. tr.sender_pid = task_tgid_nr_ns(sender, current->nsproxy->pid_ns);
  74. } else {
  75. ......
  76. }
  77. tr.data_size = t->buffer->data_size;
  78. tr.offsets_size = t->buffer->offsets_size;
  79. tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
  80. tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
  81. if (put_user(cmd, (uint32_t __user *)ptr))
  82. return -EFAULT;
  83. ptr += sizeof(uint32_t);
  84. if (copy_to_user(ptr, &tr, sizeof(tr)))
  85. return -EFAULT;
  86. ptr += sizeof(tr);
  87. ......
  88. list_del(&t->work.entry);
  89. t->buffer->allow_user_free = 1;
  90. if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
  91. t->to_parent = thread->transaction_stack;
  92. t->to_thread = thread;
  93. thread->transaction_stack = t;
  94. } else {
  95. ......
  96. }
  97. break;
  98. }
  99. done:
  100. *consumed = ptr - buffer;
  101. ......
  102. return 0;
  103. }

这里就是从语句中唤醒了:

  1. ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));

Service Manager唤醒过来看,继续往下执行,进入到while循环中。首先是从proc->todo中取回待处理事项w。这个事项w的类型是BINDER_WORK_TRANSACTION,这是上面调用binder_transaction的时候设置的,于是通过w得到待处理事务t:

  1. t = container_of(w, struct binder_transaction, work);

接下来的内容,就把cmd和t->buffer的内容拷贝到用户传进来的缓冲区去了,这里就是Service Manager从用户空间传进来的缓冲区了:

  1. if (put_user(cmd, (uint32_t __user *)ptr))
  2. return -EFAULT;
  3. ptr += sizeof(uint32_t);
  4. if (copy_to_user(ptr, &tr, sizeof(tr)))
  5. return -EFAULT;
  6. ptr += sizeof(tr);

注意,这里先是把t->buffer的内容拷贝到本地变量tr中,再拷贝到用户空间缓冲区去。关于t->buffer内容的拷贝,请参考Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析一文,它的一个关键地方是Binder驱动程序和Service Manager守护进程共享了同一个物理内存的内容,拷贝的只是这个物理内存在用户空间的虚拟地址回去:

  1. tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
  2. tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));

对于Binder驱动程序这次操作来说,这个事项就算是处理完了,就要从todo队列中删除了:

  1. list_del(&t->work.entry);

紧接着,还不放删除这个事务,因为它还要等待Service Manager处理完成后,再进一步处理,因此,放在thread->transaction_stack队列中:

  1. t->to_parent = thread->transaction_stack;
  2. t->to_thread = thread;
  3. thread->transaction_stack = t;

还要注意的一个地方是,上面写入的cmd = BR_TRANSACTION,告诉Service Manager守护进程,它要做什么事情,后面我们会看到相应的分析。

这样,binder_thread_read函数就处理完了,回到binder_ioctl函数中,同样是操作结果写回到用户空间的缓冲区中去:

  1. if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
  2. ret = -EFAULT;
  3. goto err;
  4. }

最后,就返回到frameworks/base/cmds/servicemanager/binder.c文件中的binder_loop函数去了:

  1. void binder_loop(struct binder_state *bs, binder_handler func)
  2. {
  3. int res;
  4. struct binder_write_read bwr;
  5. unsigned readbuf[32];
  6. bwr.write_size = 0;
  7. bwr.write_consumed = 0;
  8. bwr.write_buffer = 0;
  9. readbuf[0] = BC_ENTER_LOOPER;
  10. binder_write(bs, readbuf, sizeof(unsigned));
  11. for (;;) {
  12. bwr.read_size = sizeof(readbuf);
  13. bwr.read_consumed = 0;
  14. bwr.read_buffer = (unsigned) readbuf;
  15. res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
  16. if (res < 0) {
  17. LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
  18. break;
  19. }
  20. res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
  21. if (res == 0) {
  22. LOGE("binder_loop: unexpected reply?!\n");
  23. break;
  24. }
  25. if (res < 0) {
  26. LOGE("binder_loop: io error %d %s\n", res, strerror(errno));
  27. break;
  28. }
  29. }
  30. }

这里就是从下面的语句:

  1. res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

返回来了。接着就进入binder_parse函数处理从Binder驱动程序里面读取出来的数据:

  1. int binder_parse(struct binder_state *bs, struct binder_io *bio,
  2. uint32_t *ptr, uint32_t size, binder_handler func)
  3. {
  4. int r = 1;
  5. uint32_t *end = ptr + (size / 4);
  6. while (ptr < end) {
  7. uint32_t cmd = *ptr++;
  8. switch(cmd) {
  9. ......
  10. case BR_TRANSACTION: {
  11. struct binder_txn *txn = (void *) ptr;
  12. ......
  13. if (func) {
  14. unsigned rdata[256/4];
  15. struct binder_io msg;
  16. struct binder_io reply;
  17. int res;
  18. bio_init(&reply, rdata, sizeof(rdata), 4);
  19. bio_init_from_txn(&msg, txn);
  20. res = func(bs, txn, &msg, &reply);
  21. binder_send_reply(bs, &reply, txn->data, res);
  22. }
  23. ptr += sizeof(*txn) / sizeof(uint32_t);
  24. break;
  25. }
  26. ......
  27. default:
  28. LOGE("parse: OOPS %d\n", cmd);
  29. return -1;
  30. }
  31. }
  32. return r;
  33. }

前面我们说过,Binder驱动程序写入到用户空间的缓冲区中的cmd为BR_TRANSACTION,因此,这里我们只关注BR_TRANSACTION相关的逻辑。

这里用到的两个数据结构struct binder_txn和struct binder_io可以参考前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析,这里就不复述了。

接着往下看,函数调bio_init来初始化reply变量:

  1. void bio_init(struct binder_io *bio, void *data,
  2. uint32_t maxdata, uint32_t maxoffs)
  3. {
  4. uint32_t n = maxoffs * sizeof(uint32_t);
  5. if (n > maxdata) {
  6. bio->flags = BIO_F_OVERFLOW;
  7. bio->data_avail = 0;
  8. bio->offs_avail = 0;
  9. return;
  10. }
  11. bio->data = bio->data0 = data + n;
  12. bio->offs = bio->offs0 = data;
  13. bio->data_avail = maxdata - n;
  14. bio->offs_avail = maxoffs;
  15. bio->flags = 0;
  16. }

最后,真正进行处理的函数是从参数中传进来的函数指针func,这里就是定义在frameworks/base/cmds/servicemanager/service_manager.c文件中的svcmgr_handler函数:

  1. int svcmgr_handler(struct binder_state *bs,
  2. struct binder_txn *txn,
  3. struct binder_io *msg,
  4. struct binder_io *reply)
  5. {
  6. struct svcinfo *si;
  7. uint16_t *s;
  8. unsigned len;
  9. void *ptr;
  10. uint32_t strict_policy;
  11. //    LOGI("target=%p code=%d pid=%d uid=%d\n",
  12. //         txn->target, txn->code, txn->sender_pid, txn->sender_euid);
  13. if (txn->target != svcmgr_handle)
  14. return -1;
  15. // Equivalent to Parcel::enforceInterface(), reading the RPC
  16. // header with the strict mode policy mask and the interface name.
  17. // Note that we ignore the strict_policy and don't propagate it
  18. // further (since we do no outbound RPCs anyway).
  19. strict_policy = bio_get_uint32(msg);
  20. s = bio_get_string16(msg, &len);
  21. if ((len != (sizeof(svcmgr_id) / 2)) ||
  22. memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
  23. fprintf(stderr,"invalid id %s\n", str8(s));
  24. return -1;
  25. }
  26. switch(txn->code) {
  27. case SVC_MGR_GET_SERVICE:
  28. case SVC_MGR_CHECK_SERVICE:
  29. s = bio_get_string16(msg, &len);
  30. ptr = do_find_service(bs, s, len);
  31. if (!ptr)
  32. break;
  33. bio_put_ref(reply, ptr);
  34. return 0;
  35. ......
  36. }
  37. default:
  38. LOGE("unknown code %d\n", txn->code);
  39. return -1;
  40. }
  41. bio_put_uint32(reply, 0);
  42. return 0;
  43. }

这里, Service Manager要处理的code是SVC_MGR_CHECK_SERVICE,这是在前面的BpServiceManager::checkService函数里面设置的。

回忆一下,在BpServiceManager::checkService时,传给Binder驱动程序的参数为:

  1. writeInt32(IPCThreadState::self()->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
  2. writeString16("android.os.IServiceManager");
  3. writeString16("media.player");

这里的语句:

  1. strict_policy = bio_get_uint32(msg);
  2. s = bio_get_string16(msg, &len);
  3. s = bio_get_string16(msg, &len);

其中,会验证一下传进来的第二个参数,即"android.os.IServiceManager"是否正确,这个是验证RPC头,注释已经说得很清楚了。

最后,就是调用do_find_service函数查找是存在名称为"media.player"的服务了。回忆一下前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析,MediaPlayerService已经把一个名称为"media.player"的服务注册到Service Manager中,所以这里一定能找到。我们看看do_find_service这个函数:

  1. void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)
  2. {
  3. struct svcinfo *si;
  4. si = find_svc(s, len);
  5. //    LOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);
  6. if (si && si->ptr) {
  7. return si->ptr;
  8. } else {
  9. return 0;
  10. }
  11. }

这里又调用了find_svc函数:

  1. struct svcinfo *find_svc(uint16_t *s16, unsigned len)
  2. {
  3. struct svcinfo *si;
  4. for (si = svclist; si; si = si->next) {
  5. if ((len == si->len) &&
  6. !memcmp(s16, si->name, len * sizeof(uint16_t))) {
  7. return si;
  8. }
  9. }
  10. return 0;
  11. }

就是在svclist列表中查找对应名称的svcinfo了。

然后返回到do_find_service函数中。回忆一下前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析,这里的si->ptr就是指MediaPlayerService这个Binder实体在Service Manager进程中的句柄值了。

回到svcmgr_handler函数中,调用bio_put_ref函数将这个Binder引用写回到reply参数。我们看看bio_put_ref的实现:

  1. void bio_put_ref(struct binder_io *bio, void *ptr)
  2. {
  3. struct binder_object *obj;
  4. if (ptr)
  5. obj = bio_alloc_obj(bio);
  6. else
  7. obj = bio_alloc(bio, sizeof(*obj));
  8. if (!obj)
  9. return;
  10. obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
  11. obj->type = BINDER_TYPE_HANDLE;
  12. obj->pointer = ptr;
  13. obj->cookie = 0;
  14. }

这里很简单,就是把一个类型为BINDER_TYPE_HANDLE的binder_object写入到reply缓冲区中去。这里的binder_object就是相当于是flat_binder_obj了,具体可以参考Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析一文。

再回到svcmgr_handler函数中,最后,还写入一个0值到reply缓冲区中,表示操作结果码:

  1. bio_put_uint32(reply, 0);

最后返回到binder_parse函数中,调用binder_send_reply函数将操作结果反馈给Binder驱动程序:

  1. void binder_send_reply(struct binder_state *bs,
  2. struct binder_io *reply,
  3. void *buffer_to_free,
  4. int status)
  5. {
  6. struct {
  7. uint32_t cmd_free;
  8. void *buffer;
  9. uint32_t cmd_reply;
  10. struct binder_txn txn;
  11. } __attribute__((packed)) data;
  12. data.cmd_free = BC_FREE_BUFFER;
  13. data.buffer = buffer_to_free;
  14. data.cmd_reply = BC_REPLY;
  15. data.txn.target = 0;
  16. data.txn.cookie = 0;
  17. data.txn.code = 0;
  18. if (status) {
  19. data.txn.flags = TF_STATUS_CODE;
  20. data.txn.data_size = sizeof(int);
  21. data.txn.offs_size = 0;
  22. data.txn.data = &status;
  23. data.txn.offs = 0;
  24. } else {
  25. data.txn.flags = 0;
  26. data.txn.data_size = reply->data - reply->data0;
  27. data.txn.offs_size = ((char*) reply->offs) - ((char*) reply->offs0);
  28. data.txn.data = reply->data0;
  29. data.txn.offs = reply->offs0;
  30. }
  31. binder_write(bs, &data, sizeof(data));
  32. }

Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析(2)...相关推荐

  1. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder ...

  2. 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6627260 在前面一篇文章浅谈Service Manager成为Android进程间 ...

  3. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析(1)

    在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路中,介绍了在Android系统中Binder进程间通信机 ...

  4. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  5. Android系统默认Home应用程序(Launcher)的启动过程源代码分析(3)

    Step 13.  ActivityStack.startActivityLocked 这个函数定义在frameworks/base/services/java/com/android/server/ ...

  6. Android系统默认Home应用程序 Launcher 的启动过程源代码分析

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 在前面一 ...

  7. Android应用程序绑定服务(bindService)的过程源代码分析

    Android应用程序组件Service与Activity一样,既可以在新的进程中启动,也可以在应用程序进程内部启动:前面我们已经分析了在新的进程中启动Service的过程,本文将要介绍在应用程序内部 ...

  8. Android应用程序组件Content Provider的启动过程源代码分析(1)

             通过前面的学习,我们知道在Android系统中,Content Provider可以为不同的应用程序访问相同的数据提供统一的入口.Content Provider一般是运行在独立的进 ...

  9. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析(3)

    Service Manager被唤醒之后,就进入while循环开始处理事务了.这里wait_for_proc_work等于1,并且proc->todo不为空,所以从proc->todo列表 ...

最新文章

  1. 如何在Mac环境下搭建svn服务器端环境
  2. head tail mkdir cp
  3. 内存管理之程序内存分布
  4. Raspberry Pi 3B 安装Miniconda
  5. 数据结构源码笔记(C语言):链接队列
  6. 使用shell脚本完成自动化部署jar包
  7. java封装弊端_JAVA-初步认识-第十三章-同步的好处和弊端
  8. 跨部门不配合工作_跨部门协作,队友总是“甩锅”,这三个方法教你快速避坑!...
  9. E - Flow Gym - 102471E
  10. CentOS 7 + Win 双系统的安装遇到的重要问题
  11. vs2013 资源视图 看不到东西
  12. 读书笔记(随笔5)虚拟终端
  13. 湿空气性质计算,随笔与学习记录 (3.露点温度,绝对湿度)
  14. 【分享实录-猫眼电影】业务纵横捭阖背后的技术拆分与融合
  15. 区块链DAO vs. 股份制:生产关系革命的原因和意义
  16. 积分球测试软件无法创建新文档,关于积分球测试的总结(最新整理)
  17. CP-ABE方案形式化定义及安全模型总结
  18. 首发 阿里P8架构师借助Spring10大功能成功升到P9
  19. 【ffmpeg】——批量合并视频
  20. 安恒西湖论剑周周练Reverse刮开有奖WP

热门文章

  1. echarts柱图根据值显示不同颜色_视频 | Origin画3D柱图,这篇讲透了!
  2. linux ppp漏洞,Linux下ppp拨号的实现(Arm)
  3. 航测大数据量处理_揭秘航测局“航空摄影质量自动检查系统”的“黑科技”
  4. java遍历斐波纳契数列_详解循环、迭代、递归、分治(Leet Code 509 斐波那契数列),实际运用...
  5. html5判断多选框是否选择的函数,复选框(checkbox)、多选框
  6. linux下安装mysql5.7.19,Linux下MySQL5.1升级到高版本MySQL5.7.19详解 | zifangsky的个人博客...
  7. java newfile() bug_java初学者小白?遇到BUG就慌了?有关java异常的十大问题详解!...
  8. SRA 练习:解释 CRUD 矩阵方法,并从上面 “用于化学品跟踪管理系统的 CRUDL 矩阵示例”图中找出被遗漏的需求,并解释是如何找的?
  9. 如何设置Pycharm在打开Terminal终端时,自动进入虚拟环境?
  10. 关于SQL2012里,出现“表中的数据受保护,不能修改”的情况的解决办法