基于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