当前位置:   article > 正文

Android Display 系统分析

android display

Android Display 系统分析

大概两年前做过一个项目,大致是在Android 系统上实现双显的支持,其中有个需求是需要手动配置每个显示器的旋转角度,当时对Android 的 Display系统有关简单了解,但是不够深入。一直觉得是留下了一个遗憾,现在趁有时间来把这一块再好好了解下。闲话少说,开始吧。本文将按照以下方式来组织:
  • Android Display 框架
  • Android SurfaceFlinger中Display部分
  • Android Framework 中Display 部分
    DisplayManagerService对display的管理
    WindowManagerService对Display的管理
  • Android系统转屏问题

Android Display 框架

  1. Android中Display 框架如下:
  2. ![Android Display](https://img-blog.csdn.net/20161130214140525)
  3. 如上图所示,Android App除使用Android Presentation 外不需要特别了解Display的相关信息()。而在linux kernel当中的MIPI/HDMI等相关显示设备的驱动也不在本文的讨论范围之列。所以本文讨论的重点在于图中的Android FW中的DisplayManagerService 部分与SurfaceFlinger部分。

Android SurfaceFlinger中的Display部分

  1. 从Android 启动开始,我们知道在Android的启动过程中,SurfaceFlinger会作为一个系统进程被Init进程启动,具体的相关信息可以研究Android启动的相关流程。
  2. 在SurfaceFlinger中其init函数会在SurfaceFlinger被初始化后被调用。
  1. void SurfaceFlinger::init() {
  2. ALOGI( "SurfaceFlinger's main thread ready to run. "
  3. "Initializing graphics H/W...");
  4. Mutex::Autolock _l(mStateLock);
  5. // initialize EGL for the default display
  6. mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  7. eglInitialize(mEGLDisplay, NULL, NULL);
  8. // Initialize the H/W composer object. There may or may not be an
  9. // actual hardware composer underneath.
  10. mHwc = new HWComposer(this,
  11. *static_cast<HWComposer::EventHandler *>(this));
  12. ..........
  13. ..........

我们可以看到在init函数中会创建一个HWComposer的对象。

  1. HWComposer::HWComposer(
  2. const sp<SurfaceFlinger>& flinger,
  3. EventHandler& handler)
  4. : mFlinger(flinger),
  5. mFbDev(0), mHwc(0), mNumDisplays(1),
  6. mCBContext(new cb_context),
  7. mEventHandler(handler),
  8. mDebugForceFakeVSync(false)
  9. {
  10. ............
  11. .............
  12. // Note: some devices may insist that the FB HAL be opened before HWC.
  13. int fberr = loadFbHalModule();
  14. loadHwcModule();
  15. if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
  16. // close FB HAL if we don't needed it.
  17. // FIXME: this is temporary until we're not forced to open FB HAL
  18. // before HWC.
  19. framebuffer_close(mFbDev);
  20. mFbDev = NULL;
  21. }
  22. // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory.
  23. if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
  24. && !mFbDev) {
  25. ALOGE("ERROR: failed to open framebuffer (%s), aborting",
  26. strerror(-fberr));
  27. abort();
  28. }
  29. // these display IDs are always reserved
  30. for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
  31. mAllocatedDisplayIDs.markBit(i);
  32. }
  33. if (mHwc) {
  34. ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
  35. (hwcApiVersion(mHwc) >> 24) & 0xff,
  36. (hwcApiVersion(mHwc) >> 16) & 0xff);
  37. if (mHwc->registerProcs) {
  38. mCBContext->hwc = this;
  39. mCBContext->procs.invalidate = &hook_invalidate;
  40. mCBContext->procs.vsync = &hook_vsync;
  41. if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
  42. mCBContext->procs.hotplug = &hook_hotplug;
  43. else
  44. mCBContext->procs.hotplug = NULL;
  45. memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
  46. mHwc->registerProcs(mHwc, &mCBContext->procs);
  47. }
  48. // don't need a vsync thread if we have a hardware composer
  49. needVSyncThread = false;
  50. // always turn vsync off when we start
  51. eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
  52. // the number of displays we actually have depends on the
  53. // hw composer version
  54. if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
  55. // 1.3 adds support for virtual displays
  56. mNumDisplays = MAX_HWC_DISPLAYS;
  57. } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
  58. // 1.1 adds support for multiple displays
  59. mNumDisplays = NUM_BUILTIN_DISPLAYS;
  60. } else {
  61. mNumDisplays = 1;
  62. }
  63. }
  64. if (mFbDev) {
  65. //默认使用HWC设备,所以不会走FB分支
  66. ...............
  67. ...............
  68. } else if (mHwc) {
  69. // here we're guaranteed to have at least HWC 1.1
  70. // 查询系统相关显示设备。
  71. for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
  72. queryDisplayProperties(i);
  73. }
  74. }
  75. }

