一、概述
從上篇【Android 7.1 屏幕旋轉流程分析】知道實際的旋轉由WindowManagerService來完成,這里接着上面具體詳細展開。 調了三個函數完成了三件事,即首先調用updateRotationUncheckedLocked()更新rotation,然后調用performSurfacePlacement()做屏幕的繪制,最后調用sendNewConfiguration()發送Configuration變更事件。
本篇對updateRotationUncheckedLocked()詳細展開,后面的系列會繼續詳細介紹剩余的部分。
public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked(" + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")"); long origId = Binder.clearCallingIdentity(); boolean changed; synchronized(mWindowMap) { //(1)更新rotation changed = updateRotationUncheckedLocked(false); if (!changed || forceRelayout) { getDefaultDisplayContentLocked().layoutNeeded = true; //(2)做屏幕的繪制 mWindowPlacerLocked.performSurfacePlacement(); } } if (changed || alwaysSendConfiguration) { //(3)發送Configuration變更事件 sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); }
二、更新rotation
updateRotationUncheckedLocked()主要完成如下幾件事:
A : 首先判斷有下列情形的則返回false放棄更新rotation
(1)有延遲的Rotation尚未完成,即DeferredRotationPauseCount > 0
(2)上次旋轉還沒有完成旋轉動畫還在運行中,即screenRotationAnimation.isAnimating() == true
(3)旋轉動畫已完成但display還未unfrozen(display 還在Frozen狀態)及 mDisplayFrozen == true
(4)display 不可用
B:獲取設備方向
(PhoneWindowManager的rotationForOrientationLw()根據根據傳感器數據計算轉換為設備方向)和是否需要平滑(Seamlessly)旋轉(PhoneWindowManager的shouldRotateSeamlessly()函數)如果Seamlessly則不能有旋轉動畫。
C:根據新的顯示方向(Orientation)來更新DisplayInfo
D通過SurfaceControl.openTransaction()設置Surface參數
public boolean updateRotationUncheckedLocked(boolean inTransaction) { if (mDeferredRotationPauseCount > 0) { // Rotation updates have been paused temporarily. Defer the update until // updates have been resumed. if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused."); return false; } //如果上次的旋轉動畫還在運行中(即上次還未旋轉完成則無需旋轉,返回false) ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { // Rotation updates cannot be performed while the previous rotation change // animation is still in progress. Skip this update. We will try updating // again after the animation is finished and the display is unfrozen. if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress."); return false; } //display 尚未unfrozen if (mDisplayFrozen) { // Even if the screen rotation animation has finished (e.g. isAnimating // returns false), there is still some time where we haven't yet unfrozen // the display. We also need to abort rotation here. if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, still finishing previous rotation"); return false; } if (!mDisplayEnabled) { // No point choosing a rotation if the display is not enabled. if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled."); return false; } final DisplayContent displayContent = getDefaultDisplayContentLocked(); final WindowList windows = displayContent.getWindowList(); final int oldRotation = mRotation; //獲取設備方向(根據傳感器數據計算轉換為設備方向) int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation); //獲取rotateSeamlessly boolean rotateSeamlessly = mPolicy.shouldRotateSeamlessly(oldRotation, rotation); // 處理判斷是否可以Seamlessly if (rotateSeamlessly) { for (int i = windows.size() - 1; i >= 0; i--) { WindowState w = windows.get(i); if (w.mSeamlesslyRotated) { return false; } if (w.isChildWindow() & w.isVisibleNow() && !w.mWinAnimator.mSurfaceController.getTransformToDisplayInverse()) { rotateSeamlessly = false; } } } boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( mLastOrientation, rotation); if (DEBUG_ORIENTATION) { Slog.v(TAG_WM, "Selected orientation " + mLastOrientation + ", got rotation " + rotation + " which has " + (altOrientation ? "incompatible" : "compatible") + " metrics"); } if (mRotation == rotation && mAltOrientation == altOrientation) { // No change. return false; } if (DEBUG_ORIENTATION) { Slog.v(TAG_WM, "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "") + " from " + mRotation + (mAltOrientation ? " (alt)" : "") + ", lastOrientation=" + mLastOrientation); } mRotation = rotation; mAltOrientation = altOrientation; mPolicy.setRotationLw(mRotation); mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; //發送WINDOW_FREEZE_TIMEOUT消息 mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION); mWaitingForConfig = true; displayContent.layoutNeeded = true; final int[] anim = new int[2]; if (displayContent.isDimming()) { anim[0] = anim[1] = 0; } else { mPolicy.selectRotationAnimationLw(anim); } // 如果是Seamlessly則無旋轉動畫,因Seamlessly目前不支持旋轉動畫 if (!rotateSeamlessly) { startFreezingDisplayLocked(inTransaction, anim[0], anim[1]); // startFreezingDisplayLocked can reset the ScreenRotationAnimation. screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); } else { // The screen rotation animation uses a screenshot to freeze the screen // while windows resize underneath. // When we are rotating seamlessly, we allow the elements to transition // to their rotated state independently and without a freeze required. screenRotationAnimation = null; // We have to reset this in case a window was removed before it // finished seamless rotation. mSeamlessRotationCount = 0; } // We need to update our screen size information to match the new rotation. If the rotation // has actually changed then this method will return true and, according to the comment at // the top of the method, the caller is obligated to call computeNewConfigurationLocked(). // By updating the Display info here it will be available to // computeScreenConfigurationLocked later. //根據rotation 去更新DisplayInfo updateDisplayAndOrientationLocked(mCurConfiguration.uiMode); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); if (!inTransaction) { if (SHOW_TRANSACTIONS) { Slog.i(TAG_WM, ">>> OPEN TRANSACTION setRotationUnchecked"); } // 設置surface參數 SurfaceControl.openTransaction(); } try { // NOTE: We disable the rotation in the emulator because // it doesn't support hardware OpenGL emulation yet. if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { if (screenRotationAnimation.setRotationInTransaction( rotation, mFxSession, MAX_ANIMATION_DURATION, getTransitionAnimationScaleLocked(), displayInfo.logicalWidth, displayInfo.logicalHeight)) { scheduleAnimationLocked(); } } if (rotateSeamlessly) { for (int i = windows.size() - 1; i >= 0; i--) { WindowState w = windows.get(i); w.mWinAnimator.seamlesslyRotateWindow(oldRotation, mRotation); } } mDisplayManagerInternal.performTraversalInTransactionFromWindowManager(); } finally { if (!inTransaction) { SurfaceControl.closeTransaction(); if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG_WM, "<<< CLOSE TRANSACTION setRotationUnchecked"); } } } for (int i = windows.size() - 1; i >= 0; i--) { WindowState w = windows.get(i); // Discard surface after orientation change, these can't be reused. if (w.mAppToken != null) { w.mAppToken.destroySavedSurfaces(); } if (w.mHasSurface && !rotateSeamlessly) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w); w.mOrientationChanging = true; mWindowPlacerLocked.mOrientationChangeComplete = false; w.mLastFreezeDuration = 0; } } if (rotateSeamlessly) { mH.removeMessages(H.SEAMLESS_ROTATION_TIMEOUT); mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION); } for (int i=mRotationWatchers.size()-1; i>=0; i--) { try { mRotationWatchers.get(i).watcher.onRotationChanged(rotation); } catch (RemoteException e) { } } // TODO (multidisplay): Magnification is supported only for the default display. // Announce rotation only if we will not animate as we already have the // windows in final state. Otherwise, we make this call at the rotation end. if (screenRotationAnimation == null && mAccessibilityController != null && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(),rotation); } return true; }
1)通過PhoneWindowManager獲取orientation 和rotateSeamlessly
A:PhoneWindowManager的rotationForOrientationLw()
PhoneWindowManager的rotationForOrientationLw()根據傳感器數據和各種場景和設置來更新preferredRotation 和orientation(注釋比較詳細,不贅述)
public int rotationForOrientationLw(int orientation, int lastRotation) { if (mForceDefaultOrientation) { return Surface.ROTATION_0; } synchronized (mLock) { int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1 if (sensorRotation < 0) { sensorRotation = lastRotation; } final int preferredRotation; //根據各種場景和設置來更新preferredRotation 和orientation if (mLidState == LID_OPEN && mLidOpenRotation >= 0) { // Ignore sensor when lid switch is open and rotation is forced. preferredRotation = mLidOpenRotation; } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) { // Ignore sensor when in car dock unless explicitly enabled. // This case can override the behavior of NOSENSOR, and can also // enable 180 degree rotation while docked. preferredRotation = mCarDockEnablesAccelerometer ? sensorRotation : mCarDockRotation; } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) { // Ignore sensor when in desk dock unless explicitly enabled. // This case can override the behavior of NOSENSOR, and can also // enable 180 degree rotation while docked. preferredRotation = mDeskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation; } else if (mHdmiPlugged && mDemoHdmiRotationLock) { // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled. // Note that the dock orientation overrides the HDMI orientation. preferredRotation = mDemoHdmiRotation; } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED && mUndockedHdmiRotation >= 0) { // Ignore sensor when plugged into HDMI and an undocked orientation has // been specified in the configuration (only for legacy devices without // full multi-display support). // Note that the dock orientation overrides the HDMI orientation. preferredRotation = mUndockedHdmiRotation; } else if (mDemoRotationLock) { // Ignore sensor when demo rotation lock is enabled. // Note that the dock orientation and HDMI rotation lock override this. preferredRotation = mDemoRotation; } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { // Application just wants to remain locked in the last rotation. preferredRotation = lastRotation; } else if (!mSupportAutoRotation) { // If we don't support auto-rotation then bail out here and ignore // the sensor and any rotation lock settings. preferredRotation = -1; } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER)) || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { // Otherwise, use sensor only if requested by the application or enabled // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR. if (mAllowAllRotations < 0) { // Can't read this during init() because the context doesn't // have display metrics at that time so we cannot determine // tablet vs. phone then. mAllowAllRotations = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0; } if (sensorRotation != Surface.ROTATION_180 || mAllowAllRotations == 1 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) { preferredRotation = sensorRotation; } else { preferredRotation = lastRotation; } } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { // Apply rotation lock. Does not apply to NOSENSOR. // The idea is that the user rotation expresses a weak preference for the direction // of gravity and as NOSENSOR is never affected by gravity, then neither should // NOSENSOR be affected by rotation lock (although it will be affected by docks). preferredRotation = mUserRotation; } else { // No overriding preference. // We will do exactly what the application asked us to do. preferredRotation = -1; } switch (orientation) { case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: // Return portrait unless overridden. if (isAnyPortrait(preferredRotation)) { return preferredRotation; } return mPortraitRotation; case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: // Return landscape unless overridden. if (isLandscapeOrSeascape(preferredRotation)) { return preferredRotation; } return mLandscapeRotation; case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: // Return reverse portrait unless overridden. if (isAnyPortrait(preferredRotation)) { return preferredRotation; } return mUpsideDownRotation; case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: // Return seascape unless overridden. if (isLandscapeOrSeascape(preferredRotation)) { return preferredRotation; } return mSeascapeRotation; case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: // Return either landscape rotation. if (isLandscapeOrSeascape(preferredRotation)) { return preferredRotation; } if (isLandscapeOrSeascape(lastRotation)) { return lastRotation; } return mLandscapeRotation; case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: // Return either portrait rotation. if (isAnyPortrait(preferredRotation)) { return preferredRotation; } if (isAnyPortrait(lastRotation)) { return lastRotation; } return mPortraitRotation; default: // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR, // just return the preferred orientation we already calculated. if (preferredRotation >= 0) { return preferredRotation; } return Surface.ROTATION_0; } } }
B: shouldRotateSeamlessly()
判斷是否需要平滑(Seamlessly)旋轉,目前僅 top window且為fullscreen 狀態時才會返回true,其他均為false,因為Seamlessly要求凍結Surface的各種狀態並且影響旋轉動畫效果,所以此狀態時不支持旋轉動畫。所以這種狀態支持的場景也僅限於此。了解一下即可。
public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) { if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) { return false; } int delta = newRotation - oldRotation; if (delta < 0) delta += 4; if (delta == Surface.ROTATION_180) { return false; } final WindowState w = mTopFullscreenOpaqueWindowState; if (w != mFocusedWindow) { return false; } if (w != null && !w.isAnimatingLw() && ((w.getAttrs().rotationAnimation == ROTATION_ANIMATION_JUMPCUT) || (w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS))) { return true; } return false; }
2)更新顯示信息(DisplayInfo)
根據新的顯示方向(Orientation)來更新DisplayInfo,然后通過setDisplayInfoOverrideFromWindowManager()來更新顯示設備最后計算frame Rect 給應用來做縮放用。
DisplayInfo updateDisplayAndOrientationLocked(int uiMode) { // TODO(multidisplay): For now, apply Configuration to main screen only. final DisplayContent displayContent = getDefaultDisplayContentLocked(); // Use the effective "visual" dimensions based on current rotation final boolean rotated = (mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270); final int realdw = rotated ? displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; final int realdh = rotated ? displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; int dw = realdw; int dh = realdh; if (mAltOrientation) { if (realdw > realdh) { // Turn landscape into portrait. int maxw = (int)(realdh/1.3f); if (maxw < realdw) { dw = maxw; } } else { // Turn portrait into landscape. int maxh = (int)(realdw/1.3f); if (maxh < realdh) { dh = maxh; } } } // Update application display metrics. final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode); final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); displayInfo.rotation = mRotation; displayInfo.logicalWidth = dw; displayInfo.logicalHeight = dh; displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity; displayInfo.appWidth = appWidth; displayInfo.appHeight = appHeight; displayInfo.getLogicalMetrics(mRealDisplayMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); displayInfo.getAppMetrics(mDisplayMetrics); if (displayContent.mDisplayScalingDisabled) { displayInfo.flags |= Display.FLAG_SCALING_DISABLED; } else { displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED; } mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( displayContent.getDisplayId(), displayInfo); displayContent.mBaseDisplayRect.set(0, 0, dw, dh); if (false) { Slog.i(TAG_WM, "Set app display size: " + appWidth + " x " + appHeight); } mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics, mCompatDisplayMetrics); return displayInfo; }
A: DisplayManagerService.setDisplayInfoOverrideFromWindowManager()
window manager 通過此函數來更新邏輯顯示設備(logical display)的大小變化和一些特性的變更。
首先通過sendDisplayEventLocked()會發送一個MSG_DELIVER_DISPLAY_EVENT消息然后通過,然后調用scheduleTraversalLocked()發送MSG_REQUEST_TRAVERSAL消息請求surface 和display處理變更請求。
public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) { setDisplayInfoOverrideFromWindowManagerInternal(displayId, info); } private void setDisplayInfoOverrideFromWindowManagerInternal(int displayId, DisplayInfo info) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplays.get(displayId); if (display != null) { if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) { //發送MSG_DELIVER_DISPLAY_EVENT消息 sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); //發送MSG_REQUEST_TRAVERSAL消息 scheduleTraversalLocked(false); } } } } private void sendDisplayEventLocked(int displayId, int event) { Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); mHandler.sendMessage(msg); }
發送MSG_REQUEST_TRAVERSAL消息請求,稍后由surface 和display處理變更請求。這樣處理是為了異步執行。
private void scheduleTraversalLocked(boolean inTraversal) { if (!mPendingTraversal && mWindowManagerInternal != null) { mPendingTraversal = true; if (!inTraversal) { mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL); } } }
B: computeCompatibleScaling() 計算frame Rect來供應用做縮放
public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) { final int width = dm.noncompatWidthPixels; final int height = dm.noncompatHeightPixels; int shortSize, longSize; if (width < height) { shortSize = width; longSize = height; } else { shortSize = height; longSize = width; } int newShortSize = (int)(DEFAULT_NORMAL_SHORT_DIMENSION * dm.density + 0.5f); float aspect = ((float)longSize) / shortSize; if (aspect > MAXIMUM_ASPECT_RATIO) { aspect = MAXIMUM_ASPECT_RATIO; } int newLongSize = (int)(newShortSize * aspect + 0.5f); int newWidth, newHeight; if (width < height) { newWidth = newShortSize; newHeight = newLongSize; } else { newWidth = newLongSize; newHeight = newShortSize; } float sw = width/(float)newWidth; float sh = height/(float)newHeight; float scale = sw < sh ? sw : sh; if (scale < 1) { scale = 1; } if (outDm != null) { outDm.widthPixels = newWidth; outDm.heightPixels = newHeight; } return scale; }
3)DisplayManagerService的performTraversalInTransactionFromWindowManager()
public void performTraversalInTransactionFromWindowManager() { performTraversalInTransactionFromWindowManagerInternal(); }
private void performTraversalInTransactionFromWindowManagerInternal() { synchronized (mSyncRoot) { if (!mPendingTraversal) { return; } mPendingTraversal = false; performTraversalInTransactionLocked(); } // List is self-synchronized copy-on-write. for (DisplayTransactionListener listener : mDisplayTransactionListeners) { listener.onDisplayTransaction(); } }
private void performTraversalInTransactionLocked() { // Clear all viewports before configuring displays so that we can keep // track of which ones we have configured. clearViewportsLocked(); // Configure each display device. final int count = mDisplayDevices.size(); for (int i = 0; i < count; i++) { DisplayDevice device = mDisplayDevices.get(i); configureDisplayInTransactionLocked(device); device.performTraversalInTransactionLocked(); } // Tell the input system about these new viewports. if (mInputManagerInternal != null) { mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT); } }
private void configureDisplayInTransactionLocked(DisplayDevice device) { final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0; // Find the logical display that the display device is showing. // Certain displays only ever show their own content. LogicalDisplay display = findLogicalDisplayForDeviceLocked(device); if (!ownContent) { if (display != null && !display.hasContentLocked()) { // If the display does not have any content of its own, then // automatically mirror the default logical display contents. display = null; } if (display == null) { display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY); } } // Apply the logical display configuration to the display device. if (display == null) { // TODO: no logical display for the device, blank it Slog.w(TAG, "Missing logical display to use for physical display device: " + device.getDisplayDeviceInfoLocked()); return; } display.configureDisplayInTransactionLocked(device, info.state == Display.STATE_OFF); // Update the viewports if needed. if (!mDefaultViewport.valid && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { setViewportLocked(mDefaultViewport, display, device); } if (!mExternalTouchViewport.valid && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) { setViewportLocked(mExternalTouchViewport, display, device); } }