一、概述
从上篇【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); } }