上面代码的主要意思是打开HWC设备,然后根据HWC的相关版本定义最多支持的显示设备数量。HWC是Android新版本引入的新模块,我个人的理解是替换掉早期的OverLayer机制,提供出全新的使用硬件合成的功能。而在我们这个范畴里只考虑了其对Display设备的管理。

  1. status_t HWComposer::queryDisplayProperties(int disp) {
  2. LOG_ALWAYS_FATAL_IF(!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1));
  3. // use zero as default value for unspecified attributes
  4. int32_t values[NUM_DISPLAY_ATTRIBUTES - 1];
  5. memset(values, 0, sizeof(values));
  6. const size_t MAX_NUM_CONFIGS = 128;
  7. uint32_t configs[MAX_NUM_CONFIGS] = {0};
  8. size_t numConfigs = MAX_NUM_CONFIGS;
  9. status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs);
  10. if (err != NO_ERROR) {
  11. // this can happen if an unpluggable display is not connected
  12. mDisplayData[disp].connected = false;
  13. return err;
  14. }
  15. mDisplayData[disp].currentConfig = 0;
  16. for (size_t c = 0; c < numConfigs; ++c) {
  17. err = mHwc->getDisplayAttributes(mHwc, disp, configs[c],
  18. DISPLAY_ATTRIBUTES, values);
  19. if (err != NO_ERROR) {
  20. // we can't get this display's info. turn it off.
  21. mDisplayData[disp].connected = false;
  22. return err;
  23. }
  24. DisplayConfig config = DisplayConfig();
  25. for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) {
  26. switch (DISPLAY_ATTRIBUTES[i]) {
  27. case HWC_DISPLAY_VSYNC_PERIOD:
  28. config.refresh = nsecs_t(values[i]);
  29. break;
  30. case HWC_DISPLAY_WIDTH:
  31. config.width = values[i];
  32. break;
  33. case HWC_DISPLAY_HEIGHT:
  34. config.height = values[i];
  35. break;
  36. case HWC_DISPLAY_DPI_X:
  37. config.xdpi = values[i] / 1000.0f;
  38. break;
  39. case HWC_DISPLAY_DPI_Y:
  40. config.ydpi = values[i] / 1000.0f;
  41. break;
  42. #ifdef MTK_AOSP_ENHANCEMENT
  43. case HWC_DISPLAY_SUBTYPE:
  44. mDisplayData[disp].subtype = values[i];
  45. break;
  46. #endif
  47. default:
  48. ALOG_ASSERT(false, "unknown display attribute[%zu] %#x",
  49. i, DISPLAY_ATTRIBUTES[i]);
  50. break;
  51. }
  52. }
  53. if (config.xdpi == 0.0f || config.ydpi == 0.0f) {
  54. float dpi = getDefaultDensity(config.width, config.height);
  55. config.xdpi = dpi;
  56. config.ydpi = dpi;
  57. }
  58. mDisplayData[disp].configs.push_back(config);
  59. }
  60. // FIXME: what should we set the format to?
  61. mDisplayData[disp].format = HAL_PIXEL_FORMAT_RGBA_8888;
  62. mDisplayData[disp].connected = true;
  63. return NO_ERROR;
  64. }

