平台

RK3288 + Android 7.1 + EDP x 2 (1080P)

问题描述

打开客户的双屏异显应用, 发现副屏的显示布局挤压错乱, 经过测试排查, 发现是副屏的DPI过高导致(320).

解决

  • frameworks/base/services/core/java/com/android/server/display/DisplayDeviceInfo.java
    public void setAssumedDensityForExternalDisplay(int width, int height) {        //修改DPI XHDPI(320) -> MEDIUM(160), 也可以设置为任意想要的值.        densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_MEDIUM / 1080;        // Technically, these values should be smaller than the apparent density        // but we don't know the physical size of the display.                xDpi = densityDpi;        yDpi = densityDpi;    }

旋转副屏补丁

主要问题可以通过dumpsys display和 dumpsys window中看出, 强制旋转后, 副屏显示的方向存在两个问题

  1. 主屏已旋转, 但副屏仍然保持横屏
  2. 虽然已旋转, 但副屏显示的尺寸为 1920x1080, 而不是 1080x1920, 从而导致显示不全等问题

补丁见资源.

  • 解决双横屏强制旋转为双竖屏(旋转90度)
  • 解决旋转90度后, 副屏显示不完整

调试

dump下显示信息:

  • adb shell dumpsys display
DISPLAY MANAGER (dumpsys display)  mOnlyCode=false  mSafeMode=false  mPendingTraversal=false  mGlobalDisplayState=ON  mNextNonDefaultDisplayId=2  mDefaultViewport=DisplayViewport{valid=true, displayId=0, orientation=0, logicalFrame=Rect(0, 0 - 1920, 1080), physicalFrame=Rect(0, 0 - 1920, 1080), deviceWidth=1920, deviceHeight=1080}  mExternalTouchViewport=DisplayViewport{valid=true, displayId=0, orientation=0, logicalFrame=Rect(0, 0 - 1920, 1080), physicalFrame=Rect(0, 0 - 1920, 1080), deviceWidth=1920, deviceHeight=1080}  mDefaultDisplayDefaultColorMode=0  mSingleDisplayDemoMode=false  mWifiDisplayScanRequestCount=0Display Adapters: size=4  LocalDisplayAdapter  OverlayDisplayAdapter    mCurrentOverlaySetting=    mOverlays: size=0  WifiDisplayAdapter    mCurrentStatus=WifiDisplayStatus{featureState=2, scanState=0, activeDisplayState=0, activeDisplay=null, displays=[], sessionInfo=WifiDisplaySessionInfo:        Client/Owner: Client        GroupId:         Passphrase:         SessionId: 0        IP Address: }    mFeatureState=2    mScanState=0    mActiveDisplayState=0    mActiveDisplay=null    mDisplays=[]    mAvailableDisplays=[]    mRememberedDisplays=[]    mPendingStatusChangeBroadcast=false    mSupportsProtectedBuffers=false    mDisplayController:      mWifiDisplayOnSetting=false      mWifiP2pEnabled=true      mWfdEnabled=false      mWfdEnabling=false      mNetworkInfo=[type: WIFI_P2P[], state: UNKNOWN/IDLE, reason: (unspecified), extra: (none), failover: false, available: true, roaming: false, metered: false]      mScanRequested=false      mDiscoverPeersInProgress=false      mDesiredDevice=null      mConnectingDisplay=null      mDisconnectingDisplay=null      mCancelingDisplay=null      mConnectedDevice=null      mConnectionRetriesLeft=0      mRemoteDisplay=null      mRemoteDisplayInterface=null      mRemoteDisplayConnected=false      mAdvertisedDisplay=null      mAdvertisedDisplaySurface=null      mAdvertisedDisplayWidth=0      mAdvertisedDisplayHeight=0      mAdvertisedDisplayFlags=0      mAvailableWifiDisplayPeers: size=0  VirtualDisplayAdapterDisplay Devices: size=2  DisplayDeviceInfo{"内置屏幕": uniqueId="local:0", 1920 x 1080, modeId 1, defaultModeId 1, supportedModes [{id=1, width=1920, height=1080, fps=65.0}], colorMode 0, supportedColorModes [0], HdrCapabilities [email protected], density 160, 378.046 x 160.421 dpi, appVsyncOff 1000000, presDeadline 15384615, touch INTERNAL, rotation 0, type BUILT_IN, state ON, FLAG_DEFAULT_DISPLAY, FLAG_ROTATES_WITH_CONTENT, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}    mAdapter=LocalDisplayAdapter    mUniqueId=local:0    [email protected]    mCurrentLayerStack=0    mCurrentOrientation=0    mCurrentLayerStackRect=Rect(0, 0 - 1920, 1080)    mCurrentDisplayRect=Rect(0, 0 - 1920, 1080)    mCurrentSurface=null    mBuiltInDisplayId=0    mActivePhysIndex=0    mActiveModeId=1    mActiveColorMode=0    mState=ON    mBrightness=205    [email protected]e5b8    mDisplayInfos=      PhysicalDisplayInfo{1920 x 1080, 65.0 fps, density 1.0, 378.046 x 160.421 dpi, secure true, appVsyncOffset 1000000, bufferDeadline 15384615}    mSupportedModes=      DisplayModeRecord{mMode={id=1, width=1920, height=1080, fps=65.0}}    mSupportedColorModes=[0]  DisplayDeviceInfo{"HDMI 屏幕": uniqueId="local:1", 1920 x 1080, modeId 2, defaultModeId 2, supportedModes [{id=2, width=1920, height=1080, fps=63.000004}], colorMode 0, supportedColorModes [0], HdrCapabilities [email protected], density 320, 320.0 x 320.0 dpi, appVsyncOff 1000000, presDeadline 15873015, touch EXTERNAL, rotation 0, type HDMI, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION}    mAdapter=LocalDisplayAdapter    mUniqueId=local:1    [email protected]    mCurrentLayerStack=0    mCurrentOrientation=0    mCurrentLayerStackRect=Rect(0, 0 - 1920, 1080)    mCurrentDisplayRect=Rect(0, 0 - 1920, 1080)    mCurrentSurface=null    mBuiltInDisplayId=1    mActivePhysIndex=0    mActiveModeId=2    mActiveColorMode=0    mState=ON    mBrightness=-1    mBacklight=null    mDisplayInfos=      PhysicalDisplayInfo{1920 x 1080, 63.000004 fps, density 1.33125, 320.0 x 320.0 dpi, secure true, appVsyncOffset 1000000, bufferDeadline 15873015}    mSupportedModes=      DisplayModeRecord{mMode={id=2, width=1920, height=1080, fps=63.000004}}    mSupportedColorModes=[0]Logical Displays: size=2  Display 0:    mDisplayId=0    mLayerStack=0    mHasContent=true    mRequestedMode=0    mRequestedColorMode=0    mDisplayOffset=(0, 0)    mPrimaryDisplayDevice=内置屏幕    mBaseDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1920 x 1080, real 1920 x 1080, largest app 1920 x 1080, smallest app 1920 x 1080, mode 1, defaultMode 1, modes [{id=1, width=1920, height=1080, fps=65.0}], colorMode 0, supportedColorModes [0], hdrCapabilities [email protected], rotation 0, density 160 (378.046 x 160.421) dpi, layerStack 0, appVsyncOff 1000000, presDeadline 15384615, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}    mOverrideDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1920 x 1024, real 1920 x 1080, largest app 1920 x 1840, smallest app 1080 x 1000, mode 1, defaultMode 1, modes [{id=1, width=1920, height=1080, fps=65.0}], colorMode 0, supportedColorModes [0], hdrCapabilities [email protected], rotation 0, density 160 (378.046 x 160.421) dpi, layerStack 0, appVsyncOff 1000000, presDeadline 15384615, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}  Display 1:    mDisplayId=1    mLayerStack=1    mHasContent=false    mRequestedMode=0    mRequestedColorMode=0    mDisplayOffset=(0, 0)    mPrimaryDisplayDevice=HDMI 屏幕    mBaseDisplayInfo=DisplayInfo{"HDMI 屏幕", uniqueId "local:1", app 1920 x 1080, real 1920 x 1080, largest app 1920 x 1080, smallest app 1920 x 1080, mode 2, defaultMode 2, modes [{id=2, width=1920, height=1080, fps=63.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities [email protected], rotation 0, density 320 (320.0 x 320.0) dpi, layerStack 1, appVsyncOff 1000000, presDeadline 15873015, type HDMI, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION}    mOverrideDisplayInfo=DisplayInfo{"HDMI 屏幕", uniqueId "local:1", app 1920 x 1080, real 1920 x 1080, largest app 1920 x 1080, smallest app 1920 x 1080, mode 2, defaultMode 2, modes [{id=2, width=1920, height=1080, fps=63.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities [email protected], rotation 0, density 320 (320.0 x 320.0) dpi, layerStack 1, appVsyncOff 1000000, presDeadline 15873015, type HDMI, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION}

从上面的信息可以看得, 副屏HDMI 屏幕使用的是320的DPI.

分析排查

从dump出来的信息着手, 查找相关的变量PhysicalDisplayInfo信息来源:

  • frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
    private void tryConnectDisplayLocked(int builtInDisplayId) {        IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);        if (displayToken != null) {            SurfaceControl.PhysicalDisplayInfo[] configs =                    SurfaceControl.getDisplayConfigs(displayToken);    }
  • frameworks/base/core/java/android/view/SurfaceControl.java
    public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) {        if (displayToken == null) {            throw new IllegalArgumentException("displayToken must not be null");        }        return nativeGetDisplayConfigs(displayToken);    }    private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(            IBinder displayToken);
  • frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz,        jobject tokenObj) {    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));    if (token == NULL) return NULL;    Vector<DisplayInfo> configs;    if (SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||            configs.size() == 0) {        return NULL;    }//...}
  • frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::getDisplayConfigs(        const sp<IBinder>& display, Vector<DisplayInfo>* configs){    return ComposerService::getComposerService()->getDisplayConfigs(display, configs);}void ComposerService::connectLocked() {    const String16 name("SurfaceFlinger");    while (getService(name, &mComposerService) != NO_ERROR) {        usleep(250000);    }    assert(mComposerService != NULL);    // Create the death listener.    class DeathObserver : public IBinder::DeathRecipient {        ComposerService& mComposerService;        virtual void binderDied(const wp<IBinder>& who) {            ALOGW("ComposerService remote (surfaceflinger) died [%p]",                  who.unsafe_get());            mComposerService.composerServiceDied();        }     public:        DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }    };    mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));    IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);}

几经跳转, 获取的是SurfaceFlinger服务.

  • frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,        Vector<DisplayInfo>* configs) {    if ((configs == NULL) || (display.get() == NULL)) {        return BAD_VALUE;    }    if (!display.get())        return NAME_NOT_FOUND;    int32_t type = NAME_NOT_FOUND;    for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {        if (display == mBuiltinDisplays[i]) {            type = i;            break;        }    }    if (type < 0) {        return type;    }    // TODO: Not sure if display density should handled by SF any longer    class Density {        static int getDensityFromProperty(char const* propName) {            char property[PROPERTY_VALUE_MAX];            int density = 0;            if (property_get(propName, property, NULL) > 0) {                density = atoi(property);            }            return density;        }    public:        static int getEmuDensity() {            return getDensityFromProperty("qemu.sf.lcd_density"); }        static int getBuildDensity()  {            return getDensityFromProperty("ro.sf.lcd_density"); }    };    configs->clear();    for (const auto& hwConfig : getHwComposer().getConfigs(type)) {        DisplayInfo info = DisplayInfo();        float xdpi = hwConfig->getDpiX();        float ydpi = hwConfig->getDpiY();        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();        } 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->getWidth();        info.h = hwConfig->getHeight();        info.xdpi = xdpi;        info.ydpi = ydpi;        info.fps = 1e9 / hwConfig->getVsyncPeriod();        info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;        // This is how far in advance a buffer must be queued for        // presentation at a given time.  If you want a buffer to appear        // on the screen at time N, you must submit the buffer before        // (N - presentationDeadline).        //        // Normally it's one full refresh period (to give SF a chance to        // latch the buffer), but this can be reduced by configuring a        // DispSync offset.  Any additional delays introduced by the hardware        // composer or panel must be accounted for here.        //        // We add an additional 1ms to allow for processing time and        // differences between the ideal and actual refresh rate.        info.presentationDeadline = hwConfig->getVsyncPeriod() -                SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;        // All non-virtual displays are currently considered secure.        info.secure = true;        configs->push_back(info);    }    return NO_ERROR;}
  • frameworks/native/services/surfaceflinger/SurfaceFlinger.h
HWComposer& getHwComposer() const { return *mHwc; }
  • frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::init() {mHwc = new HWComposer(this);}
  • frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger)    : mFlinger(flinger),      mAdapter(),      mHwcDevice(),      mDisplayData(2),      mFreeDisplaySlots(),      mHwcDisplaySlots(),      mCBContext(),      mEventHandler(nullptr),      mVSyncCounts(),      mRemainingHwcVirtualDisplays(0){    for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {        mLastHwVSync[i] = 0;        mVSyncCounts[i] = 0;    }    loadHwcModule();}// Load and prepare the hardware composer module.  Sets mHwc.void HWComposer::loadHwcModule(){    ALOGV("loadHwcModule");    hw_module_t const* module;    if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {        ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID);        abort();    }    hw_device_t* device = nullptr;    int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);    if (error != 0) {        ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));        abort();    }    uint32_t majorVersion = (device->version >> 24) & 0xF;    if (majorVersion == 2) {        mHwcDevice = std::make_unique<HWC2::Device>(                reinterpret_cast<hwc2_device_t*>(device));    } else {        mAdapter = std::make_unique<HWC2On1Adapter>(                reinterpret_cast<hwc_composer_device_1_t*>(device));        uint8_t minorVersion = mAdapter->getHwc1MinorVersion();        if (minorVersion < 1) {            ALOGE("Cannot adapt to HWC version %d.%d",                    static_cast<int32_t>((minorVersion >> 8) & 0xF),                    static_cast<int32_t>(minorVersion & 0xF));            abort();        }        mHwcDevice = std::make_unique<HWC2::Device>(                static_cast<hwc2_device_t*>(mAdapter.get()));    }    mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();}

加载HW库(/system/lib/hw/hwcomposer.rk30board.so)

  • hardware/rockchip/hwcomposer/hwcomposer.cpp
static float getDefaultDensity(uint32_t width, uint32_t height) {    // Default density is based on TVs: 1080p displays get XHIGH density,    // lower-resolution displays get TV density. Maybe eventually we'll need    // to update it for 4K displays, though hopefully those just report    // accurate DPI information to begin with. This is also used for virtual    // displays and even primary displays with older hwcomposers, so be    // careful about orientation.    uint32_t h = width < height ? width : height;    if (h >= 1080) return ACONFIGURATION_DENSITY_XHIGH;//320    else           return ACONFIGURATION_DENSITY_TV;//213}static int hwc_get_display_attributes(struct hwc_composer_device_1 *dev,                                      int display, uint32_t config,                                      const uint32_t *attributes,                                      int32_t *values) {  UN_USED(config);  struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;  DrmConnector *c = ctx->drm.GetConnectorFromType(display);  if (!c) {    ALOGE("Failed to get DrmConnector for display %d", display);    return -ENODEV;  }  hwc_drm_display_t *hd = &ctx->displays[c->display()];  if (!hd->active)    return -ENODEV;  uint32_t mm_width = c->mm_width();  uint32_t mm_height = c->mm_height();  int w = hd->framebuffer_width;  int h = hd->framebuffer_height;  int vrefresh = hd->vrefresh;  for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; ++i) {    switch (attributes[i]) {      case HWC_DISPLAY_VSYNC_PERIOD:        values[i] = 1000 * 1000 * 1000 / vrefresh;        break;      case HWC_DISPLAY_WIDTH:        values[i] = w;        break;      case HWC_DISPLAY_HEIGHT:        values[i] = h;        break;      case HWC_DISPLAY_DPI_X:        /* Dots per 1000 inches */        values[i] = mm_width ? (w * UM_PER_INCH) / mm_width : getDefaultDensity(w,h)*1000;        break;      case HWC_DISPLAY_DPI_Y:        /* Dots per 1000 inches */        values[i] =            mm_height ? (h * UM_PER_INCH) / mm_height : getDefaultDensity(w,h)*1000;        break;    }  }  return 0;}

if (h >= 1080) return ACONFIGURATION_DENSITY_XHIGH;//320这部分代码, 直接返回了320的DPI
尝试修改为ACONFIGURATION_DENSITY_MEDIUM, 成功把PhysicalDisplayInfo修改为160 dpi
但是DisplayDeviceInfo中的DPI依然是320, 那么问题并不是出在PhysicalDisplayInfo

回过头, 重新看看dump的函数:

  • frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
    private void dumpInternal(PrintWriter pw) {        pw.println("DISPLAY MANAGER (dumpsys display)");//...            pw.println("Display Devices: size=" + mDisplayDevices.size());            for (DisplayDevice device : mDisplayDevices) {                pw.println("  " + device.getDisplayDeviceInfoLocked());                device.dumpLocked(ipw);            }      //....      }
  • frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java
    /**     * Gets information about the display device.     *     * The information returned should not change between calls unless the display     * adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event and     * {@link #applyPendingDisplayDeviceInfoChangesLocked()} has been called to apply     * the pending changes.     *     * @return The display device info, which should be treated as immutable by the caller.     * The display device should allocate a new display device info object whenever     * the data changes.     */    public abstract DisplayDeviceInfo getDisplayDeviceInfoLocked();
  • frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
private final class LocalDisplayDevice extends DisplayDevice        @Override        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {            if (mInfo == null) {                SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];                mInfo = new DisplayDeviceInfo();                mInfo.width = phys.width;//...                    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);}}
  • frameworks/base/services/core/java/com/android/server/display/DisplayDeviceInfo.java
    public void setAssumedDensityForExternalDisplay(int width, int height) {        densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;        // Technically, these values should be smaller than the apparent density        // but we don't know the physical size of the display.        xDpi = densityDpi;        yDpi = densityDpi;    }

找到了setAssumedDensityForExternalDisplay, 这熟悉的味道, 跟HW很像…

更多相关文章

  1. Android P Launcher3中添加未读信息角标
  2. Android 屏幕切换时不销毁Activity
  3. android从未安装的apk文件里获取信息(包信息,资源信息)
  4. Android唤醒、解锁屏幕代码实例
  5. android中,handler处理信息中弹出提示框注意事项
  6. 【Android】 从头搭建视频播放器(4)——屏幕旋转处理

随机推荐

  1. Android技术篇-了解Android的屏幕适配
  2. Android开发者的Air For Android简单入门
  3. Android单个进程内存分配
  4. 优秀的Android音频播放器
  5. Android版本与Android sdk int的对应关系
  6. (4.1.2.6)Android(安卓)判断app是否在前台
  7. Android定时任务的实现
  8. Android LinearLayout中实现水平方向控件
  9. Android——SharedPreferences
  10. android 常用布局有哪些