Android(安卓)Display 系统分析
Android Display 系统分析
大概两年前做过一个项目,大致是在Android 系统上实现双显的支持,其中有个需求是需要手动配置每个显示器的旋转角度,当时对Android 的 Display系统有关简单了解,但是不够深入。一直觉得是留下了一个遗憾,现在趁有时间来把这一块再好好了解下。闲话少说,开始吧。本文将按照以下方式来组织:
- Android Display 框架
- Android SurfaceFlinger中Display部分
- Android Framework 中Display 部分
DisplayManagerService对display的管理
WindowManagerService对Display的管理 - Android系统转屏问题
Android Display 框架
Android中Display 框架如下:![Android Display](https://img-blog.csdn.net/20161130214140525)如上图所示,Android App除使用Android Presentation 外不需要特别了解Display的相关信息()。而在linux kernel当中的MIPI/HDMI等相关显示设备的驱动也不在本文的讨论范围之列。所以本文讨论的重点在于图中的Android FW中的DisplayManagerService 部分与SurfaceFlinger部分。
Android SurfaceFlinger中的Display部分
从Android 启动开始,我们知道在Android的启动过程中,SurfaceFlinger会作为一个系统进程被Init进程启动,具体的相关信息可以研究Android启动的相关流程。在SurfaceFlinger中其init函数会在SurfaceFlinger被初始化后被调用。
void SurfaceFlinger::init() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); Mutex::Autolock _l(mStateLock); // initialize EGL for the default display mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(mEGLDisplay, NULL, NULL); // Initialize the H/W composer object. There may or may not be an // actual hardware composer underneath. mHwc = new HWComposer(this, *static_cast(this)); .......... ..........
我们可以看到在init函数中会创建一个HWComposer的对象。
HWComposer::HWComposer( const sp<SurfaceFlinger>& flinger, EventHandler& handler) : mFlinger(flinger), mFbDev(0), mHwc(0), mNumDisplays(1), mCBContext(new cb_context), mEventHandler(handler), mDebugForceFakeVSync(false){ ............ ............. // Note: some devices may insist that the FB HAL be opened before HWC. int fberr = loadFbHalModule(); loadHwcModule(); if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { // close FB HAL if we don't needed it. // FIXME: this is temporary until we're not forced to open FB HAL // before HWC. framebuffer_close(mFbDev); mFbDev = NULL; } // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory. if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) && !mFbDev) { ALOGE("ERROR: failed to open framebuffer (%s), aborting", strerror(-fberr)); abort(); } // these display IDs are always reserved for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { mAllocatedDisplayIDs.markBit(i); } if (mHwc) { ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER, (hwcApiVersion(mHwc) >> 24) & 0xff, (hwcApiVersion(mHwc) >> 16) & 0xff); if (mHwc->registerProcs) { mCBContext->hwc = this; mCBContext->procs.invalidate = &hook_invalidate; mCBContext->procs.vsync = &hook_vsync; if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) mCBContext->procs.hotplug = &hook_hotplug; else mCBContext->procs.hotplug = NULL; memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero)); mHwc->registerProcs(mHwc, &mCBContext->procs); } // don't need a vsync thread if we have a hardware composer needVSyncThread = false; // always turn vsync off when we start eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0); // the number of displays we actually have depends on the // hw composer version if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { // 1.3 adds support for virtual displays mNumDisplays = MAX_HWC_DISPLAYS; } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { // 1.1 adds support for multiple displays mNumDisplays = NUM_BUILTIN_DISPLAYS; } else { mNumDisplays = 1; } } if (mFbDev) { //默认使用HWC设备,所以不会走FB分支 ............... ............... } else if (mHwc) { // here we're guaranteed to have at least HWC 1.1 // 查询系统相关显示设备。 for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { queryDisplayProperties(i); } }}
上面代码的主要意思是打开HWC设备,然后根据HWC的相关版本定义最多支持的显示设备数量。HWC是Android新版本引入的新模块,我个人的理解是替换掉早期的OverLayer机制,提供出全新的使用硬件合成的功能。而在我们这个范畴里只考虑了其对Display设备的管理。
status_t HWComposer::queryDisplayProperties(int disp) { LOG_ALWAYS_FATAL_IF(!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)); // use zero as default value for unspecified attributes int32_t values[NUM_DISPLAY_ATTRIBUTES - 1]; memset(values, 0, sizeof(values)); const size_t MAX_NUM_CONFIGS = 128; uint32_t configs[MAX_NUM_CONFIGS] = {0}; size_t numConfigs = MAX_NUM_CONFIGS; status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs); if (err != NO_ERROR) { // this can happen if an unpluggable display is not connected mDisplayData[disp].connected = false; return err; } mDisplayData[disp].currentConfig = 0; for (size_t c = 0; c < numConfigs; ++c) { err = mHwc->getDisplayAttributes(mHwc, disp, configs[c], DISPLAY_ATTRIBUTES, values); if (err != NO_ERROR) { // we can't get this display's info. turn it off. mDisplayData[disp].connected = false; return err; } DisplayConfig config = DisplayConfig(); for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) { switch (DISPLAY_ATTRIBUTES[i]) { case HWC_DISPLAY_VSYNC_PERIOD: config.refresh = nsecs_t(values[i]); break; case HWC_DISPLAY_WIDTH: config.width = values[i]; break; case HWC_DISPLAY_HEIGHT: config.height = values[i]; break; case HWC_DISPLAY_DPI_X: config.xdpi = values[i] / 1000.0f; break; case HWC_DISPLAY_DPI_Y: config.ydpi = values[i] / 1000.0f; break;#ifdef MTK_AOSP_ENHANCEMENT case HWC_DISPLAY_SUBTYPE: mDisplayData[disp].subtype = values[i]; break;#endif default: ALOG_ASSERT(false, "unknown display attribute[%zu] %#x", i, DISPLAY_ATTRIBUTES[i]); break; } } if (config.xdpi == 0.0f || config.ydpi == 0.0f) { float dpi = getDefaultDensity(config.width, config.height); config.xdpi = dpi; config.ydpi = dpi; } mDisplayData[disp].configs.push_back(config); } // FIXME: what should we set the format to? mDisplayData[disp].format = HAL_PIXEL_FORMAT_RGBA_8888; mDisplayData[disp].connected = true; return NO_ERROR;}
从上面代码中可以看出HWC是怎么查询到显示屏的相关的参数,如显示屏宽度高度刷新率等等,注意下,HWC中可以查询出很多组的显示屏的相关参数。
uint32_t configs[MAX_NUM_CONFIGS] = {0}; size_t numConfigs = MAX_NUM_CONFIGS; status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs);
大胆的猜测下,android中是否会开始支持分辨率的动态调整了呢?从以为的经验来说,一个手机在出厂的时候就固定好了分辨率,后续是不是能像windows 系统一样能动态调整呢?
我们记一下,显示屏的相关参数被保留在mDisplayData[disp]中。SurfaceFlinger中的display相关先到这里。之后再回来看看。
Android Framework 中Display 部分
DisplayManagerService对display的管理
从最上面的Android Display 框架图中可以看到,在Android 的JAVA的系统服务中会有一个DisplayManagerService的系统服务与我们常见的ActivityManagerService/WindowsManagerService 并列。从名字中也能看出来它实现的就是对Android Display的管理,这一节开始研究下这个系统服务。
DisplayManagerService的启动在于Android系统流程中由systemserver启动,与AMS/WMS 等JAVA层系统服务的启动方式一致。在这里就不再赘述了。接下来我们先来看看DMS(DisplayManagerService)怎么拿到已经存在的显示屏相关信息,注意,怎么获取显示屏相关信息已经在上一节中有介绍过了。
DisplayManagerService.java中DMS服务启动之时onStart函数会被调用,这个函数中会外发一个MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER的消息。
@Override public void onStart() { mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);.......................... }
而这个消息会被registerDefaultDisplayAdapter函数处理。
private void registerDefaultDisplayAdapter() { // Register default display adapter. synchronized (mSyncRoot) { registerDisplayAdapterLocked(new LocalDisplayAdapter( mSyncRoot, mContext, mHandler, mDisplayAdapterListener)); } }
啥都没干,只是创建了一个LocalDisplayAdapter对象。
private void registerDisplayAdapterLocked(DisplayAdapter adapter) { mDisplayAdapters.add(adapter); adapter.registerLocked(); }
在这里插一句,DMS中有很多类型的的DisplayAdapter
- LocalDisplayAdapter是针对本地已经存在的物理显示屏设备。
- WifiDisplayAdapter针对WiFi Display
- OverlayDisplayAdapter 这个还没有来得及看
- VirtualDisplayAdapter 显示一个虚拟屏幕,该功能可以在开发者选项中开启,可以去研究下这个,可以把android 怎么composer然后display流程理的比较清楚,而且可以不用去关心kernel中的一些问题,比如display 驱动,HWC/Grelloc等等。
好了,在这里我们先只关心LocalDisplayAdapter.
@Override public void registerLocked() { super.registerLocked(); ......................... for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) { tryConnectDisplayLocked(builtInDisplayId); } }
在这里,系统会去尝试连接两种显示屏幕,built in跟HDMI,builtin可以理解成默认的显示屏,比如手机中默认的MIPI屏,而HDMI则是扩展屏,目前在手机上集成HDMI接口的貌似不多,但是usb type c流行后,通过type c来扩展屏幕可能不少,这可能会是一个新的手机定制需求。
private void tryConnectDisplayLocked(int builtInDisplayId) { IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId); if (displayToken != null) { SurfaceControl.PhysicalDisplayInfo[] configs = SurfaceControl.getDisplayConfigs(displayToken); if (configs == null) { // There are no valid configs for this device, so we can't use it Slog.w(TAG, "No valid configs found for display device " + builtInDisplayId); return; } int activeConfig = SurfaceControl.getActiveConfig(displayToken); if (activeConfig < 0) { // There is no active config, and for now we don't have the // policy to set one. Slog.w(TAG, "No active config found for display device " + builtInDisplayId); return; } LocalDisplayDevice device = mDevices.get(builtInDisplayId); if (device == null) { // Display was added. device = new LocalDisplayDevice(displayToken, builtInDisplayId, configs, activeConfig); mDevices.put(builtInDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) { // Display properties changed. sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } } else { // The display is no longer available. Ignore the attempt to add it. // If it was connected but has already been disconnected, we'll get a // disconnect event that will remove it from mDevices. } }
这个函数里主要干了这几件事:
1,从SurfaceFlinger 中获取到显示屏的所有支持的配置参数。以及正在使用的参数。
status_t SurfaceFlinger::getDisplayConfigs(const sp& display, Vector* configs) { .................. .................. configs->clear(); const Vector& hwConfigs = getHwComposer().getConfigs(type); for (size_t c = 0; c < hwConfigs.size(); ++c) { const HWComposer::DisplayConfig& hwConfig = hwConfigs[c]; DisplayInfo info = DisplayInfo(); float xdpi = hwConfig.xdpi; float ydpi = hwConfig.ydpi; if (type == DisplayDevice::DISPLAY_PRIMARY) { // The density of the device is provided by a build property float density = Density::getBuildDensity() / 160.0f; if (density == 0) { // the build doesn't provide a density -- this is wrong! // use xdpi instead ALOGE("ro.sf.lcd_density must be defined as a build property"); density = xdpi / 160.0f; } if (Density::getEmuDensity()) { // if "qemu.sf.lcd_density" is specified, it overrides everything xdpi = ydpi = density = Density::getEmuDensity(); density /= 160.0f; } info.density = density; // TODO: this needs to go away (currently needed only by webkit) sp<const DisplayDevice> hw(getDefaultDisplayDevice()); info.orientation = hw->getOrientation();#ifdef MTK_AOSP_ENHANCEMENT } else if (HWC_DISPLAY_SMARTBOOK == hwc.getSubType(type)) { static const int SMB_DENSITY = 160; info.density = SMB_DENSITY / 160.0f; info.orientation = 0;#endif } else { // TODO: where should this value come from? static const int TV_DENSITY = 213; info.density = TV_DENSITY / 160.0f; info.orientation = 0; } info.w = hwConfig.width; info.h = hwConfig.height; info.xdpi = xdpi; info.ydpi = ydpi; info.fps = float(1e9 / hwConfig.refresh); info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; info.presentationDeadline = hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000; // All non-virtual displays are currently considered secure. info.secure = true;#ifdef MTK_AOSP_ENHANCEMENT // correct for primary display to normalize graphic plane if (DisplayDevice::DISPLAY_PRIMARY == type) { getDefaultDisplayDevice()->correctSizeByHwOrientation(info.w, info.h); }#endif configs->push_back(info); } return NO_ERROR;}
看到了吧,取到的就是之前提到的在SurfaceFlinger怎么获取display信息的。
2,创建新的LocalDisplayDevice对象,并且根据正在使用的参数配置LocalDisplayDevice对象。
LocalDisplayDevice会保留所有的显示屏所支持的配置信息。
特别注意:
mBaseDisplayInfo.rotation = Surface.ROTATION_0;
Surface.ROTATION_0的意思是不转屏,也就是说,如果显示屏配置成了横屏设备,那么Surface.ROTATION_90 就意味着需要转屏90度成为竖屏了。
3,通过DISPLAY_DEVICE_EVENT_ADDED消息告知DMS有新的显示设备添加。
DMS会去处理DISPLAY_DEVICE_EVENT_ADDED消息,并且会去创建一个新的LogicalDisplay
// Adds a new logical display based on the given display device. // Sends notifications if needed. private void 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; } final int displayId = assignDisplayIdLocked(isDefault); 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; } mLogicalDisplays.put(displayId, display); // Wake up waitForDefaultDisplay. if (isDefault) { mSyncRoot.notifyAll(); } sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); }
在最开始,LogicalDisplay会使用与LocalDisplayDevice同样的显示屏配置信息。同时会为这个LogicalDisplay设备分配displayId 与layerStack,displayId很好理解,每个显示设备就有自己的display id嘛,layerStack是用来干嘛的呢?研究下SurfaceFlinger的源码就能理解,sf会把相同layerStack值的图层composer在一起,丢给display去显示。
在这里可能需要思考下为什么Android需要使用LogicalDisplay呢,这个跟LocalDisplayDevice究竟是什么区别呢?在这里我的理解是LocalDisplayDevice是真实存在,是本质,是一块实实在在的显示设备,不可改变。有具体的宽度,高度等信息。而LogicalDisplay是表象,是能够依托与LocalDisplayDevice,并且能更改的。比如LocalDisplayDevice描述了一个宽度是720,高度是1280的竖屏设备,如果这个设备被默认当做横屏设备使用,那么就应该创建一个高度是720,宽度是1280的横屏LogicalDisplay设备。接下来,我们就要开始深入研究这个了。
WindowManagerService对display的管理
除此之外,Android 在framework中还包装有一个Display 的类作为对DisplayManagerService中display设备的封装。其中Display 类中最重要的成员变量
private DisplayInfo mDisplayInfo;
来自于LogicalDisplay对象中,通过display ID让两者指向同一个显示屏.至于具体这两个对象怎么联系在一起的在这里我不做多介绍,有兴趣的自己去翻源码。
LogicalDisplay类中的getDisplayInfoLocked函数:
public DisplayInfo getDisplayInfoLocked() { if (mInfo == null) { mInfo = new DisplayInfo(); mInfo.copyFrom(mBaseDisplayInfo); if (mOverrideDisplayInfo != null) { mInfo.appWidth = mOverrideDisplayInfo.appWidth; mInfo.appHeight = mOverrideDisplayInfo.appHeight; mInfo.smallestNominalAppWidth = mOverrideDisplayInfo.smallestNominalAppWidth; mInfo.smallestNominalAppHeight = mOverrideDisplayInfo.smallestNominalAppHeight; mInfo.largestNominalAppWidth = mOverrideDisplayInfo.largestNominalAppWidth; mInfo.largestNominalAppHeight = mOverrideDisplayInfo.largestNominalAppHeight; mInfo.logicalWidth = mOverrideDisplayInfo.logicalWidth; mInfo.logicalHeight = mOverrideDisplayInfo.logicalHeight; mInfo.overscanLeft = mOverrideDisplayInfo.overscanLeft; mInfo.overscanTop = mOverrideDisplayInfo.overscanTop; mInfo.overscanRight = mOverrideDisplayInfo.overscanRight; mInfo.overscanBottom = mOverrideDisplayInfo.overscanBottom; mInfo.rotation = mOverrideDisplayInfo.rotation; mInfo.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi; mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi; mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi; } } return mInfo; }
注意到mOverrideDisplayInfo,这个比较重要,先标记下,后面会有介绍到。
而在WindowManagerService当中则使用了DisplayContent类间接操作Display类
class DisplayContent {................... private final Display mDisplay;.................. /** * @param display May not be null. * @param service You know. */ DisplayContent(Display display, WindowManagerService service) { mDisplay = display; mDisplayId = display.getDisplayId(); display.getDisplayInfo(mDisplayInfo); isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; mService = service; }
就这样DisplayContent中的mDisplayInfo将等同与Display中的等同与LogicalDisplay中的。而且DisplayContent中的相关屏幕宽高参数会默认使用LogicalDisplay对象mDisplayInfo中的宽高:
private void displayReady(int displayId) { synchronized(mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null) { mAnimator.addDisplayLocked(displayId); synchronized(displayContent.mDisplaySizeLock) { // Bootstrap the default logical display from the display manager. final DisplayInfo displayInfo = displayContent.getDisplayInfo(); DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId); if (newDisplayInfo != null) { displayInfo.copyFrom(newDisplayInfo); } **displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth; displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;** displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity; displayContent.mBaseDisplayRect.set(0, 0, displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight); } } } }
记得之前在DisplayManagerService中对LogicalDisplay的分析么?其屏幕相关配置参数的初始值等同于物理屏幕的参数。displayContent.mBaseDisplayHeigh与displayContent.mBaseDisplayWidth将会影响到系统对横竖屏参数的初始化:
mPolicy.setInitialDisplaySize(displayContent.getDisplay(), displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity);
PhoneWindowsManager是整个Android系统中对显示窗口的策略类,在这里会决定屏幕的旋转与大小.
@Override public void setInitialDisplaySize(Display display, int width, int height, int density) { // This method might be called before the policy has been fully initialized // or for other displays we don't care about. if (mContext == null || display.getDisplayId() != Display.DEFAULT_DISPLAY) { return; } mDisplay = display; final Resources res = mContext.getResources(); int shortSize, longSize; if (width > height) { shortSize = height; longSize = width; mLandscapeRotation = Surface.ROTATION_0; mSeascapeRotation = Surface.ROTATION_180; if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) { mPortraitRotation = Surface.ROTATION_90; mUpsideDownRotation = Surface.ROTATION_270; } else { mPortraitRotation = Surface.ROTATION_270; mUpsideDownRotation = Surface.ROTATION_90; } } else { shortSize = width; longSize = height; mPortraitRotation = Surface.ROTATION_0; mUpsideDownRotation = Surface.ROTATION_180; if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) { mLandscapeRotation = Surface.ROTATION_270; mSeascapeRotation = Surface.ROTATION_90; } else { mLandscapeRotation = Surface.ROTATION_90; mSeascapeRotation = Surface.ROTATION_270; } }
这部分的逻辑就是检查宽高值之间的大小,如果宽大于高,则硬件是配置成横屏,那么mLandscapeRotation配置成Surface.ROTATION_0,意思是如果应用强行配置成Landscape模式显示则不需要转屏,mPortraitRotation配置成Surface.ROTATION_270或者Surface.ROTATION_90,意思是应用如果需要竖屏显示,则需要相应的转屏操作。反之如果高大于宽亦然。
接下来我们简单分析下Android下的转屏问题。
Android系统转屏问题
我们开始探讨这个问题之前,我们先假设下我们现在手上拥有一台设备,这台设备的物理尺寸是宽度720像素,高度1280像素, 那么很显然这是一部竖屏设备。那么我们假设现在需要启动一个强制横屏应用的应用程序,那么:
WindowManagerService当中的updateRotationUncheckedLocked最终会被调用:
public boolean updateRotationUncheckedLocked(boolean inTransaction) { ............................... int rotation = (mIsUpdateIpoRotation || mIsUpdateAlarmBootRotation) ? Surface.ROTATION_0 : mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation); boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( mForcedAppOrientation, rotation); ............................... updateDisplayAndOrientationLocked(); }
mForcedAppOrientation 在这里会被置为ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
在PhoneWindowManagerService.java中:
public int rotationForOrientationLw(int orientation, int lastRotation) {.................... case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: // Return landscape unless overridden. if (isLandscapeOrSeascape(preferredRotation)) { return preferredRotation; } return mLandscapeRotation;
根据我们之前的分析,由于这原本是一个竖屏设备,那么mLandscapeRotation将等于Surface.ROTATION_90,即等于1.
回到WindowManagerService中来:
DisplayInfo updateDisplayAndOrientationLocked() { // 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); 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); displayContent.mBaseDisplayRect.set(0, 0, dw, dh); } if (false) { Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight); } mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics, mCompatDisplayMetrics); return displayInfo; }
我们会看到mRotation会等于Surface.ROTATION_90,所以有转屏动作,这时会变换屏幕的宽度与高度,并且将最新的宽高信息设置到LogicalDisplay对象中。
final int realdw = rotated ? displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; final int realdh = rotated ? displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; int dw = realdw; int dh = realdh;................... displayInfo.rotation = mRotation; displayInfo.logicalWidth = dw; displayInfo.logicalHeight = dh; displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity; displayInfo.appWidth = appWidth; displayInfo.appHeight = appHeight;........................ mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( displayContent.getDisplayId(), displayInfo);
displayInfo.rotation 会被置为1。这个时候LogicalDisplay中的rotation信息,宽度与高度信息会与LocalDisplayDevice中不一致了。
LogicalDisplay 中的setDisplayInfoOverrideFromWindowManagerLocked函数,设置了mOverrideDisplayInfo,回头想想上面所提到的getDisplayInfoLocked函数。
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; }
与此同时,WindowManagerService会更新最新的Configure配置信息:
void computeScreenConfigurationLocked(Configuration config) { final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(); ............... final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(); final int dw = displayInfo.logicalWidth; final int dh = displayInfo.logicalHeight; config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; .........................mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
这段代码里会调用上面有提到的updateDisplayAndOrientationLocked函数更新displayInfo信息,进而生成新的Configuration,之后会将Configuration发生出去,而这时一般情况下应用程序会收到转屏消息,应用会重新获取屏幕的宽高再重新绘制一遍。这里的屏幕的宽高指的是LogicalDisplay的。
而每次刷新屏幕的时候LogicalDisplay的configureDisplayInTransactionLocked会被调用:
public void configureDisplayInTransactionLocked(DisplayDevice device, boolean isBlanked) {.................. // Only grab the display info now as it may have been changed based on the requests above. //获取LogicalDisplay的最新屏幕信息,见上面分析 final DisplayInfo displayInfo = getDisplayInfoLocked(); //获取LocalDisplayDevice的物理屏幕信息 final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();............................ int orientation = Surface.ROTATION_0; if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) { //设置与LogicalDisplay的转屏信息,本例子里肯定为1. orientation = displayInfo.rotation; } // Apply the physical rotation of the display device itself. //求余计算,结果依然为1嘛。。。 orientation = (orientation + displayDeviceInfo.rotation) % 4; 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; } /// M: Enable anti-overscan capability on wifi display @{ if (displayDeviceInfo.type == Display.TYPE_WIFI) { displayRectWidth = (int) (displayRectWidth * ANTI_OVERSCAN_RATIO); displayRectHeight = (int) (displayRectHeight * ANTI_OVERSCAN_RATIO); } /// @} 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;//将转屏信息,显示范围最终设置到SurfaceFlinger当中 device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect); }
好了,这个就先到这里了,后面可以再写下两年前在Android 4.4上实现双屏幕的思路。
更多相关文章
- Android系统设计中存在设计模式分析
- android图形系统详解二:Drawables
- 基于Android(安卓)5.1系统的nfc读卡驱动和上层的调试记录,nfc移植
- Android(安卓)View视图系统分析和Scroller和OverScroller分析
- Android系统布局——android.R.layout详解
- windows系统上安装与使用Android(安卓)NDK r5
- Android系统架构基本模式解析
- 深入浅出 - Android系统移植与平台开发(九)- JNI介绍
- Android(安卓)Debug Bridge