从上面代码中可以看出HWC是怎么查询到显示屏的相关的参数,如显示屏宽度高度刷新率等等,注意下,HWC中可以查询出很多组的显示屏的相关参数。

  1. uint32_t configs[MAX_NUM_CONFIGS] = {0};
  2. size_t numConfigs = MAX_NUM_CONFIGS;
  3. status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs);
  • 1
  • 2
  • 3

大胆的猜测下,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的消息。

  1. @Override
  2. public void onStart() { mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
  3. .............
  4. .............
  5. }

而这个消息会被registerDefaultDisplayAdapter函数处理。

  1. private void registerDefaultDisplayAdapter() {
  2. // Register default display adapter.
  3. synchronized (mSyncRoot) {
  4. registerDisplayAdapterLocked(new LocalDisplayAdapter(
  5. mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
  6. }
  7. }

啥都没干,只是创建了一个LocalDisplayAdapter对象。

  1. private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
  2. mDisplayAdapters.add(adapter);
  3. adapter.registerLocked();
  4. }

在这里插一句,DMS中有很多类型的的DisplayAdapter

  1. LocalDisplayAdapter是针对本地已经存在的物理显示屏设备。
  2. WifiDisplayAdapter针对WiFi Display
  3. OverlayDisplayAdapter 这个还没有来得及看
  4. VirtualDisplayAdapter 显示一个虚拟屏幕,该功能可以在开发者选项中开启,可以去研究下这个,可以把android 怎么composer然后display流程理的比较清楚,而且可以不用去关心kernel中的一些问题,比如display 驱动,HWC/Grelloc等等。

好了,在这里我们先只关心LocalDisplayAdapter.

  1. @Override
  2. public void registerLocked() {
  3. super.registerLocked();
  4. .........................
  5. for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
  6. tryConnectDisplayLocked(builtInDisplayId);
  7. }
  8. }

在这里,系统会去尝试连接两种显示屏幕,built in跟HDMI,builtin可以理解成默认的显示屏,比如手机中默认的MIPI屏,而HDMI则是扩展屏,目前在手机上集成HDMI接口的貌似不多,但是usb type c流行后,通过type c来扩展屏幕可能不少,这可能会是一个新的手机定制需求。

  1. private void tryConnectDisplayLocked(int builtInDisplayId) {
  2. IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
  3. if (displayToken != null) {
  4. SurfaceControl.PhysicalDisplayInfo[] configs =
  5. SurfaceControl.getDisplayConfigs(displayToken);
  6. if (configs == null) {
  7. // There are no valid configs for this device, so we can't use it
  8. Slog.w(TAG, "No valid configs found for display device " +
  9. builtInDisplayId);
  10. return;
  11. }
  12. int activeConfig = SurfaceControl.getActiveConfig(displayToken);
  13. if (activeConfig < 0) {
  14. // There is no active config, and for now we don't have the
  15. // policy to set one.
  16. Slog.w(TAG, "No active config found for display device " +
  17. builtInDisplayId);
  18. return;
  19. }
  20. LocalDisplayDevice device = mDevices.get(builtInDisplayId);
  21. if (device == null) {
  22. // Display was added.
  23. device = new LocalDisplayDevice(displayToken, builtInDisplayId,
  24. configs, activeConfig);
  25. mDevices.put(builtInDisplayId, device);
  26. sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
  27. } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
  28. // Display properties changed.
  29. sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
  30. }
  31. } else {
  32. // The display is no longer available. Ignore the attempt to add it.
  33. // If it was connected but has already been disconnected, we'll get a
  34. // disconnect event that will remove it from mDevices.
  35. }
  36. }

