上篇博客中我们一直提到updateRotationUnchecked函数,这篇博客我们就来分析下这个函数,这个函数可以说是旋转屏幕的一个核心函数,我们主要看下updateRotationUncheckedLocked和sendNewConfiguration函数,当updateRotationUncheckedLocked返回true代表设备已经旋转,这个时候要调用sendNewConfiguration函数通知AMS来使相应的Activity重新Launch等。

public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
long origId = Binder.clearCallingIdentity();
boolean changed;
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (!changed || forceRelayout) {
getDefaultDisplayContentLocked().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
if (changed || alwaysSendConfiguration) {
sendNewConfiguration();//后续分析
}
Binder.restoreCallingIdentity(origId);
}

sendNewConfiguration我们放到下篇博客分析,这里我们先分析updateRotationUncheckedLocked函数。

PhoneWindowManager的rotationForOrientationLw获取rotation

下面是部分代码,我们先调用PhoneWindowManager的rotationForOrientationLw函数来获取rotation,然后与之前的mRotation对比是否有变化,没有变化直接返回false。有变化将mRotation重新赋值。

......
int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);
boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
mForcedAppOrientation, rotation);
if (mRotation == rotation && mAltOrientation == altOrientation) {
// No change.
return false;
}
mRotation = rotation;
mAltOrientation = altOrientation;
......

PhoneWindowManager的rotationForOrientationLw函数我们就不分析了,就是获取sensor的rotation,然后计算返回我们需要的rotation。我们继续看updateRotationUncheckedLocked函数的剩下代码。

updateDisplayAndOrientationLocked DisplayInfo数据放入display中

在updateRotationUncheckedLocked中还调用了updateDisplayAndOrientationLocked函数,我们也来看下,这个函数把各种数据更新下放到DisplayInfo中,最后调用了DisplayManagerService的setDisplayInfoOverrideFromWindowManager函数

DisplayInfo updateDisplayAndOrientationLocked() {
......
// Update application display metrics.
final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
synchronized(displayContent.mDisplaySizeLock) {
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);setDisplayInfoOverrideFromWindowManager就是调用了setDisplayInfoOverrideFromWindowManagerInternal
@Override
public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
setDisplayInfoOverrideFromWindowManagerInternal(displayId, info);
}
setDisplayInfoOverrideFromWindowManagerInternal函数找到相应的display然后调用其setDisplayInfoOverrideFromWindowManagerLocked函数。
private void setDisplayInfoOverrideFromWindowManagerInternal(
int displayId, DisplayInfo info) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
scheduleTraversalLocked(false);
}
}
}
}

最后到LogicalDisplay的setDisplayInfoOverrideFromWindowManagerLocked函数中,把DisplayInfo数据放到了mOverrideDisplayInfo中

public boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
if (info != null) {
if (mOverrideDisplayInfo == null) {
mOverrideDisplayInfo = new DisplayInfo(info);
mInfo = null;
return true;
}
if (!mOverrideDisplayInfo.equals(info)) {
mOverrideDisplayInfo.copyFrom(info);
mInfo = null;
return true;
}
} else if (mOverrideDisplayInfo != null) {
mOverrideDisplayInfo = null;
mInfo = null;
return true;
}
return false;
}

设置设备旋转

然后在updateRotationUncheckedLocked又调用了如下代码:

mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();

这样我们又到了DisplayManagerService的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();
}
}
performTraversalInTransactionLocked函数,遍历所有的Device,先调用了configureDisplayInTransactionLocked函数,然后调用了各个Device的performTraversalInTransactionLocked,而普通的Device的为空,因此这里我们不关注这个函数。
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);
}
}

下面我们主要看configureDisplayInTransactionLocked函数,这个函数就是找到那个LogicalDisplay 然后调用其configureDisplayInTransactionLocked函数

