Android R DisplayManagerService模块(2) DisplayAdapter和DisplayDevice的创建

2,606 阅读5分钟

在上一篇Android R DisplayManagerService模块(1) 启动中说道,执行onStart()方法时,将开始注册默认的Display适配器,本篇文章就该流程进行分析和总结。

1.创建屏幕适配器对象

在onStart()方法中,将会在android.display线程中执行registerDefaultDisplayAdapters()方法去注册默认DisplayAdapter:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

    private void registerDefaultDisplayAdapters() {
        // Register default display adapters.
        synchronized (mSyncRoot) {
            // main display adapter
            registerDisplayAdapterLocked(new LocalDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
            ......
        }
    }

这里创建了LocalDisplayAdapter对象,并将该对象作为参数,执行registerDisplayAdapterLocked()方法:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
    private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
        mDisplayAdapters.add(adapter);
        adapter.registerLocked();
    }

首先,将LocalDisplayAdapter对象添加到mDisplayAdapters中。mDisplayAdapters是DMS中用来保存所有DisplayAdapter的列表。LocalDisplayAdapter的构造方法中没有多余的处理,基本属性的赋值都在其父类DisplayAdapter中:

// frameworks/base/services/core/java/com/android/server/display/DisplayAdapter.java

public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
        Context context, Handler handler, Listener listener, String name) {
    // DMS模块全局同步锁
    mSyncRoot = syncRoot;
    mContext = context;
    // android.display线程的Handler
    mHandler = handler;
    // DislayApdater.Listener对象,用于回调DMS
    mListener = listener;
    mName = name;
}

2.创建物理屏对象

创建好适配器对象后,将执行registerLocked()方法,进入到DisplayAdapter中,和物理屏进行连接, LocalDisplayAdapter.registerLocked()如下:

@Override
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
public void registerLocked() {
    super.registerLocked();
    // 从SurfaceControl中获取物理DisplayId
    for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
        // 连接Physical Display
        tryConnectDisplayLocked(physicalDisplayId);
    }
}

这个方法中,首先通过SurfaceControl获得所有的物理显示屏幕id,然后执行tryConnectDisplayLocked()方法,根据id创建对应的物理显示并和DMS进行连接:

// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java

private void tryConnectDisplayLocked(long physicalDisplayId) {
    // 根据id获取Display的token
    final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
    if (displayToken != null) {
        // 根据token获取所有的物理显示配置项
        SurfaceControl.PhysicalDisplayInfo[] configs =
                SurfaceControl.getDisplayConfigs(displayToken);
        // 根据token获取正在使用的物理显示配置项
        int activeConfig = SurfaceControl.getActiveConfig(displayToken);
        ......
        // 根据id从mDevices数组中获取对应的LocalDisplayDevice
        LocalDisplayDevice device = mDevices.get(physicalDisplayId);
        if (device == null) {
            // 创建LocalDisplayDevice
            final boolean isInternal = mDevices.size() == 0;
            device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
                    configs, activeConfig, configSpecs, colorModes, activeColorMode,
                    hdrCapabilities, isDefaultDisplay);
            // 添加到mDevices中
            mDevices.put(physicalDisplayId, device);
            // 通知DMS添加DisplayDevice事件
            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
        } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
                    allowedConfigs, colorModes, activeColorMode)) {     // 更新DisplayDevice
            // 通知DMS更新DisplayDevice事件
            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
        }
    } else {
    }
}

这里会从SurfaceControler中获取多个物理显示相关的配置属性。其中PhysicalDisplayInfo中封装了物理显示所有的属性配置,可通过如下命令查看其中的属性配置:

adb shell dumpsys display | grep mDisplayInfos

接下来,通过获取的id从mDevices数组中查找是否已存在对应的DisplayDevice对象,若不存在,将开始创建DisplayDevice对象,并在创建完成后,通知DMS创建对应的逻辑屏。

LocalDisplayDevice()如下:

// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java

        LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
                SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
                int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
                int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
                boolean isDefaultDisplay) {
            // 设置mDisplayAdapter、mDisplayToken、mUniqueId三个属性
            super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
            // 物理屏幕id
            mPhysicalDisplayId = physicalDisplayId;
            // 是否是默认屏
            mIsDefaultDisplay = isDefaultDisplay;
            mDisplayInfo = info;
            // 更新物理屏配置
            updateDisplayProperties(configs, activeConfigId, configSpecs, colorModes,
                    activeColorMode, hdrCapabilities);
            mSidekickInternal = LocalServices.getService(SidekickInternal.class);
            // 获取LightsManager对象
            if (mIsDefaultDisplay) {
                LightsManager lights = LocalServices.getService(LightsManager.class);
                mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
            } else {
                mBacklight = null;
            }
            ......
            // 创建亮度映射曲线
            BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
                    LocalDisplayDevice::loadDisplayConfigurationBrightnessMapping, this));
        }