这个函数里主要干了这几件事:
1,从SurfaceFlinger 中获取到显示屏的所有支持的配置参数。以及正在使用的参数。

  1. status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
  2. Vector<DisplayInfo>* configs) {
  3. ..................
  4. ..................
  5. configs->clear();
  6. const Vector<HWComposer::DisplayConfig>& hwConfigs =
  7. getHwComposer().getConfigs(type);
  8. for (size_t c = 0; c < hwConfigs.size(); ++c) {
  9. const HWComposer::DisplayConfig& hwConfig = hwConfigs[c];
  10. DisplayInfo info = DisplayInfo();
  11. float xdpi = hwConfig.xdpi;
  12. float ydpi = hwConfig.ydpi;
  13. if (type == DisplayDevice::DISPLAY_PRIMARY) {
  14. // The density of the device is provided by a build property
  15. float density = Density::getBuildDensity() / 160.0f;
  16. if (density == 0) {
  17. // the build doesn't provide a density -- this is wrong!
  18. // use xdpi instead
  19. ALOGE("ro.sf.lcd_density must be defined as a build property");
  20. density = xdpi / 160.0f;
  21. }
  22. if (Density::getEmuDensity()) {
  23. // if "qemu.sf.lcd_density" is specified, it overrides everything
  24. xdpi = ydpi = density = Density::getEmuDensity();
  25. density /= 160.0f;
  26. }
  27. info.density = density;
  28. // TODO: this needs to go away (currently needed only by webkit)
  29. sp<const DisplayDevice> hw(getDefaultDisplayDevice());
  30. info.orientation = hw->getOrientation();
  31. #ifdef MTK_AOSP_ENHANCEMENT
  32. } else if (HWC_DISPLAY_SMARTBOOK == hwc.getSubType(type)) {
  33. static const int SMB_DENSITY = 160;
  34. info.density = SMB_DENSITY / 160.0f;
  35. info.orientation = 0;
  36. #endif
  37. } else {
  38. // TODO: where should this value come from?
  39. static const int TV_DENSITY = 213;
  40. info.density = TV_DENSITY / 160.0f;
  41. info.orientation = 0;
  42. }
  43. info.w = hwConfig.width;
  44. info.h = hwConfig.height;
  45. info.xdpi = xdpi;
  46. info.ydpi = ydpi;
  47. info.fps = float(1e9 / hwConfig.refresh);
  48. info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
  49. info.presentationDeadline =
  50. hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
  51. // All non-virtual displays are currently considered secure.
  52. info.secure = true;
  53. #ifdef MTK_AOSP_ENHANCEMENT
  54. // correct for primary display to normalize graphic plane
  55. if (DisplayDevice::DISPLAY_PRIMARY == type) {
  56. getDefaultDisplayDevice()->correctSizeByHwOrientation(info.w, info.h);
  57. }
  58. #endif
  59. configs->push_back(info);
  60. }
  61. return NO_ERROR;
  62. }

看到了吧,取到的就是之前提到的在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

  1. // Adds a new logical display based on the given display device.
  2. // Sends notifications if needed.
  3. private void addLogicalDisplayLocked(DisplayDevice device) {
  4. DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
  5. boolean isDefault = (deviceInfo.flags
  6. & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
  7. if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
  8. Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
  9. isDefault = false;
  10. }
  11. if (!isDefault && mSingleDisplayDemoMode) {
  12. Slog.i(TAG, "Not creating a logical display for a secondary display "
  13. + " because single display demo mode is enabled: " + deviceInfo);
  14. return;
  15. }
  16. final int displayId = assignDisplayIdLocked(isDefault);
  17. final int layerStack = assignLayerStackLocked(displayId);
  18. LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
  19. display.updateLocked(mDisplayDevices);
  20. if (!display.isValidLocked()) {
  21. // This should never happen currently.
  22. Slog.w(TAG, "Ignoring display device because the logical display "
  23. + "created from it was not considered valid: " + deviceInfo);
  24. return;
  25. }
  26. mLogicalDisplays.put(displayId, display);
  27. // Wake up waitForDefaultDisplay.
  28. if (isDefault) {
  29. mSyncRoot.notifyAll();
  30. }
  31. sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
  32. }