private void configureDisplayInTransactionLocked(DisplayDevice device) {
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
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);
LogicalDisplay 的configureDisplayInTransactionLocked函数,就是设置长宽,旋转角度等。
public void configureDisplayInTransactionLocked(DisplayDevice device,
boolean isBlanked) {
// Set the layer stack.
device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack);
// Set the color transform and mode.
if (device == mPrimaryDisplayDevice) {
device.requestColorTransformAndModeInTransactionLocked(
mRequestedColorTransformId, mRequestedModeId);
} else {
device.requestColorTransformAndModeInTransactionLocked(0, 0); // Revert to default.
}
// Only grab the display info now as it may have been changed based on the requests above.
final DisplayInfo displayInfo = getDisplayInfoLocked();
final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();//获取设备的info
// Set the viewport.
// This is the area of the logical display that we intend to show on the
// display device. For now, it is always the full size of the logical display.
mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
// Set the orientation.
// The orientation specifies how the physical coordinate system of the display
// is rotated when the contents of the logical display are rendered.
int orientation = Surface.ROTATION_0;
if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) {
orientation = displayInfo.rotation;
}
// Apply the physical rotation of the display device itself.
orientation = (orientation + displayDeviceInfo.rotation) % 4;//方向是根据传入的方向加上原有设备的方向除以4
// Set the frame.
// The frame specifies the rotated physical coordinates into which the viewport
// is mapped. We need to take care to preserve the aspect ratio of the viewport.
// Currently we maximize the area to fill the display, but we could try to be
// more clever and match resolutions.
boolean rotated = (orientation == Surface.ROTATION_90
|| orientation == Surface.ROTATION_270);
int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;//旋转了长宽是否转换
int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
// Determine whether the width or height is more constrained to be scaled.
// physWidth / displayInfo.logicalWidth => letter box
// or physHeight / displayInfo.logicalHeight => pillar box
//
// We avoid a division (and possible floating point imprecision) here by
// multiplying the fractions by the product of their denominators before
// comparing them.
int displayRectWidth, displayRectHeight;
if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0) {
displayRectWidth = displayInfo.logicalWidth;
displayRectHeight = displayInfo.logicalHeight;
} else if (physWidth * displayInfo.logicalHeight
< physHeight * displayInfo.logicalWidth) {
// Letter box.
displayRectWidth = physWidth;
displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
} else {
// Pillar box.
displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
displayRectHeight = physHeight;
}
int displayRectTop = (physHeight - displayRectHeight) / 2;
int displayRectLeft = (physWidth - displayRectWidth) / 2;
mTempDisplayRect.set(displayRectLeft, displayRectTop,
displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
mTempDisplayRect.left += mDisplayOffsetX;
mTempDisplayRect.right += mDisplayOffsetX;
mTempDisplayRect.top += mDisplayOffsetY;
mTempDisplayRect.bottom += mDisplayOffsetY;
device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
}

最后调用了DisplayDevice的setProjectionInTransactionLocked函数,然后调用了SurfaceControl的setDisplayProjection函数。

public final void setProjectionInTransactionLocked(int orientation,
Rect layerStackRect, Rect displayRect) {
if (mCurrentOrientation != orientation
|| mCurrentLayerStackRect == null
|| !mCurrentLayerStackRect.equals(layerStackRect)
|| mCurrentDisplayRect == null
|| !mCurrentDisplayRect.equals(displayRect)) {
mCurrentOrientation = orientation;
if (mCurrentLayerStackRect == null) {
mCurrentLayerStackRect = new Rect();
}
mCurrentLayerStackRect.set(layerStackRect);
if (mCurrentDisplayRect == null) {
mCurrentDisplayRect = new Rect();
}
mCurrentDisplayRect.set(displayRect);
SurfaceControl.setDisplayProjection(mDisplayToken,
orientation, layerStackRect, displayRect);
}

}SurfaceControl.setDisplayProjection函数如下,这个函数最后又调用了JNI函数,最后是根据mDisplayToken来设置到相应的设备中去。

public static void setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
if (layerStackRect == null) {
throw new IllegalArgumentException("layerStackRect must not be null");
}
if (displayRect == null) {
throw new IllegalArgumentException("displayRect must not be null");
}
nativeSetDisplayProjection(displayToken, orientation,
layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
}

这样updateRotationUncheckedLocked函数分析完了,当设备旋转了之后我们就要调用sendNewConfiguration,这个函数下篇博客分析。这里我们发现DisplayManagerService的相关流程还不是很熟悉(包括和SurfaceControl的联系等),后续也要分析下。