Android Choreographer 初始化
Choreographer Init
frameworks/base/core/java/android/view/ViewRootImpl.java
[ViewRootImpl.java–>ViewRootImpl.ViewRootImpl()]
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession(); mDisplay = display; ...... /** 创建Choreographer */ mChoreographer = Choreographer.getInstance(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); loadSystemProperties(); }
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java–>Choreographer.getInstance()]
/** * Gets the choreographer for the calling thread. Must be called from * a thread that already has a {@link android.os.Looper} associated with it. * * @return The choreographer for this thread. * @throws IllegalStateException if the thread does not have a looper. */ public static Choreographer getInstance() { return sThreadInstance.get(); } ``` [Choreographer.java-->Choreographer.sThreadInstance()] ``` // Thread local storage for the choreographer. private static final ThreadLocal sThreadInstance = new ThreadLocal() { @Override protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } return new Choreographer(looper); } };
[Choreographer.java–>Choreographer.Choreographer()]
private Choreographer(Looper looper) { mLooper = looper; mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } }
1.mLooper
2.mHandler
3.mDisplayEventReceiver
4.mCallbackQueues
1.mLooper
mLooper = looper –> looper = Looper.myLooper()
/frameworks/base/core/java/android/os/Looper.java
[Looper.java–>Looper.myLooper()]
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ /** 返回和当前线程绑定的Looper */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
2.mHandler
mHandler = new FrameHandler(looper)
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java–>Choreographer.Choreographer().FrameHandler()]
private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break; } } }
3.mDisplayEventReceiver
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java–>FrameDisplayEventReceiver]
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper) { super(looper); } }
frameworks/base/core/java/android/view/DisplayEventReceiver.java
[DisplayEventReceiver.java–>DisplayEventReceiver()]
/** * Creates a display event receiver. * * @param looper The looper to use when invoking callbacks. */ public DisplayEventReceiver(Looper looper) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue); mCloseGuard.open("dispose"); }
/frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
[android_view_DisplayEventReceiver.cpp–>nativeInit()]
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj) { /** 1. 获取messageQueue */ sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } /** 2. 创建一个NativeDisplayEventReceiver */ sp receiver = new NativeDisplayEventReceiver(env, receiverWeak, messageQueue); /** 3. 调用NativeDisplayEventReceiver的initialize函数 */ status_t status = receiver->initialize(); if (status) { String8 message; message.appendFormat("Failed to initialize display event receiver. status=%d", status); jniThrowRuntimeException(env, message.string()); return 0; } receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object /** 4. 返回一个NativeDisplayEventReceiver */ return reinterpret_cast(receiver.get());}
3.1 messageQueue
3.2 NativeDisplayEventReceiver
[android_view_DisplayEventReceiver.cpp–>NativeDisplayEventReceiver]
class NativeDisplayEventReceiver : public DisplayEventDispatcher {public: NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, const sp& messageQueue); void dispose();protected: virtual ~NativeDisplayEventReceiver();private: jobject mReceiverWeakGlobal; sp mMessageQueue; /** NativeDisplayEventReceiver中有DisplayEventReceiver,则在创建NativeDisplayEventReceiver 对象时调用DisplayEventReceiver的构造方法 */ DisplayEventReceiver mReceiver; bool mWaitingForVsync; virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);};
/frameworks/native/libs/gui/DisplayEventReceiver.cpp
[DisplayEventReceiver.cpp–>DisplayEventReceiver()]
DisplayEventReceiver::DisplayEventReceiver() { /** 1. 创建ISurfaceComposer */ sp sf(ComposerService::getComposerService()); if (sf != NULL) { /** 2. 调用ISurfaceComposer的createDisplayEventConnection()函数创建EventConnection */ mEventConnection = sf->createDisplayEventConnection(); if (mEventConnection != NULL) { /** 3. 调用getDataChannel函数,mDataChannel对应的是BitTube */ mDataChannel = mEventConnection->getDataChannel(); } }}
3.1.1ComposerService
/frameworks/native/include/private/gui/ComposerService.h
[ComposerService.h]
class ComposerService : public Singleton{ sp mComposerService; sp mDeathObserver; Mutex mLock; ComposerService(); void connectLocked(); void composerServiceDied(); friend class Singleton;public: // Get a connection to the Composer Service. This will block until // a connection is established. static sp getComposerService();};
/frameworks/native/libs/gui/SurfaceComposerClient.cpp
[SurfaceComposerClient.cpp]
/** ComposerService单例模式,在/system/core/include/utils/Singleton.h中有定义 */ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);/** ComposerService构造函数,调用connectLocked()函数 */ComposerService::ComposerService(): Singleton() { Mutex::Autolock _l(mLock); connectLocked();}/** 获取mComposerService,即SurfaceFlinger服务的连接 */void ComposerService::connectLocked() { const String16 name("SurfaceFlinger"); /** >sp mComposerService; getService将mComposerService实例化成一个BpSurfaceComposer对象,定义在ISurfaceComposer.cpp中 其中这里的BpSurfaceComposer已经获得了SurfaceFlinger的service的在binder驱动中的handle值 mComposerService是一个 BpSurfaceComposer(new BpBinder(handle))对象 >getService是从ServiceManager进程中查找"SurfaceFlinger"的Binder对应的handle值, 返回的out型参数mComposerService对应的就是"SurfaceFlinger" binder 服务端的客户端Binder对象,通过这个 mComposerService就可以和"SurfaceFlinger"进行通信 */ while (getService(name, &mComposerService) != NO_ERROR) { usleep(250000); } assert(mComposerService != NULL); // Create the death listener. class DeathObserver : public IBinder::DeathRecipient { ComposerService& mComposerService; virtual void binderDied(const wp& who) { ALOGW("ComposerService remote (surfaceflinger) died [%p]", who.unsafe_get()); mComposerService.composerServiceDied(); } public: DeathObserver(ComposerService& mgr) : mComposerService(mgr) { } }; mDeathObserver = new DeathObserver(*const_cast(this)); IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);}/*static*/ sp ComposerService::getComposerService() { /** static方法获取单例模式下的BpSurfaceComposer(new BpBinder(handle))对象 */ ComposerService& instance = ComposerService::getInstance(); Mutex::Autolock _l(instance.mLock); if (instance.mComposerService == NULL) { ComposerService::getInstance().connectLocked(); assert(instance.mComposerService != NULL); ALOGD("ComposerService reconnected"); } return instance.mComposerService;}void ComposerService::composerServiceDied(){ Mutex::Autolock _l(mLock); mComposerService = NULL; mDeathObserver = NULL;}
/frameworks/native/include/binder/IServiceManager.h
[IServiceManager.h–>IServiceManager::getService()]
templatestatus_t getService(const String16& name, sp* outService){ /** defaultServiceManager返回的进程范围内唯一的BpServiceManager,即 sm = new BpServiceManager(new BpBinder(0)); */ const sp sm = defaultServiceManager(); if (sm != NULL) { /** sm->getService(name): 会调用BpServiceManager中的getService函数, 经过binder驱动程序和service_manager守护进程进行通信,得到service名 称为name的service的handle值,返回的是一个BpBinder(handle) */ /** interface_cast(new BpBinder(handle))会创建一个BpINTERFACE对象, */ *outService = interface_cast(sm->getService(name)); if ((*outService) != NULL) return NO_ERROR; } return NAME_NOT_FOUND;}
sm返回的是一个BpServiceManager对象,该对象是一个Binder的客户端,也就是Service_Manager
服务守护进程的Bp客户端,相当于是BpServiceManager(new BpBinder(0)),根据前面的服务名称
”SurfaceFlinger”,最终outService返回的是一个BpSurfaceComposer对象
/frameworks/native/services/surfaceflinger/SurfaceFlinger.h
[SurfaceFlinger.h]
/** SurfaceFlinger是BnSurfaceComposer的实现者,因此ComposerService::getComposerService()获取的服务,即是SurfaceFlinger */class SurfaceFlinger : public BnSurfaceComposer, private IBinder::DeathRecipient, private HWComposer::EventHandler{
ComposerService::getComposerService()返回的是一个与SurfaceFlinger进程建立Binder进程通信的客户端,服务端就SurfaceFlinger对象本身
3.1.2mEventConnection
mEventConnection = sf->createDisplayEventConnection();
/frameworks/native/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
[SurfaceFlinger_hwc1.cpp–>SurfaceFlinger_hwc1::createDisplayEventConnection()]
/** 返回DisplayEventReceiver构造函数中的mEventConnection是一个BpDisplayEventConnection对象 */sp SurfaceFlinger::createDisplayEventConnection() { return mEventThread->createEventConnection();}
/frameworks/native/services/surfaceflinger/EventThread.cpp
[EventThread.cpp–>EventThread::createEventConnection()]
sp EventThread::createEventConnection() const { return new Connection(const_cast(this));}
[EventThread.cpp–>EventThread::Connection::Connection()]
/** onnection的构造函数会创建一个BitTube对象,BitTube对象中包含一对互联的socket, 一端发送另一端就能收到。并将BitTube对象存储在EventThread::Connection.mChannel里面 */EventThread::Connection::Connection( const sp& eventThread) : count(-1), mEventThread(eventThread), mChannel(new BitTube()){}
frameworks/native/libs/gui/BitTube.cpp
[BitTube.cpp–>BitTube::BitTube()]
static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;BitTube::BitTube() : mSendFd(-1), mReceiveFd(-1){ init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);}
[BitTube.cpp–>BitTube::init()]
void BitTube::init(size_t rcvbuf, size_t sndbuf) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { size_t size = DEFAULT_SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); // sine we don't use the "return channel", we keep it small... setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); fcntl(sockets[0], F_SETFL, O_NONBLOCK); fcntl(sockets[1], F_SETFL, O_NONBLOCK); mReceiveFd = sockets[0]; mSendFd = sockets[1]; } else { mReceiveFd = -errno; ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); }}
/frameworks/native/services/surfaceflinger/EventThread.h
[EventThread.h–>EventThread]
class EventThread : public Thread, private VSyncSource::Callback { /** Connection是一个BnDisplayEventConnection对象 */ class Connection : public BnDisplayEventConnection { public: Connection(const sp& eventThread); status_t postEvent(const DisplayEventReceiver::Event& event); // count >= 1 : continuous event. count is the vsync rate // count == 0 : one-shot event that has not fired // count ==-1 : one-shot event that fired this round / disabled int32_t count; private: virtual ~Connection(); virtual void onFirstRef(); virtual sp getDataChannel() const; virtual void setVsyncRate(uint32_t count); virtual void requestNextVsync(); // asynchronous sp const mEventThread; /** mChannel是一个BitTube */ sp const mChannel; };
3.1.3mDataChannel
mDataChannel= mEventConnection->getDataChannel();
BpDisplayEventConnection.getDataChannel() //IDisplayEventConnection.cpp –>BnDisplayEventConnection.onTransact //IDisplayEventConnection.cpp
–>EventThread::Connection.getDataChannel() //EventThread.cpp
/frameworks/native/services/surfaceflinger/EventThread.cpp
sp<BitTube> EventThread::Connection::getDataChannel() const { return mChannel;}
[BnDisplayEventConnection.onTransact //IDisplayEventConnection.cpp]
status_t BnDisplayEventConnection::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){ switch(code) { case GET_DATA_CHANNEL: { /** 调用Connection.getDataChannel()函数返回前面创建的BitTube对象: */ CHECK_INTERFACE(IDisplayEventConnection, data, reply); sp channel(getDataChannel()); channel->writeToParcel(reply); return NO_ERROR; } case SET_VSYNC_RATE: { CHECK_INTERFACE(IDisplayEventConnection, data, reply); setVsyncRate(data.readUint32()); return NO_ERROR; } case REQUEST_NEXT_VSYNC: { CHECK_INTERFACE(IDisplayEventConnection, data, reply); requestNextVsync(); return NO_ERROR; } } return BBinder::onTransact(code, data, reply, flags);}
/frameworks/native/libs/gui/BitTube.cpp
[BitTube.cpp–>BitTube::writeToParcel()]
status_t BitTube::writeToParcel(Parcel* reply) const{ if (mReceiveFd < 0) return -EINVAL; status_t result = reply->writeDupFileDescriptor(mReceiveFd); close(mReceiveFd); mReceiveFd = -1; return result;}
[BpDisplayEventConnection.getDataChannel() //IDisplayEventConnection.cpp]
virtual sp<BitTube> getDataChannel() const { Parcel data, reply; data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); remote()->transact(GET_DATA_CHANNEL, data, &reply); return new BitTube(reply); }
frameworks/native/libs/gui/BitTube.cpp
[BitTube.cpp–>BitTube::BitTube(const Parcel& data)]
/** 构造函数从Parcel中读取前面在SurfaceFlinger进程中写入的socket接收端的描述符 */BitTube::BitTube(const Parcel& data) : mSendFd(-1), mReceiveFd(-1){ mReceiveFd = dup(data.readFileDescriptor()); if (mReceiveFd < 0) { mReceiveFd = -errno; ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", strerror(-mReceiveFd)); }}
native层的NativeDisplayEventReceiver对象已经创建结束,这时候客户端进程已经有了一个和SurfaceFlinger服务端相连的socket接收端描述符
3.3 NativeDisplayEventReceiver->initialize()
/frameworks/base/libs/androidfw/DisplayEventDispatcher.cpp
[DisplayEventDispatcher.cpp–>DisplayEventDispatcher::initialize()]
status_t DisplayEventDispatcher::initialize() { status_t result = mReceiver.initCheck(); if (result) { ALOGW("Failed to initialize display event receiver, status=%d", result); return result; } /** 参数mReceiver.getFd()返回的是在创建NativeDisplayEventReceiver时从SurfaceFlinger服务端接收回来的socket接收端描述符 */ int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL); if (rc < 0) { return UNKNOWN_ERROR; } return OK;}
/system/core/libutils/Looper.cpp
[Looper.cpp–>Looper::addFd()]
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) { /** 将上面传进来的NativeDisplayEventReceiver对象封装成一个SimpleLooperCallback对象 */ return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);}int Looper::addFd(int fd, int ident, int events, const sp& callback, void* data) {#if DEBUG_CALLBACKS ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, events, callback.get(), data);#endif if (!callback.get()) { if (! mAllowNonCallbacks) { ALOGE("Invalid attempt to set NULL callback but not allowed for this looper."); return -1; } if (ident < 0) { ALOGE("Invalid attempt to set NULL callback with ident < 0."); return -1; } } else { ident = POLL_CALLBACK; } { // acquire lock AutoMutex _l(mLock); Request request; request.fd = fd; request.ident = ident; request.events = events; request.seq = mNextRequestSeq++; request.callback = callback; request.data = data; if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1 /** 1. 创建一个struct epoll_event结构体对象,将对应的内存全部用清0,并作对应的初始化 */ struct epoll_event eventItem; request.initEventItem(&eventItem); /** 2. 查询通过addFd方法已经添加到epoll中监听的文件描述符 */ ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { /** 查询不到的话,则调用epoll_ctl方法设置EPOLL_CTL_ADD属性将对应的文件描述符添加到epoll监听的描述符中 */ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno)); return -1; } mRequests.add(fd, request); } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { if (errno == ENOENT) { // Tolerate ENOENT because it means that an older file descriptor was // closed before its callback was unregistered and meanwhile a new // file descriptor with the same number has been created and is now // being registered for the first time. This error may occur naturally // when a callback has the side-effect of closing the file descriptor // before returning and unregistering itself. Callback sequence number // checks further ensure that the race is benign. // // Unfortunately due to kernel limitations we need to rebuild the epoll // set from scratch because it may contain an old file handle that we are // now unable to remove since its file descriptor is no longer valid. // No such problem would have occurred if we were using the poll system // call instead, but that approach carries others disadvantages.#if DEBUG_CALLBACKS ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor " "being recycled, falling back on EPOLL_CTL_ADD: %s", this, strerror(errno));#endif epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error modifying or adding epoll events for fd %d: %s", fd, strerror(errno)); return -1; } scheduleEpollRebuildLocked(); } else { ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno)); return -1; } } mRequests.replaceValueAt(requestIndex, request); } } // release lock return 1;}
addFd传入的参数EVENT_INPUT,说明当前应用线程的native层的Looper对象中的epoll机制已经开始监听来自于SurfaceFlinger服务端socket端的写入事件
4.mCallbackQueues
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java]
/** * Callback type: Input callback. Runs first. * @hide */ public static final int CALLBACK_INPUT = 0; /** * Callback type: Animation callback. Runs before traversals. * @hide */ public static final int CALLBACK_ANIMATION = 1; /** * Callback type: Traversal callback. Handles layout and draw. Runs * after all other asynchronous messages have been handled. * @hide */ public static final int CALLBACK_TRAVERSAL = 2; /** * Callback type: Commit callback. Handles post-draw operations for the frame. * Runs after traversal completes. The {@link #getFrameTime() frame time} reported * during this callback may be updated to reflect delays that occurred while * traversals were in progress in case heavy layout operations caused some frames * to be skipped. The frame time reported during this callback provides a better * estimate of the start time of the frame in which animations (and other updates * to the view hierarchy state) actually took effect. * @hide */ public static final int CALLBACK_COMMIT = 3; private static final int CALLBACK_LAST = CALLBACK_COMMIT;
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); }
[Choreographer.java–>CallbackQueue]
private final class CallbackQueue { private CallbackRecord mHead; public boolean hasDueCallbacksLocked(long now) { return mHead != null && mHead.dueTime <= now; } public CallbackRecord extractDueCallbacksLocked(long now) { CallbackRecord callbacks = mHead; if (callbacks == null || callbacks.dueTime > now) { return null; } CallbackRecord last = callbacks; CallbackRecord next = last.next; while (next != null) { if (next.dueTime > now) { last.next = null; break; } last = next; next = next.next; } mHead = next; return callbacks; } public void addCallbackLocked(long dueTime, Object action, Object token) { CallbackRecord callback = obtainCallbackLocked(dueTime, action, token); CallbackRecord entry = mHead; if (entry == null) { mHead = callback; return; } if (dueTime < entry.dueTime) { callback.next = entry; mHead = callback; return; } while (entry.next != null) { if (dueTime < entry.next.dueTime) { callback.next = entry.next; break; } entry = entry.next; } entry.next = callback; } public void removeCallbacksLocked(Object action, Object token) { CallbackRecord predecessor = null; for (CallbackRecord callback = mHead; callback != null;) { final CallbackRecord next = callback.next; if ((action == null || callback.action == action) && (token == null || callback.token == token)) { if (predecessor != null) { predecessor.next = next; } else { mHead = next; } recycleCallbackLocked(callback); } else { predecessor = callback; } callback = next; } } }
[Choreographer.java–>CallbackRecord]
private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; public void run(long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } } }
UML图
Choreographer Init UML
更多相关文章
- C语言函数以及函数的使用
- Android 对象序列化之 Parcelable 取代 Serializable ?
- Android 对象序列化之追求完美的 Serial
- Android学习札记12:对Parcelable中describeContents()函数的一种
- android常用函数参数补充
- Android跨进程通信IPC系列
- Android跨进程通信IPC之9——Binder之Framework层C++篇2
- Android跨进程通信IPC之9——Binder之Framework层C++篇1