在最开始,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函数:

  1. public DisplayInfo getDisplayInfoLocked() {
  2. if (mInfo == null) {
  3. mInfo = new DisplayInfo();
  4. mInfo.copyFrom(mBaseDisplayInfo);
  5. if (mOverrideDisplayInfo != null) {
  6. mInfo.appWidth = mOverrideDisplayInfo.appWidth;
  7. mInfo.appHeight = mOverrideDisplayInfo.appHeight;
  8. mInfo.smallestNominalAppWidth = mOverrideDisplayInfo.smallestNominalAppWidth;
  9. mInfo.smallestNominalAppHeight = mOverrideDisplayInfo.smallestNominalAppHeight;
  10. mInfo.largestNominalAppWidth = mOverrideDisplayInfo.largestNominalAppWidth;
  11. mInfo.largestNominalAppHeight = mOverrideDisplayInfo.largestNominalAppHeight;
  12. mInfo.logicalWidth = mOverrideDisplayInfo.logicalWidth;
  13. mInfo.logicalHeight = mOverrideDisplayInfo.logicalHeight;
  14. mInfo.overscanLeft = mOverrideDisplayInfo.overscanLeft;
  15. mInfo.overscanTop = mOverrideDisplayInfo.overscanTop;
  16. mInfo.overscanRight = mOverrideDisplayInfo.overscanRight;
  17. mInfo.overscanBottom = mOverrideDisplayInfo.overscanBottom;
  18. mInfo.rotation = mOverrideDisplayInfo.rotation;
  19. mInfo.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
  20. mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
  21. mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
  22. }
  23. }
  24. return mInfo;
  25. }

注意到mOverrideDisplayInfo,这个比较重要,先标记下,后面会有介绍到。
而在WindowManagerService当中则使用了DisplayContent类间接操作Display类

  1. class DisplayContent {
  2. ...................
  3. private final Display mDisplay;
  4. ..................
  5. /**
  6. * @param display May not be null.
  7. * @param service You know.
  8. */
  9. DisplayContent(Display display, WindowManagerService service) {
  10. mDisplay = display;
  11. mDisplayId = display.getDisplayId();
  12. display.getDisplayInfo(mDisplayInfo);
  13. isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
  14. mService = service;
  15. }

就这样DisplayContent中的mDisplayInfo将等同与Display中的等同与LogicalDisplay中的。而且DisplayContent中的相关屏幕宽高参数会默认使用LogicalDisplay对象mDisplayInfo中的宽高:

  1. private void displayReady(int displayId) {
  2. synchronized(mWindowMap) {
  3. final DisplayContent displayContent = getDisplayContentLocked(displayId);
  4. if (displayContent != null) {
  5. mAnimator.addDisplayLocked(displayId);
  6. synchronized(displayContent.mDisplaySizeLock) {
  7. // Bootstrap the default logical display from the display manager.
  8. final DisplayInfo displayInfo = displayContent.getDisplayInfo();
  9. DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
  10. if (newDisplayInfo != null) {
  11. displayInfo.copyFrom(newDisplayInfo);
  12. }
  13. **displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
  14. displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
  15. displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
  16. displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
  17. displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;**
  18. displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
  19. displayContent.mBaseDisplayRect.set(0, 0,
  20. displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);
  21. }
  22. }
  23. }
  24. }

记得之前在DisplayManagerService中对LogicalDisplay的分析么?其屏幕相关配置参数的初始值等同于物理屏幕的参数。displayContent.mBaseDisplayHeigh与displayContent.mBaseDisplayWidth将会影响到系统对横竖屏参数的初始化:

  1. mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
  2. displayContent.mBaseDisplayWidth,
  3. displayContent.mBaseDisplayHeight,
  4. displayContent.mBaseDisplayDensity);

