基于Android5.1的双屏异显分析
平台:android5.1
场景:客户的设备需要使用到双屏异显。分析双屏异显时,framework所做的准备。
时间:2016.9.28
Android从4.2开始支持双屏异显,其Java使用示例代码如下:
1.如何获取设备上的屏幕?
DisplayManager mDisplayManager;//屏幕管理类Display[] displays;//屏幕数组mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);displays =mDisplayManager.getDisplays();2.主屏和副屏的区分?
主屏:displays[0]
副屏:displays[1]
3.如何在副屏上展示内容?
通过Presentation来实现,Presentation继承了Dialog。
假设我们写了一个DifferentDislay的类,这个类是要继承Presentation类:
4.开启副屏
DifferentDislay mPresentation =new DifferentDislay (context,displays[1]);//displays[1]是副屏mPresentation.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);mPresentation.show();以上代码的核心在于Presentation类,其继承与Dialog。
从new DifferentDislay (context,displays[1]) 函数分析其初始化:
上面代码的设计原则:
每一个display拥有自己的管理对象以及context对象,这样双屏的操作互相独立—Display是核心对象。对于上层而言,其即意味着一个屏幕。
mPresentation.show()拉开了双屏异显的序幕。结合上面的分析,WindowManagerImpl.java中的addView()方法将传入上面初始化的display[1],继而:
接下来开始进入到主题,framework为支持双屏异显做了哪些工作?
两个重点:
1.检测到双屏(第二个屏)后,系统做了哪些准备?(主屏幕和HDMI是两个默认开机就进行检测的屏幕设备,其他监听后使用hotplug处理)
2.如何进行/区分双屏异显?
上面提到Android4.2开始支持双屏异显,除了引入Presentation 类,其还定制了HWComposer,其构造函数中:
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))mCBContext->procs.hotplug = &hook_hotplug;elsemCBContext->procs.hotplug = NULL;即HWC的版本大于等于1.1即可支持双屏异显。我们从hook_hotplug开始查看检测到插入新屏时的系统
HWComposer.cpp void HWComposer::hotplug(int disp, int connected)SurfaceFlinger.cpp void SurfaceFlinger::onHotplugReceived(int type, bool connected) //NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES//默认为2。当type小于2时,才会继续执行。即默认最多支持两个屏。此处的type标示着屏幕类型,如下所示:enum DisplayType {DISPLAY_ID_INVALID = -1,DISPLAY_PRIMARY = HWC_DISPLAY_PRIMARY, //0,默认屏幕DISPLAY_EXTERNAL = HWC_DISPLAY_EXTERNAL, //1,第二屏DISPLAY_VIRTUAL = HWC_DISPLAY_VIRTUAL, //虚拟屏幕NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES,};void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type){ALOGW_IF(mBuiltinDisplays[type],"Overwriting display token for display type %d", type);mBuiltinDisplays[type] = new BBinder();DisplayDeviceState info(type);// All non-virtual displays are currently considered secure.info.isSecure = true;mCurrentState.displays.add(mBuiltinDisplays[type], info); //保存到mCurrentState.displays,此处的info为DisplayDeviceState对象,mBuiltinDisplays[type]为IBinder对象。 } uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) void SurfaceFlinger::signalTransaction() MessageQueue.cpp void MessageQueue::invalidate() { #if INVALIDATE_ON_VSYNC //此宏为1mEvents->requestNextVsync(); #elsemHandler->dispatchInvalidate(); #endif }EventThread.cpp void EventThread::requestNextVsync( const sp<EventThread::Connection>& connection) //mCondition.broadcast()将挂起的线程唤起 Vector< sp<EventThread::Connection> > EventThread::waitForEvent( DisplayEventReceiver::Event* event) //此函数中的mDisplayEventConnections值得深究。其通过registerDisplayEventConnection()<<<EventThread::Connection::onFirstRef()<<<EventThread::createEventConnection()<<<(SurfaceFlinger::createDisplayEventConnection() <<<DisplayEventReceiver::DisplayEventReceiver())---这个是mEventThread对象的 | (MessageQueue::setEventThread()<<<SurfaceFlinger::init())---这个是mSFEventThread对象的在此构造函数中创建了DisplayEventConnection SurfaceFlinger.cpp void SurfaceFlinger::onMessageReceived(int32_t what) void SurfaceFlinger::handleTransaction(uint32_t transactionFlags){ ...// Here we're guaranteed that some transaction flags are set// so we can call handleTransactionLocked() unconditionally.// We call getTransactionFlags(), which will also clear the flags,// with mStateLock held to guarantee that mCurrentState won't change// until the transaction is committed.transactionFlags = getTransactionFlags(eTransactionMask); //此函数作用?handleTransactionLocked(transactionFlags); //上面函数看上去是将transactionFlags清零 ...invalidateHwcGeometry(); } void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags){ ...if (transactionFlags & eDisplayTransactionNeeded) {// here we take advantage of Vector's copy-on-write semantics to// improve performance by skipping the transaction entirely when// know that the lists are identicalconst KeyedVector< wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);const KeyedVector< wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);...const size_t cc = curr.size();...// find displays that were added// (ie: in current state but not in drawing state)for (size_t i=0 ; i<cc ; i++) {...BufferQueue::createBufferQueue(&bqProducer, &bqConsumer, new GraphicBufferAlloc()); //为对应的display创建新的BufferQueue...else {mEventThread->onHotplugReceived(state.type, true); //通知有新屏设备接入,mEventThread对象中的Connection需要先创建,即createEventConnection()函数需要先被执行,以便后面利用Connection内部对象mChannel来通信!---注意区分与mSFEventThread差别}}} ... } EventThread.cpp void EventThread::onHotplugReceived(int type, bool connected){ //此时type为1,即DISPLAY_EXTERNAL。connected为trueMutex::Autolock _l(mLock);if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {DisplayEventReceiver::Event event;event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; event.header.id = type;event.header.timestamp = systemTime();event.hotplug.connected = connected;mPendingEvents.add(event); //插入event,将在waitForEvent()读取mCondition.broadcast();} } Vector< sp<EventThread::Connection> > EventThread::waitForEvent(DisplayEventReceiver::Event* event){ ...if (!timestamp) {// no vsync event, see if there are some other eventeventPending = !mPendingEvents.isEmpty();if (eventPending) {// we have some other event to dispatch*event = mPendingEvents[0]; //取出上面插入的eventmPendingEvents.removeAt(0);}}// find out connections waiting for eventssize_t count = mDisplayEventConnections.size(); //此处对应新display的connectcion已经建立,如何建立???...if (eventPending && !timestamp && !added) {// we don't have a vsync event to process// (timestamp==0), but we have some pending// messages.signalConnections.add(connection);} ...if (!timestamp && !eventPending) { //此时eventPending为true,不会进入此if进行wait,同时signalConnections.add(),所以会退出while循环...}while (signalConnections.isEmpty()); ... } bool EventThread::threadLoop() status_t EventThread::Connection::postEvent( const DisplayEventReceiver::Event& event) //sendEvents()中的mChannel为new new BitTube(),此对象用于pipe通信。关注此时signalConnections[i]来源,其决定pipe通信的两端。 BitTube.cpp ssize_t BitTube::sendObjects(const sp<BitTube>& tube, void const* events, size_t count, size_t objSize) //objSize为模板size ssize_t BitTube::write(void const* vaddr, size_t size){ //vaddr为event的地址,size为objSize*count ...len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); //socket函数进行pipe通信。BitTube的构造函数中,调用init函数,其调用socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)初始化了mReceiveFd和mSendFd ... }至此,将新屏插入的event消息通过socket发出。
那何处何时接收并处理此消息呢?从mReceiveFd逆向推出:
SF在创建事件线程时,同时创建了监听回调:
mSFEventThread = new EventThread(sfVsyncSrc);mEventQueue.setEventThread(mSFEventThread);void MessageQueue::setEventThread(const sp<EventThread>& eventThread) {mEventThread = eventThread;mEvents = eventThread->createEventConnection(); //其中new Connection(),即初始化了mChannel(new BitTube()),此通道用于监听注册mEventTube = mEvents->getDataChannel(); //mChannelmLooper->addFd(mEventTube->getFd(), 0, Looper::EVENT_INPUT,MessageQueue::cb_eventReceiver, this); //Android自4.1后在SF嵌入了消息机制。mLooper->addFd()为注册监听,MessageQueue::cb_eventReceiver为回调函数,其调用了eventReceiver() }分析到此处,插入消息接收处理仿佛已经找到,但 eventReceiver()只处理DISPLAY_EVENT_VSYNC,而我们event包含的是DISPLAY_EVENT_HOTPLUG消息,莫非不用处理?
addFd()先注册了监听,当向pipe写入event插入消息时,从消息队列中唤起并执行处理。
先关注mEventTube->getFd()监听的对象:
setEventThread()只适用于mSFEventThread变量,而我们跟踪的是另外EventThread对象mEventThread。
所以上面并非正确的调用处。
回头看DisplayEventReceiver::getEvents(),同时结合其构造函数创建mEventConnection,等同创建mChannel,必然有地方提前初始化此对象。
搜索”DisplayEventReceiver “:
此类继承LooperCallback,说明其可以实现消息机制的回调。再查看注册监听以及消息获取:
int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL); while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0)该有的都有了,断定此处即为处理插入事件的地方。
在分析EventThread::waitForEvent()时,此函数中的mDisplayEventConnections通过
registerDisplayEventConnection()<<<EventThread::Connection::onFirstRef()<<<EventThread::createEventConnection()<<<(SurfaceFlinger::createDisplayEventConnection() <<<DisplayEventReceiver::DisplayEventReceiver())"所以addFd(mReceiver.getFd(),xxx)和conn->postEvent(event)中的mChannel为同一个。
关于native的消息机制,与java的原理类似,熟悉的Handler,Looper,MessageQueue。
总结一下mChannel这个变量:
DisplayEventReceiver::mDataChannel<>EventThread::Connection::mChannel
1).其为BitTube对象
2).BitTube对象中的mReceiveFd和mSendFd为socket的两端
3).addFd注册监听,传入BitTube::mReceiveFd被epoll挂起监听。同时获取消息时,::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT),从mReceiveFd读取
4).::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL),mSendFd用于发送消息
现在搞清楚了监听注册,接受消息,我们继续看两个问题:
(1).对于插入新屏的处理
(2).android_view_DisplayEventReceiver.cpp的初始化
android_view_DisplayEventReceiver.cpp
int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) //虽然传入了receiveFd,但此函数内部并没有使用。为什么可以自己思考。 bool NativeDisplayEventReceiver::processPendingEvents( nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount){ ...while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {...case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected);break;} ... }DisplayEventReceiver.java //DisplayEventReceiver.java为抽象类,LocalDisplayAdapter.java的内部类HotplugDisplayEventReceiver继承了DisplayEventReceiver
// Called from native code.
@SuppressWarnings(“unused”)
private void dispatchHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
onHotplug(timestampNanos, builtInDisplayId, connected);
}
LocalDisplayAdapter.java
private void tryConnectDisplayLocked(int builtInDisplayId){ //为ev.header.id,此处值为1IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);if (displayToken != null) {SurfaceControl.PhysicalDisplayInfo[] configs =SurfaceControl.getDisplayConfigs(displayToken); //查询mBuiltinDisplays.此处configs何时被设定?if (configs == null) {// There are no valid configs for this device, so we can't use itSlog.w(TAG, "No valid configs found for display device " +builtInDisplayId);return;} ...LocalDisplayDevice device = mDevices.get(builtInDisplayId); //此处创建的是LocalDisplayDevice对象if (device == null) {// Display was added.device = new LocalDisplayDevice(displayToken, builtInDisplayId,configs, activeConfig);mDevices.put(builtInDisplayId, device);sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); //此处场景为第一次插入第二个屏} ... }DisplayManagerService.java
private void handleDisplayDeviceAddedLocked(DisplayDevice device){ ...addLogicalDisplayLocked(device);Runnable work = updateDisplayStateLocked(device); //此device为FLAG_NEVER_BLANK并且mGlobalDisplayState状态不一致时,会调用SurfaceControl.setDisplayPowerMode(token, mode)if (work != null) {work.run();}scheduleTraversalLocked(false); //将调用WindowManagerService::performLayoutAndPlaceSurfacesLockedLoop()>>>performLayoutAndPlaceSurfacesLockedInner()>>>mDisplayManagerInternal.performTraversalInTransactionFromWindowManager()>>>DMS::performTraversalInTransactionLocked()>>>configureDisplayInTransactionLocked()>>>LogicalDisplay::configureDisplayInTransactionLocked()>>>DisplayDevice::setLayerStackInTransactionLocked()>>>SurfaceControl.setDisplayLayerStack(mDisplayToken, layerStack) 如此重要的调用,隐藏的好深!其最终对应Composer::setDisplayLayerStack(),将新创建DisplayState对象并保存到mDisplayStates中,同时DisplayState对象设定状态eLayerStackChanged和对应layerstack,提供后面显示使用 ... } private void addLogicalDisplayLocked(DisplayDevice device){ //Adds a new logical display based on the given display device. Sends notifications if needed. ...final int displayId = assignDisplayIdLocked(isDefault);final int layerStack = assignLayerStackLocked(displayId); //最开始提到的将displayid与layerstack一致便是此时完成LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);display.updateLocked(mDisplayDevices); ...sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); }DisplayManagerGlobal.java
public void registerDisplayListener(DisplayListener listener, Handler handler) //收到EVENT_DISPLAY_ADDED消息,将调用参数listener的回调函数onDisplayAdded()。Presentation.java和ViewRootImpl.java对ADD是空操作,它们更关心CHANGE,REMOVE的变化到此,插入第二个屏基本结束。从SF开始,到WMS结束。
其主要作用:在SF对新屏进行了参数初始化,在DMS创建并保存了新的Display,DisplayDevice对象,使得displayId与layerstack对应一致。
注意:此处分析的是插入新屏,系统开机过程中,DMS默认会对主屏和HDMI进行扫描并创建设备对象:
LocalDisplayAdapter.java
系统初始化了第二个的设备对象,那么是如何控制显示的呢?
我们从ViewRootImpl说起:
ViewRootImpl.java
WindowManagerService.java
public int relayoutWindow(Session session, IWindow client, int seq,WindowManager.LayoutParams attrs, int requestedWidth,int requestedHeight, int viewVisibility, int flags,Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,Surface outSurface){ //此处outSurface为null,其在WMS被创建赋值,并返回给ViewRootImpl ...SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();if (surfaceControl != null) {outSurface.copyFrom(surfaceControl);if (SHOW_TRANSACTIONS) Slog.i(TAG," OUT SURFACE " + outSurface + ": copied");} ... }WindowStateAnimator.java
SurfaceControl createSurfaceLocked(){ ... // Start a new transaction and apply position & offset.SurfaceControl.openTransaction(); ...if (displayContent != null) {mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack()); //此处只关注第二个屏,即layerstack的处理} ...SurfaceControl.closeTransaction(); //将此处的设置一并提交 ... }因为SF与framework对应的变量有着重重封装,此处直接跳过:
SurfaceComposerClient.cpp
status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client,const sp<IBinder>& id, uint32_t layerStack) { //id为SurfaceControl.cpp中的mHandleMutex::Autolock _l(mLock);layer_state_t* s = getLayerStateLocked(client, id); //将第二个屏对应的ComposerState对象添加到mComposerStates中。ComposerState对象s.client为SF的Bp端,s.state.surface为IBinder对象mHandleif (!s)return BAD_INDEX;s->what |= layer_state_t::eLayerStackChanged;s->layerStack = layerStack; //设置了flag和laystack,在closeTransaction()提交时处理return NO_ERROR; }//SurfaceControl.closeTransaction()的对应处理 void Composer::closeGlobalTransactionImpl(bool synchronous) { ...transaction = mComposerStates;mComposerStates.clear();displayTransaction = mDisplayStates; //上面分析已经提供mComposerStates和mDisplayStates的创建添加mDisplayStates.clear(); ...sm->setTransactionState(transaction, displayTransaction, flags); //因为前面也有getLayerStateLockedmSurfaceControl.setSize(),其会将mForceSynchronous赋值为true,所以flag有eSynchronous。 }SurfaceFlinger.cpp
void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,const Vector<DisplayState>& displays,uint32_t flags) { ...count = state.size();for (size_t i=0 ; i<count ; i++) {const ComposerState& s(state[i]);if (s.client != NULL) {sp<IBinder> binder = s.client->asBinder();if (binder != NULL) {String16 desc(binder->getInterfaceDescriptor());if (desc == ISurfaceComposerClient::descriptor) {sp<Client> client( static_cast<Client *>(s.client.get()) );transactionFlags |= setClientStateLocked(client, s.state); //将调用Layer::setLayerStack(),将layerstack保存到mCurrentState.layerStack,为mTransactionFlags添加eTransactionNeeded,后面的onDraw()使用}}}} ...if (transactionFlags) { //此时eTransactionNeeded|eTraversalNeeded// this triggers the transactionsetTransactionFlags(transactionFlags); //将调用到handleTransactionLocked()// if this is a synchronous transaction, wait for it to take effect// before returning.if (flags & eSynchronous) { mTransactionPending = true;}if (flags & eAnimation) {mAnimTransactionPending = true;}while (mTransactionPending) { //此时mTransactionPending为truestatus_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));if (CC_UNLIKELY(err != NO_ERROR)) {// just in case something goes wrong in SF, return to the// called after a few seconds.ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");mTransactionPending = false;break;}}} }void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags){ ...if (transactionFlags & eTraversalNeeded) {for (size_t i=0 ; i<count ; i++) {const sp<Layer>& layer(currentLayers[i]);uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); //if (!trFlags) continue;const uint32_t flags = layer->doTransaction(0); //每个layer进行遍历处理,将调用Layer::commitTransaction()>>>mDrawingState = mCurrentState。这样第二个屏的已经转变为mDrawingState状态。if (flags & Layer::eVisibleRegion)mVisibleRegionsDirty = true;}} ...commitTransaction(); //调用mTransactionCV.broadcast(),使得 SurfaceControl.closeTransaction()线程返回.同时mDrawingState = mCurrentState;updateCursorAsync(); }至此,已经将SF和Layer中的mDrawingState与第二个屏绑定。performDraw()可以愉快在插入的新屏上绘制了。
http://www.voidcn.com/article/p-wxfimpep-bpd.html
总结
以上是生活随笔为你收集整理的基于Android5.1的双屏异显分析的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Android P 图形显示系统
- 下一篇: Android7.1 Presentat