这里会对LocalDisplayDevice中的属性进行初始化,之后执行updatePhysicalDisplayInfoLocked()方法更新物理屏配置,最后获取设置背光的对象mBacklight,可以看出原生机制中,即使存在多个物理屏,mBacklight只有一个。

在updateDisplayProperties()方法中,会更新物理屏配置、色彩模式、HDR模式,分别对应三个方法。

创建完成LocalDisplayDevice对象后,接下来执行sendDisplayDeviceEventLocked()方法,通知DMS新添加DisplayDevice事件。

3.通知DMS完成物理屏创建

DisplayAdapter中是通过回调的方式来通知DMS的,回调接口类是DisplayAdapter.Listener,DMS中对其进行了实现,并在实例化LocalDisplayAdapter时作为参数传入。

下面接着看sendDisplayDeviceEventLocked()方法:

// frameworks/base/services/core/java/com/android/server/display/DisplayAdapter.java

    protected final void sendDisplayDeviceEventLocked(
            final DisplayDevice device, final int event) {
        // 执行DisplayAdapter.Listener#onDisplayDeviceEvent()方法
        mHandler.post(() -> mListener.onDisplayDeviceEvent(device, event));
    }

这个mListener对象,正是DMS.DisplayAdapterListener类的对象:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

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);
        }
    }
}

因此,LocalDisplayAdapter发起回调后,执行handleDisplayDeviceAdded()方法,其中直接调用handleDisplayDeviceAddedLocked()方法:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

    private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
        // 获取DisplayDeviceInfo对象
        DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        ......
        // 添加到mDisplayDevices列表中
        mDisplayDevices.add(device);
        // 创建逻辑屏对象
        LogicalDisplay display = addLogicalDisplayLocked(device);
        // 更新display状态
        Runnable work = updateDisplayStateLocked(device);
        if (work != null) {
            work.run();
        }
        // 向WMS发起窗口更新
        scheduleTraversalLocked(false);
    }

在这个方法中,首先会创建一个DisplayDeviceInfo对象,然后创建对应的逻辑屏对象,最后更新Display的状态信息,并向WMS发起窗口更新请求。

5.创建逻辑屏对象

DMS模块中通过addLogicalDisplayLocked()方法为物理屏创建对应的逻辑屏对象:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
    private LogicalDisplay addLogicalDisplayLocked(DisplayDevice device) {
        DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
        // 是否是默认Display
        boolean isDefault = (deviceInfo.flags
                & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
        ......
        // 分配逻辑屏id
        final int displayId = assignDisplayIdLocked(isDefault, deviceInfo.address);
        // 分配layer stack id
        final int layerStack = assignLayerStackLocked(displayId);
        // 创建LogicalDisplay对象
        LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
        // 根据和物理Display对应的DisplayDeviceInfo更新和LogicalDisplay对应的DisplayInfo
        display.updateLocked(mDisplayDevices);
        ......
        configureColorModeLocked(display, device);
        // 添加到mLogicalDisplays列表中
        mLogicalDisplays.put(displayId, display);
        
        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
        return display;
    }

该方法中:

  • 首先,在创建逻辑显示对象前,会为即将创建的逻辑屏幕分配id和Layer Stack id;
  • 然后,开始创建LogicalDisplay对象;
  • 接下来,执行LogicalDisplay#updateLocked()方法,这个方法中,更新代表逻辑屏信息的DisplayInfo对象;
  • 最后,执行sendDisplayEventLocked()方法,将逻辑屏添加事件通知给DMS模块之外的组件。

6. 更新Display状态

回到handleDisplayDeviceAddedLocked()中,LogicalDisplay创建相关流程执行后,接下开始更新DisplayDevice的状态:

private Runnable updateDisplayStateLocked(DisplayDevice device) {
    // 获取DisplayDeviceInfo
    DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
    // 如果不带有FLAG_NEVER_BLANK标记,会请求更新DisplayDevice状态和亮度
    if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
        return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness);
    }
    return null;
}

这个方法是DMS中更新Display状态和亮度的关键方法,对该方法的分析将在下一篇文章中进行。

整个过程时序图如下:

create_localdisplayadapter.jpg