PhoneWindowsManager是整个Android系统中对显示窗口的策略类,在这里会决定屏幕的旋转与大小.

  1. @Override
  2. public void setInitialDisplaySize(Display display, int width, int height, int density) {
  3. // This method might be called before the policy has been fully initialized
  4. // or for other displays we don't care about.
  5. if (mContext == null || display.getDisplayId() != Display.DEFAULT_DISPLAY) {
  6. return;
  7. }
  8. mDisplay = display;
  9. final Resources res = mContext.getResources();
  10. int shortSize, longSize;
  11. if (width > height) {
  12. shortSize = height;
  13. longSize = width;
  14. mLandscapeRotation = Surface.ROTATION_0;
  15. mSeascapeRotation = Surface.ROTATION_180;
  16. if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
  17. mPortraitRotation = Surface.ROTATION_90;
  18. mUpsideDownRotation = Surface.ROTATION_270;
  19. } else {
  20. mPortraitRotation = Surface.ROTATION_270;
  21. mUpsideDownRotation = Surface.ROTATION_90;
  22. }
  23. } else {
  24. shortSize = width;
  25. longSize = height;
  26. mPortraitRotation = Surface.ROTATION_0;
  27. mUpsideDownRotation = Surface.ROTATION_180;
  28. if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
  29. mLandscapeRotation = Surface.ROTATION_270;
  30. mSeascapeRotation = Surface.ROTATION_90;
  31. } else {
  32. mLandscapeRotation = Surface.ROTATION_90;
  33. mSeascapeRotation = Surface.ROTATION_270;
  34. }
  35. }

这部分的逻辑就是检查宽高值之间的大小,如果宽大于高,则硬件是配置成横屏,那么mLandscapeRotation配置成Surface.ROTATION_0,意思是如果应用强行配置成Landscape模式显示则不需要转屏,mPortraitRotation配置成Surface.ROTATION_270或者Surface.ROTATION_90,意思是应用如果需要竖屏显示,则需要相应的转屏操作。反之如果高大于宽亦然。

接下来我们简单分析下Android下的转屏问题。

Android系统转屏问题

我们开始探讨这个问题之前,我们先假设下我们现在手上拥有一台设备,这台设备的物理尺寸是宽度720像素,高度1280像素, 那么很显然这是一部竖屏设备。那么我们假设现在需要启动一个强制横屏应用的应用程序,那么:
WindowManagerService当中的updateRotationUncheckedLocked最终会被调用:

  1. public boolean updateRotationUncheckedLocked(boolean inTransaction) {
  2. ...............................
  3. int rotation = (mIsUpdateIpoRotation || mIsUpdateAlarmBootRotation)
  4. ? Surface.ROTATION_0
  5. : mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);
  6. boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
  7. mForcedAppOrientation, rotation);
  8. ...............................
  9. updateDisplayAndOrientationLocked();
  10. }

