概述
主屏1920x1200,副屏hdmi 1920x1080
双屏同显的时候,副屏1920x1080 显示1920x1200的画面会有缩放,横屏时两侧有较小黑边
双屏异显 副屏hdmi 显示1080的图片 能全屏显示
从DisplayManagerService 入手,在onStart 会去注册display adapter
//DisplayManagerService
public void onStart() {
// We need to pre-load the persistent data store so it's ready before the default display
// adapter is up so that we have it's configuration. We could load it lazily, but since
// we're going to have to read it in eventually we may as well do it here rather than after
// we've waited for the display to register itself with us.
synchronized (mSyncRoot) {
mPersistentDataStore.loadIfNeeded();
loadStableDisplayValuesLocked();
}
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
true /*allowIsolated*/);
publishLocalService(DisplayManagerInternal.class, new LocalService());
}
private void registerDefaultDisplayAdapters() {
// Register default display adapters.
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(new LocalDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
// here so that it is ready by the time the system sends the home Intent for
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
mHandler, mDisplayAdapterListener);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
}
}
//DisplayManagerHandler
case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS:
registerDefaultDisplayAdapters();
break;
有新设备添加的话 DisplayAdapterListener 会有消息
private final class DisplayAdapterListener implements DisplayAdapter.Listener {
@Override
public void onDisplayDeviceEvent(DisplayDevice device, int event) {
switch (event) {
case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
handleDisplayDeviceAdded(device);
break;
case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
handleDisplayDeviceChanged(device);
break;
case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
handleDisplayDeviceRemoved(device);
break;
}
}
@Override
public void onTraversalRequested() {
synchronized (mSyncRoot) {
scheduleTraversalLocked(false);
}
}
}
private void handleDisplayDeviceAdded(DisplayDevice device) {
synchronized (mSyncRoot) {
handleDisplayDeviceAddedLocked(device);
}
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if (mDisplayDevices.contains(device)) {
Slog.w(TAG, "Attempted to add already added display device: " + info);
return;
}
Slog.i(TAG, "Display device added: " + info);
device.mDebugLastLoggedDeviceInfo = info;
mDisplayDevices.add(device);
LogicalDisplay display = addLogicalDisplayLocked(device);
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
work.run();
}
scheduleTraversalLocked(false);
}
查看DisplayDeviceInfo 的话可以看到有两个屏的信息,查看DisplayDevice类型的话是LocalDisplayAdapter.LocalDisplayDevice.
LocalDisplayAdapter是本地物理显示屏设备,还有WifiDisplayAdapter 、OverlayDisplayAdapter、VirtualDisplayAdapter
这边两个屏都是LocalDisplayAdapter的
DisplayManagerService: Display device added: DisplayDeviceInfo{"Built-in Screen": uniqueId="local:0", 1200 x 1920, modeId 1, defaultModeId 1, supportedModes [{id=1, width=1200, height=1920, fps=60.000004}], colorMode 0, supportedColorModes [0], HdrCapabilities android.view.Display$HdrCapabilities@40f16308, density 280, 320.842 x 320.842 dpi, appVsyncOff 1000000, presDeadline 16666666, touch INTERNAL, rotation 0, type BUILT_IN, address {port=0}, state UNKNOWN, FLAG_DEFAULT_DISPLAY, FLAG_ROTATES_WITH_CONTENT, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
DisplayManagerService: Display device added: DisplayDeviceInfo{"HDMI Screen": uniqueId="local:1", 1920 x 1080, modeId 2, defaultModeId 2, supportedModes [{id=2, width=1920, height=1080, fps=60.000004}], colorMode 0, supportedColorModes [0], HdrCapabilities android.view.Display$HdrCapabilities@40f16308, density 320, 320.0 x 320.0 dpi, appVsyncOff 1000000, presDeadline 16666666, touch EXTERNAL, rotation 0, type HDMI, address {port=1}, state UNKNOWN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION}
看handleDisplayDeviceAddedLocked
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
//LocalDisplayAdapter
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];
mInfo = new DisplayDeviceInfo();
mInfo.width = phys.width;
mInfo.height = phys.height;
mInfo.modeId = mActiveModeId;
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
mInfo.supportedColorModes =
new int[mSupportedColorModes.size()];
for (int i = 0; i < mSupportedColorModes.size(); i++) {
mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
mInfo.state = mState;
mInfo.uniqueId = getUniqueId();
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
// Assume that all built-in displays that have secure output (eg. HDCP) also
// support compositing from gralloc protected buffers.
if (phys.secure) {
mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
final Resources res = getOverlayContext().getResources();
final boolean isBuiltIn = ((mInfo.address) != null) ?
(((DisplayAddress.Physical) mInfo.address).getPort() < 0) : false;
if (mIsInternal) {
mInfo.name = res.getString(
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
|| (Build.IS_EMULATOR
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
if (res.getBoolean(
com.android.internal.R.bool.config_maskMainBuiltInDisplayCutout)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height);
mInfo.type = Display.TYPE_BUILT_IN;
mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
mInfo.xDpi = phys.xDpi;
mInfo.yDpi = phys.yDpi;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
} else if (isBuiltIn) {
mInfo.type = Display.TYPE_BUILT_IN;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
if (SystemProperties.getBoolean(
"vendor.display.builtin_presentation", false)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
} else {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
}
if (!SystemProperties.getBoolean(
"vendor.display.builtin_mirroring", false)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
} else {
mInfo.displayCutout = null;
mInfo.type = Display.TYPE_HDMI;
mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_hdmi_display_name);
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
// For demonstration purposes, allow rotation of the external display.
// In the future we might allow the user to configure this directly.
if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
mInfo.rotation = Surface.ROTATION_270;
}
// For demonstration purposes, allow rotation of the external display
// to follow the built-in display.
if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
if (!res.getBoolean(
com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
if (isDisplayPrivate(physicalAddress)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
}
}
}
return mInfo;
}
LogicalDisplay display = addLogicalDisplayLocked(device);
private LogicalDisplay addLogicalDisplayLocked(DisplayDevice device) {
DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
boolean isDefault = (deviceInfo.flags
& DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
isDefault = false;
}
if (!isDefault && mSingleDisplayDemoMode) {
Slog.i(TAG, "Not creating a logical display for a secondary display "
+ " because single display demo mode is enabled: " + deviceInfo);
return null;
}
final int displayId = assignDisplayIdLocked(isDefault, deviceInfo.address);
final int layerStack = assignLayerStackLocked(displayId);
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
display.updateLocked(mDisplayDevices);
if (!display.isValidLocked()) {
// This should never happen currently.
Slog.w(TAG, "Ignoring display device because the logical display "
+ "created from it was not considered valid: " + deviceInfo);
return null;
}
configureColorModeLocked(display, device);
if (isDefault) {
recordStableDisplayStatsIfNeededLocked(display);
recordTopInsetLocked(display);
}
mLogicalDisplays.put(displayId, display);
// Wake up waitForDefaultDisplay.
if (isDefault) {
mSyncRoot.notifyAll();
}
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
return display;
}
这边DisplayDevice 用来构造了个LogicalDisplay,一个屏对应一个LogicalDisplay。查看LogicalDisplay相关说明,
A logical display may be mirrored onto multiple display devices in addition to its primary display device. LogicalDisplay 可以镜像到多个显示设备,这个就跟双屏同显感觉有关系了
/**
* Describes how a logical display is configured.
* <p>
* At this time, we only support logical displays that are coupled to a particular
* primary display device from which the logical display derives its basic properties
* such as its size, density and refresh rate.
* </p><p>
* A logical display may be mirrored onto multiple display devices in addition to its
* primary display device. Note that the contents of a logical display may not
* always be visible, even on its primary display device, such as in the case where
* the primary display device is currently mirroring content from a different
* logical display.
* </p><p>
* This object is designed to encapsulate as much of the policy of logical
* displays as possible. The idea is to make it easy to implement new kinds of
* logical displays mostly by making local changes to this class.
* </p><p>
* Note: The display manager architecture does not actually require logical displays
* to be associated with any individual display device. Logical displays and
* display devices are orthogonal concepts. Some mapping will exist between
* logical displays and display devices but it can be many-to-many and
* and some might have no relation at all.
* </p><p>
* Logical displays are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
final class LogicalDisplay {
后面查到performTraversalInternal, 这个和view绘制流程差不多有个performTraversals
void performTraversalInternal(SurfaceControl.Transaction t) {
synchronized (mSyncRoot) {
if (!mPendingTraversal) {
return;
}
mPendingTraversal = false;
performTraversalLocked(t);
}
// List is self-synchronized copy-on-write.
for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
listener.onDisplayTransaction(t);
}
}
private void performTraversalLocked(SurfaceControl.Transaction t) {
// 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);
configureDisplayLocked(t, device);
device.performTraversalLocked(t);
}
// Tell the input system about these new viewports.
if (mInputManagerInternal != null) {
mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
}
}
这里主要看configureDisplayLocked,同屏显示这边会调到display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
private void configureDisplayLocked(SurfaceControl.Transaction t, 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.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
final int viewportType;
// Update the corresponding viewport.
if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
viewportType = VIEWPORT_INTERNAL;
} else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
viewportType = VIEWPORT_EXTERNAL;
} else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
&& !TextUtils.isEmpty(info.uniqueId)) {
viewportType = VIEWPORT_VIRTUAL;
} else {
Slog.i(TAG, "Display " + info + " does not support input device matching.");
return;
}
populateViewportLocked(viewportType, display.getDisplayIdLocked(), device, info.uniqueId);
}
查看LogicalDisplay.configureDisplayLocked 这里主要就是控制显示、大小、旋转
/**
* Applies the layer stack and transformation to the given display device
* so that it shows the contents of this logical display.
*
* We know that the given display device is only ever showing the contents of
* a single logical display, so this method is expected to blow away all of its
* transformation properties to make it happen regardless of what the
* display device was previously showing.
*
* The caller must have an open Surface transaction.
*
* The display device may not be the primary display device, in the case
* where the display is being mirrored.
*
* @param device The display device to modify.
* @param isBlanked True if the device is being blanked.
*/
public void configureDisplayLocked(SurfaceControl.Transaction t,
DisplayDevice device,
boolean isBlanked) {
// Set the layer stack.
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
// Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
device.setAllowedDisplayModesLocked(mAllowedDisplayModes);
device.setRequestedColorModeLocked(mRequestedColorMode);
} else {
// Reset to default for non primary displays
device.setAllowedDisplayModesLocked(new int[] {0});
device.setRequestedColorModeLocked(0);
}
// 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();
// 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;
// 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;
Rect maskingInsets = getMaskingInsets(displayDeviceInfo);
InsetUtils.rotateInsets(maskingInsets, orientation);
// Don't consider the masked area as available when calculating the scaling below.
physWidth -= maskingInsets.left + maskingInsets.right;
physHeight -= maskingInsets.top + maskingInsets.bottom;
// 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 || mDisplayScalingDisabled) {
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);
// Now add back the offset for the masked area.
mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top);
if (orientation == Surface.ROTATION_0) {
mTempDisplayRect.offset(mDisplayOffsetX, mDisplayOffsetY);
} else if (orientation == Surface.ROTATION_90) {
mTempDisplayRect.offset(mDisplayOffsetY, -mDisplayOffsetX);
} else if (orientation == Surface.ROTATION_180) {
mTempDisplayRect.offset(-mDisplayOffsetX, -mDisplayOffsetY);
} else { // Surface.ROTATION_270
mTempDisplayRect.offset(-mDisplayOffsetY, mDisplayOffsetX);
}
device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect);
}
DisplayInfo默认是显示主屏大小,双屏同显,把主屏的数据镜像到其他屏 这边displayInfo.logicalWidth, displayInfo.logicalHeight 就是主屏的宽高
DisplayDeviceInfo 就是物理屏的信息,主屏1920x1200 和副屏1920x1080,physWidth physHeight 就是指的物理宽高
这一块就是缩放显示黑边的逻辑
// 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 || mDisplayScalingDisabled) {
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.
//Add by Seaton,display full screen if landscape
if (displayInfo.logicalHeight < displayInfo.logicalWidth) {
displayRectWidth = physWidth;
} else {//end Seaton
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);
// Now add back the offset for the masked area.
mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top);
最后
以上就是开放龙猫为你收集整理的Android Q 副屏HDMI 全屏显示的全部内容,希望文章能够帮你解决Android Q 副屏HDMI 全屏显示所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复