mForcedAppOrientation 在这里会被置为ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
在PhoneWindowManagerService.java中:

  1. public int rotationForOrientationLw(int orientation, int lastRotation) {
  2. ....................
  3. case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
  4. // Return landscape unless overridden.
  5. if (isLandscapeOrSeascape(preferredRotation)) {
  6. return preferredRotation;
  7. }
  8. return mLandscapeRotation;

根据我们之前的分析,由于这原本是一个竖屏设备,那么mLandscapeRotation将等于Surface.ROTATION_90,即等于1.

回到WindowManagerService中来:

  1. DisplayInfo updateDisplayAndOrientationLocked() {
  2. // TODO(multidisplay): For now, apply Configuration to main screen only.
  3. final DisplayContent displayContent = getDefaultDisplayContentLocked();
  4. // Use the effective "visual" dimensions based on current rotation
  5. final boolean rotated = (mRotation == Surface.ROTATION_90
  6. || mRotation == Surface.ROTATION_270);
  7. final int realdw = rotated ?
  8. displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;
  9. final int realdh = rotated ?
  10. displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;
  11. int dw = realdw;
  12. int dh = realdh;
  13. if (mAltOrientation) {
  14. if (realdw > realdh) {
  15. // Turn landscape into portrait.
  16. int maxw = (int)(realdh/1.3f);
  17. if (maxw < realdw) {
  18. dw = maxw;
  19. }
  20. } else {
  21. // Turn portrait into landscape.
  22. int maxh = (int)(realdw/1.3f);
  23. if (maxh < realdh) {
  24. dh = maxh;
  25. }
  26. }
  27. }
  28. // Update application display metrics.
  29. final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
  30. final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
  31. final DisplayInfo displayInfo = displayContent.getDisplayInfo();
  32. synchronized(displayContent.mDisplaySizeLock) {
  33. displayInfo.rotation = mRotation;
  34. displayInfo.logicalWidth = dw;
  35. displayInfo.logicalHeight = dh;
  36. displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
  37. displayInfo.appWidth = appWidth;
  38. displayInfo.appHeight = appHeight;
  39. displayInfo.getLogicalMetrics(mRealDisplayMetrics,
  40. CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
  41. displayInfo.getAppMetrics(mDisplayMetrics);
  42. if (displayContent.mDisplayScalingDisabled) {
  43. displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
  44. } else {
  45. displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
  46. }
  47. mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
  48. displayContent.getDisplayId(), displayInfo);
  49. displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
  50. }
  51. if (false) {
  52. Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight);
  53. }
  54. mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
  55. mCompatDisplayMetrics);
  56. return displayInfo;
  57. }

我们会看到mRotation会等于Surface.ROTATION_90,所以有转屏动作,这时会变换屏幕的宽度与高度,并且将最新的宽高信息设置到LogicalDisplay对象中。

  1. final int realdw = rotated ?
  2. displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;
  3. final int realdh = rotated ?
  4. displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;
  5. int dw = realdw;
  6. int dh = realdh;
  7. ...................
  8. displayInfo.rotation = mRotation;
  9. displayInfo.logicalWidth = dw;
  10. displayInfo.logicalHeight = dh;
  11. displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
  12. displayInfo.appWidth = appWidth;
  13. displayInfo.appHeight = appHeight;
  14. ........................
  15. mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
  16. displayContent.getDisplayId(), displayInfo);

displayInfo.rotation 会被置为1。这个时候LogicalDisplay中的rotation信息,宽度与高度信息会与LocalDisplayDevice中不一致了。

LogicalDisplay 中的setDisplayInfoOverrideFromWindowManagerLocked函数,设置了mOverrideDisplayInfo,回头想想上面所提到的getDisplayInfoLocked函数。

  1. public boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
  2. if (info != null) {
  3. if (mOverrideDisplayInfo == null) {
  4. mOverrideDisplayInfo = new DisplayInfo(info);
  5. mInfo = null;
  6. return true;
  7. }
  8. if (!mOverrideDisplayInfo.equals(info)) {
  9. mOverrideDisplayInfo.copyFrom(info);
  10. mInfo = null;
  11. return true;
  12. }
  13. } else if (mOverrideDisplayInfo != null) {
  14. mOverrideDisplayInfo = null;
  15. mInfo = null;
  16. return true;
  17. }
  18. return false;
  19. }

与此同时,WindowManagerService会更新最新的Configure配置信息:

  1. void computeScreenConfigurationLocked(Configuration config) {
  2. final DisplayInfo displayInfo = updateDisplayAndOrientationLocked();
  3. ...............
  4. final DisplayInfo displayInfo = updateDisplayAndOrientationLocked();
  5. final int dw = displayInfo.logicalWidth;
  6. final int dh = displayInfo.logicalHeight;
  7. config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
  8. Configuration.ORIENTATION_LANDSCAPE;
  9. .........................
  10. mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);

这段代码里会调用上面有提到的updateDisplayAndOrientationLocked函数更新displayInfo信息,进而生成新的Configuration,之后会将Configuration发生出去,而这时一般情况下应用程序会收到转屏消息,应用会重新获取屏幕的宽高再重新绘制一遍。这里的屏幕的宽高指的是LogicalDisplay的。

而每次刷新屏幕的时候LogicalDisplay的configureDisplayInTransactionLocked会被调用:

  1. public void configureDisplayInTransactionLocked(DisplayDevice device,
  2. boolean isBlanked) {
  3. ..................
  4. // Only grab the display info now as it may have been changed based on the requests above.
  5. //获取LogicalDisplay的最新屏幕信息,见上面分析
  6. final DisplayInfo displayInfo = getDisplayInfoLocked();
  7. //获取LocalDisplayDevice的物理屏幕信息
  8. final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
  9. ............................
  10. int orientation = Surface.ROTATION_0;
  11. if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) {
  12. //设置与LogicalDisplay的转屏信息,本例子里肯定为1.
  13. orientation = displayInfo.rotation;
  14. }
  15. // Apply the physical rotation of the display device itself.
  16. //求余计算,结果依然为1嘛。。。
  17. orientation = (orientation + displayDeviceInfo.rotation) % 4;
  18. boolean rotated = (orientation == Surface.ROTATION_90
  19. || orientation == Surface.ROTATION_270);
  20. // 物理屏宽高参数修改,这是为啥。
  21. int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
  22. int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
  23. // Determine whether the width or height is more constrained to be scaled.
  24. // physWidth / displayInfo.logicalWidth => letter box
  25. // or physHeight / displayInfo.logicalHeight => pillar box
  26. //
  27. // We avoid a division (and possible floating point imprecision) here by
  28. // multiplying the fractions by the product of their denominators before
  29. // comparing them.
  30. int displayRectWidth, displayRectHeight;
  31. //计算在屏幕上的显示范围,这段逻辑还需要继续看看
  32. if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0) {
  33. displayRectWidth = displayInfo.logicalWidth;
  34. displayRectHeight = displayInfo.logicalHeight;
  35. } else if (physWidth * displayInfo.logicalHeight
  36. < physHeight * displayInfo.logicalWidth) {
  37. // Letter box.
  38. displayRectWidth = physWidth;
  39. displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
  40. } else {
  41. // Pillar box.
  42. displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
  43. displayRectHeight = physHeight;
  44. }
  45. /// M: Enable anti-overscan capability on wifi display @{
  46. if (displayDeviceInfo.type == Display.TYPE_WIFI) {
  47. displayRectWidth = (int) (displayRectWidth * ANTI_OVERSCAN_RATIO);
  48. displayRectHeight = (int) (displayRectHeight * ANTI_OVERSCAN_RATIO);
  49. }
  50. /// @}
  51. int displayRectTop = (physHeight - displayRectHeight) / 2;
  52. int displayRectLeft = (physWidth - displayRectWidth) / 2;
  53. mTempDisplayRect.set(displayRectLeft, displayRectTop,
  54. displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
  55. mTempDisplayRect.left += mDisplayOffsetX;
  56. mTempDisplayRect.right += mDisplayOffsetX;
  57. mTempDisplayRect.top += mDisplayOffsetY;
  58. mTempDisplayRect.bottom += mDisplayOffsetY;
  59. //将转屏信息,显示范围最终设置到SurfaceFlinger当中
  60. device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
  61. }

好了,这个就先到这里了,后面可以再写下两年前在Android 4.4上实现双屏幕的思路。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/658879
推荐阅读
相关标签
  

闽ICP备14008679号