Android 12系统源码_屏幕设备(二)DisplayAdapter和DisplayDevice的创建

news2025/1/11 2:53:44

前言

在Android 12系统源码_屏幕设备(一)DisplayManagerService的启动这篇文章中我们具体分析了DisplayManagerService 的启动流程,本篇文章我们将在这个的基础上具体来分析下设备屏幕适配器的创建过程。

一、注册屏幕适配器

系统是在DisplayManagerService 的onStart方法中调用registerDefaultDisplayAdapters进行了默认屏幕适配器的注册,在systemReady方法中调用registerAdditionalDisplayAdapters进行额外屏幕适配器的注册。

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

public final class DisplayManagerService extends SystemService {

    private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;//注册物理屏幕适配器、虚拟屏幕适配器
    private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;//注册其他屏幕适配器
    
    @Override
    public void onStart() {
		...代码省略...
        // 在android.display线程中创建默认DisplayAdapter,并进行注册
        mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
		...代码省略...
    }
    
    public void systemReady(boolean safeMode, boolean onlyCore) {
 		...代码省略...  
        //注册除了物理屏幕适配器、虚拟屏幕适配器以外的其他屏幕适配器
        mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
		...代码省略...
    }
    
    private final class DisplayManagerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS:
                    //注册默认的屏幕适配器
                    registerDefaultDisplayAdapters();
                    break;
                case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
                    //注册额外的屏幕设备适配器
                    registerAdditionalDisplayAdapters();
                    break;
                ...代码省略...    
            }
        }
    }
    
    //注册默认的屏幕适配器
    private void registerDefaultDisplayAdapters() {
        synchronized (mSyncRoot) {
             //注册内置物理屏幕适配器
            registerDisplayAdapterLocked(new LocalDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayDeviceRepo));
            //注册虚拟屏幕适配器
            mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
                    mHandler, mDisplayDeviceRepo);
            if (mVirtualDisplayAdapter != null) {
                registerDisplayAdapterLocked(mVirtualDisplayAdapter);
            }
        }
    }
    
    //注册额外的屏幕适配器对象
    private void registerAdditionalDisplayAdapters() {
        synchronized (mSyncRoot) {
            if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
                registerOverlayDisplayAdapterLocked();//注册模拟辅助设备屏幕适配器
                registerWifiDisplayAdapterLocked();//注册WIFI屏幕适配器
            }
        }
    }

}

二、注册默认屏幕适配器

注册内置物理屏幕适配器和虚拟屏幕适配器调用的都是registerDisplayAdapterLocked方法。

public final class DisplayManagerService extends SystemService {
	//当前已经注册的屏幕适配器集合
    private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
    //虚拟屏幕适配器
    private VirtualDisplayAdapter mVirtualDisplayAdapter;
    
    //注册默认的屏幕适配器
    private void registerDefaultDisplayAdapters() {
        synchronized (mSyncRoot) {
             //注册内置物理屏幕适配器
            registerDisplayAdapterLocked(new LocalDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayDeviceRepo));
            //注册虚拟屏幕适配器
            mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
                    mHandler, mDisplayDeviceRepo);
            if (mVirtualDisplayAdapter != null) {
                registerDisplayAdapterLocked(mVirtualDisplayAdapter);
            }
        }
    }
    
    private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
        mDisplayAdapters.add(adapter);//将适配器对象添加到mDisplayAdapters集合中
        adapter.registerLocked();//进行适配器注册操作
    }
    
 }

此方法先是创建内置物理屏幕适配器LocalDisplayAdapter对象实例,然后获取虚拟屏幕适配器VirtualDisplayAdapter对象实例。进一步调用registerDisplayAdapterLocked方法将其添加到屏幕适配器集合mDisplayAdapters中,然后还有调用每个适配器的registerLocked方法。

2.1 内置物理屏幕适配器

注册内置物理屏幕适配器先是创建LocalDisplayAdapter对象,然后调用该对象的registerLocked方法。

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

final class LocalDisplayAdapter extends DisplayAdapter {

    public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener) {
        this(syncRoot, context, handler, listener, new Injector());
    }

    @VisibleForTesting
    LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener, Injector injector) {
        super(syncRoot, context, handler, listener, TAG);//父类构造方法
        mInjector = injector;
        mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
    }
    
    @Override
    public void registerLocked() {
        super.registerLocked();
        mInjector.setDisplayEventListenerLocked(getHandler().getLooper(),
                new LocalDisplayEventListener());
        // 从SurfaceControl中获取物理屏幕id
        for (long physicalDisplayId : mSurfaceControlProxy.getPhysicalDisplayIds()) {
            //尝试连接物理屏幕
            tryConnectDisplayLocked(physicalDisplayId);
        }
    }
    
    private void tryConnectDisplayLocked(long physicalDisplayId) {
        // 根据id获取当前物理屏幕的令牌
        final IBinder displayToken = mSurfaceControlProxy.getPhysicalDisplayToken(physicalDisplayId);
        if (displayToken != null) {
            // 根据token获取当前物理屏幕的配置项
            SurfaceControl.StaticDisplayInfo staticInfo =  mSurfaceControlProxy.getStaticDisplayInfo(displayToken);
            if (staticInfo == null) {
                Slog.w(TAG, "No valid static info found for display device " + physicalDisplayId);
                return;
            }
            SurfaceControl.DynamicDisplayInfo dynamicInfo = mSurfaceControlProxy.getDynamicDisplayInfo(displayToken);
            if (dynamicInfo == null) {
                Slog.w(TAG, "No valid dynamic info found for display device " + physicalDisplayId);
                return;
            }
            if (dynamicInfo.supportedDisplayModes == null) {
                // There are no valid modes for this device, so we can't use it
                Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
                return;
            }
            if (dynamicInfo.activeDisplayModeId < 0) {
                // There is no active mode, and for now we don't have the
                // policy to set one.
                Slog.w(TAG, "No valid active mode found for display device " + physicalDisplayId);
                return;
            }
            if (dynamicInfo.activeColorMode < 0) {
                // We failed to get the active color mode. We don't bail out here since on the next
                // configuration pass we'll go ahead and set it to whatever it was set to last (or
                // COLOR_MODE_NATIVE if this is the first configuration).
                Slog.w(TAG, "No valid active color mode for display device " + physicalDisplayId);
                dynamicInfo.activeColorMode = Display.COLOR_MODE_INVALID;
            }
            SurfaceControl.DesiredDisplayModeSpecs modeSpecs =
                    mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken);
            // 根据id从mDevices数组中获取对应的LocalDisplayDevice
            LocalDisplayDevice device = mDevices.get(physicalDisplayId);
            if (device == null) {
                //是否是默认屏幕
                final boolean isDefaultDisplay = mDevices.size() == 0;
                // 创建LocalDisplayDevice
                device = new LocalDisplayDevice(displayToken, physicalDisplayId, staticInfo,
                        dynamicInfo, modeSpecs, isDefaultDisplay);
                mDevices.put(physicalDisplayId, device);
                //通知DMS更新DisplayDevice事件,新增屏幕设备
                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
            } else if (device.updateDisplayPropertiesLocked(staticInfo, dynamicInfo,
                    modeSpecs)) {
                //通知DMS更新DisplayDevice事件,屏幕设备属性发生变化
                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.
        }
    }
}

2.1.1 构造方法

LocalDisplayAdapter的构造方法很简单,主要属性的赋值都在其父类DisplayAdapter中。

final class LocalDisplayAdapter extends DisplayAdapter {

    public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener) {
        this(syncRoot, context, handler, listener, new Injector());
    }

    @VisibleForTesting
    LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener, Injector injector) {
        super(syncRoot, context, handler, listener, TAG);//父类构造方法
        mInjector = injector;
        mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
    }
}

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

abstract class DisplayAdapter {
    private final DisplayManagerService.SyncRoot mSyncRoot;
    private final Context mContext;
    private final Handler mHandler;
    private final Listener mListener;
    private final String mName;

    // Called with SyncRoot lock held.
    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;
    }
    
    public void registerLocked() {
    
    }
}

2.1.2 创建物理屏对象

创建好物理屏幕适配器对象后,会执行该对象的registerLocked()方法。

final class LocalDisplayAdapter extends DisplayAdapter {
    
    @Override
    public void registerLocked() {
        super.registerLocked();
        mInjector.setDisplayEventListenerLocked(getHandler().getLooper(), new LocalDisplayEventListener());
        // 从SurfaceControl中获取物理屏幕id
        for (long physicalDisplayId : mSurfaceControlProxy.getPhysicalDisplayIds()) {
            //尝试连接物理屏幕
            tryConnectDisplayLocked(physicalDisplayId);
        }
    }
}

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

final class LocalDisplayAdapter extends DisplayAdapter {
    
    private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();

    private void tryConnectDisplayLocked(long physicalDisplayId) {
        // 根据id获取当前物理屏幕的令牌
        final IBinder displayToken = mSurfaceControlProxy.getPhysicalDisplayToken(physicalDisplayId);
        if (displayToken != null) {
            // 根据token获取当前物理屏幕的配置项
            SurfaceControl.StaticDisplayInfo staticInfo =  mSurfaceControlProxy.getStaticDisplayInfo(displayToken);
            if (staticInfo == null) {
                Slog.w(TAG, "No valid static info found for display device " + physicalDisplayId);
                return;
            }
            SurfaceControl.DynamicDisplayInfo dynamicInfo = mSurfaceControlProxy.getDynamicDisplayInfo(displayToken);
            if (dynamicInfo == null) {
                Slog.w(TAG, "No valid dynamic info found for display device " + physicalDisplayId);
                return;
            }
            if (dynamicInfo.supportedDisplayModes == null) {
                // There are no valid modes for this device, so we can't use it
                Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
                return;
            }
            if (dynamicInfo.activeDisplayModeId < 0) {
                // There is no active mode, and for now we don't have the
                // policy to set one.
                Slog.w(TAG, "No valid active mode found for display device " + physicalDisplayId);
                return;
            }
            if (dynamicInfo.activeColorMode < 0) {
                // We failed to get the active color mode. We don't bail out here since on the next
                // configuration pass we'll go ahead and set it to whatever it was set to last (or
                // COLOR_MODE_NATIVE if this is the first configuration).
                Slog.w(TAG, "No valid active color mode for display device " + physicalDisplayId);
                dynamicInfo.activeColorMode = Display.COLOR_MODE_INVALID;
            }
            SurfaceControl.DesiredDisplayModeSpecs modeSpecs =
                    mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken);
            // 根据id从mDevices数组中获取对应的LocalDisplayDevice
            LocalDisplayDevice device = mDevices.get(physicalDisplayId);
            if (device == null) {
                //是否是默认屏幕
                final boolean isDefaultDisplay = mDevices.size() == 0;
                // 创建LocalDisplayDevice
                device = new LocalDisplayDevice(displayToken, physicalDisplayId, staticInfo,
                        dynamicInfo, modeSpecs, isDefaultDisplay);
                mDevices.put(physicalDisplayId, device);
                //通知DMS更新DisplayDevice事件,新增屏幕设备
                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
            } else if (device.updateDisplayPropertiesLocked(staticInfo, dynamicInfo,
                    modeSpecs)) {
                //通知DMS更新DisplayDevice事件,屏幕设备属性发生变化
                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.
        }
    }
}

这个方法先是从SurfaceControler中获取多个物理显示相关的配置属性,然后根据物理设备id从mDevices数组中获取对应的LocalDisplayDevice,如果不存在则会进行创建,物理屏幕设备对象LocalDisplayDevice是LocalDisplayAdapter的静态内部类。

final class LocalDisplayAdapter extends DisplayAdapter {

	//物理屏幕设备对象
    private final class LocalDisplayDevice extends DisplayDevice {
        private final long mPhysicalDisplayId;
        private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
        private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
        private final boolean mIsDefaultDisplay;//是否是默认屏幕
        private final BacklightAdapter mBacklightAdapter;//背光适配器

        private DisplayDeviceInfo mInfo;//屏幕设备信息
        private boolean mHavePendingChanges;
        private int mState = Display.STATE_UNKNOWN;
        // This is only set in the runnable returned from requestDisplayStateLocked.
        private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
        private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
        private int mDefaultModeId;
        private int mDefaultModeGroup;
        private int mActiveModeId;
        private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
                new DisplayModeDirector.DesiredDisplayModeSpecs();
        private boolean mDisplayModeSpecsInvalid;
        private int mActiveColorMode;
        private Display.HdrCapabilities mHdrCapabilities;
        private boolean mAllmSupported;
        private boolean mGameContentTypeSupported;
        private boolean mAllmRequested;
        private boolean mGameContentTypeRequested;
        private boolean mSidekickActive;
        private SidekickInternal mSidekickInternal;
        private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
        // The supported display modes according in SurfaceFlinger
        private SurfaceControl.DisplayMode[] mSfDisplayModes;
        // The active display mode in SurfaceFlinger
        private SurfaceControl.DisplayMode mActiveSfDisplayMode;
        private DisplayDeviceConfig mDisplayDeviceConfig;

        private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
                new DisplayEventReceiver.FrameRateOverride[0];

        LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
                SurfaceControl.StaticDisplayInfo staticDisplayInfo,
                SurfaceControl.DynamicDisplayInfo dynamicInfo,
                SurfaceControl.DesiredDisplayModeSpecs modeSpecs, boolean isDefaultDisplay) {
            // 设置mDisplayAdapter、mDisplayToken、mUniqueId三个属性
            super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId,
                    getContext());
            //物理设备屏幕id
            mPhysicalDisplayId = physicalDisplayId;
            // 是否是默认屏
            mIsDefaultDisplay = isDefaultDisplay;
            // 更新物理屏配置,物理屏配置、色彩模式、HDR模式
            updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs);
            mSidekickInternal = LocalServices.getService(SidekickInternal.class);
            //背光适配器
            mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay,
                    mSurfaceControlProxy);
            mDisplayDeviceConfig = null;
        }
	}
}

在创建物理屏幕设备对象或者更新物理屏幕设备对象属性后,都会调用sendDisplayDeviceEventLocked方法,通知DMS屏幕设备状态发生变化。

2.2、注册虚拟屏幕适配器

public class VirtualDisplayAdapter extends DisplayAdapter {

    public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener) {
        this(syncRoot, context, handler, listener,
                (String name, boolean secure) -> SurfaceControl.createDisplay(name, secure));
    }

    @VisibleForTesting
    VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener,
            SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
        super(syncRoot, context, handler, listener, TAG);
        mHandler = handler;
        mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
    }
    
}

VirtualDisplayAdapter的构造方法同样很简单,主要属性的赋值同样是依赖于其父类DisplayAdapter,值得一提的是该类没有重新父类的registerLocked方法,意味着registerLocked方法是空实现。

三、 通知DMS屏幕设备状态发生变化

前面注册内置物理屏幕适配器的最后有提到sendDisplayDeviceEventLocked方法,来看下该方法。

abstract class DisplayAdapter {

    private final Listener mListener;

    public interface Listener {
        void onDisplayDeviceEvent(DisplayDevice device, int event);
        void onTraversalRequested();
    }
    
    /**
     * 发送一个屏幕设备事件给屏幕设备适配器的监听者
     */
    protected final void sendDisplayDeviceEventLocked(final DisplayDevice device, final int event) {
        mHandler.post(() -> mListener.onDisplayDeviceEvent(device, event));
    }
}    

3.1 DisplayDeviceRepository阶段

DisplayDeviceRepository这个类实现了DisplayAdapter.Listener的回调方法。

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

class DisplayDeviceRepository implements DisplayAdapter.Listener {

    private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();
    
    @Override
    public void onDisplayDeviceEvent(DisplayDevice device, int event) {
        switch (event) {
            case DISPLAY_DEVICE_EVENT_ADDED://新增屏幕设备
                handleDisplayDeviceAdded(device);
                break;

            case DISPLAY_DEVICE_EVENT_CHANGED://屏幕设备属性发生变化
                handleDisplayDeviceChanged(device);
                break;

            case DISPLAY_DEVICE_EVENT_REMOVED://屏幕设备被移除
                handleDisplayDeviceRemoved(device);
                break;
        }
    }
    
    private void handleDisplayDeviceAdded(DisplayDevice device) {
        synchronized (mSyncRoot) {
            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);
            //调用sendEventLocked方法发送消息事件
            sendEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
        }
    }

    private void handleDisplayDeviceChanged(DisplayDevice device) {
        synchronized (mSyncRoot) {
            final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
            if (!mDisplayDevices.contains(device)) {
                Slog.w(TAG, "Attempted to change non-existent display device: " + info);
                return;
            }

            int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
            if (diff == DisplayDeviceInfo.DIFF_STATE) {
                Slog.i(TAG, "Display device changed state: \"" + info.name
                        + "\", " + Display.stateToString(info.state));
            } else if (diff != 0) {
                Slog.i(TAG, "Display device changed: " + info);
            }

            if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
                try {
                    mPersistentDataStore.setColorMode(device, info.colorMode);
                } finally {
                    mPersistentDataStore.saveIfNeeded();
                }
            }
            device.mDebugLastLoggedDeviceInfo = info;

            device.applyPendingDisplayDeviceInfoChangesLocked();
            //调用sendEventLocked方法发送消息事件
            sendEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
        }
    }

    private void handleDisplayDeviceRemoved(DisplayDevice device) {
        synchronized (mSyncRoot) {
            DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
            if (!mDisplayDevices.remove(device)) {
                Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
                return;
            }

            Slog.i(TAG, "Display device removed: " + info);
            device.mDebugLastLoggedDeviceInfo = info;
            //调用sendEventLocked方法发送消息事件
            sendEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
        }
    }


}

DisplayDeviceRepository的onDisplayDeviceEvent方法会根据收到的事件类型分别做处理:

  • 新增屏幕设备,调用handleDisplayDeviceAdded方法,
  • 屏幕设备属性发生变化,调用handleDisplayDeviceChanged方法
  • 屏幕设备被移除,调用handleDisplayDeviceRemoved方法。

以上三种场景最终都会进一步触发sendEventLocked方法。

class DisplayDeviceRepository implements DisplayAdapter.Listener {

    //屏幕设备状态发生变化事件监听者
    private final List<Listener> mListeners = new ArrayList<>();

    private void sendEventLocked(DisplayDevice device, int event) {
        final int size = mListeners.size();
        for (int i = 0; i < size; i++) {
        	//进一步触发回调对象的onDisplayDeviceEventLocked方法
            mListeners.get(i).onDisplayDeviceEventLocked(device, event);
        }
    }
    
    /**
     * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
     */
    public interface Listener {
        void onDisplayDeviceEventLocked(DisplayDevice device, int event);

        // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
        // a shoe-horned method for a shoe-horned feature.
        void onTraversalRequested();
    };
}    

LogicalDisplayMapper实现了DisplayDeviceRepository.Listener这个回调。

3.2 LogicalDisplayMapper阶段

LogicalDisplayMapper是系统逻辑屏幕设备对象的管理者。

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

class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    
    LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
            @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
            @NonNull Handler handler) {
        mSyncRoot = syncRoot;
        mPowerManager = context.getSystemService(PowerManager.class);
        mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
        mDisplayDeviceRepo = repo;
        mListener = listener;
        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
        mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
                com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
        mDeviceStateOnWhichToWakeUp = context.getResources().getInteger(
                com.android.internal.R.integer.config_deviceStateOnWhichToWakeUp);
        mDisplayDeviceRepo.addListener(this);
        mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
    }

    @Override
    public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
        switch (event) {
            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED://新增屏幕设备
                if (DEBUG) {
                    Slog.d(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
                }
                handleDisplayDeviceAddedLocked(device);//
                break;

            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED://屏幕设备属性发生变化
                if (DEBUG) {
                    Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
                }
                finishStateTransitionLocked(false /*force*/);
                updateLogicalDisplaysLocked();//更新逻辑屏幕设备的属性
                break;

            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED://移除屏幕设备
                if (DEBUG) {
                    Slog.d(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
                }
                updateLogicalDisplaysLocked();//更新逻辑屏幕设备的属性
                break;
        }
    }
}

LogicalDisplayMapper的onDisplayDeviceEventLocked方法也会根据收到的事件类型做分流处理:

  • 新增屏幕设备,调用handleDisplayDeviceAddedLocked方法,
  • 屏幕设备被移除或者屏幕设备属性发生变化,调用updateLogicalDisplaysLocked方法

3.2.1 新增逻辑屏

class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {

    private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
        // 获取DisplayDeviceInfo对象
        DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
        // Internal Displays need to have additional initialization.
        // This initializes a default dynamic display layout for INTERNAL
        // devices, which is used as a fallback in case no static layout definitions
        // exist or cannot be loaded.
        if (deviceInfo.type == Display.TYPE_INTERNAL) {
            initializeInternalDisplayDeviceLocked(device);
        }

        // 创建逻辑屏对象
        LogicalDisplay display = createNewLogicalDisplayLocked(
                device, Layout.assignDisplayIdLocked(false /*isDefault*/));

        applyLayoutLocked();
        updateLogicalDisplaysLocked();
    }
    //创建逻辑屏幕
    private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
        final int layerStack = assignLayerStackLocked(displayId);
        final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
        display.updateLocked(mDisplayDeviceRepo);
        mLogicalDisplays.put(displayId, display);
        setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
        return display;
    }
}

3.2.2 更新逻辑屏

class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    private void updateLogicalDisplaysLocked() {
        // Go through all the displays and figure out if they need to be updated.
        // Loops in reverse so that displays can be removed during the loop without affecting the
        // rest of the loop.
        for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
            final int displayId = mLogicalDisplays.keyAt(i);
            LogicalDisplay display = mLogicalDisplays.valueAt(i);

            mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
            display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);

            display.updateLocked(mDisplayDeviceRepo);
            final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
            final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
            final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;

            // The display is no longer valid and needs to be removed.
            if (!display.isValidLocked()) {
                mUpdatedLogicalDisplays.delete(displayId);

                // Remove from group
                final DisplayGroup displayGroup = getDisplayGroupLocked(
                        getDisplayGroupIdFromDisplayIdLocked(displayId));
                if (displayGroup != null) {
                    displayGroup.removeDisplayLocked(display);
                }

                if (wasPreviouslyUpdated) {
                    // The display isn't actually removed from our internal data structures until
                    // after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
                    Slog.i(TAG, "Removing display: " + displayId);
                    mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
                } else {
                    // This display never left this class, safe to remove without notification
                    mLogicalDisplays.removeAt(i);
                }
                continue;

            // The display is new.
            } else if (!wasPreviouslyUpdated) {
                Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
                assignDisplayGroupLocked(display);
                mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);

            // Underlying displays device has changed to a different one.
            } else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
                // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
                assignDisplayGroupLocked(display);
                mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);

            // Something about the display device has changed.
            } else if (!mTempDisplayInfo.equals(newDisplayInfo)) {
                // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
                assignDisplayGroupLocked(display);
                mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);

            // The display is involved in a display layout transition
            } else if (updateState == UPDATE_STATE_TRANSITION) {
                mLogicalDisplaysToUpdate.put(displayId,
                        LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);

            // Display frame rate overrides changed.
            } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
                mLogicalDisplaysToUpdate.put(
                        displayId, LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);

            // Non-override display values changed.
            } else {
                // While application shouldn't know nor care about the non-overridden info, we
                // still need to let WindowManager know so it can update its own internal state for
                // things like display cutouts.
                display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
                if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
                    mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
                }
            }

            mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
        }

        // Go through the groups and do the same thing. We do this after displays since group
        // information can change in the previous loop.
        // Loops in reverse so that groups can be removed during the loop without affecting the
        // rest of the loop.
        for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
            final int groupId = mDisplayGroups.keyAt(i);
            final DisplayGroup group = mDisplayGroups.valueAt(i);
            final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) > -1;
            final int changeCount = group.getChangeCountLocked();

            if (group.isEmptyLocked()) {
                mUpdatedDisplayGroups.delete(groupId);
                if (wasPreviouslyUpdated) {
                    mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_REMOVED);
                }
                continue;
            } else if (!wasPreviouslyUpdated) {
                mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_ADDED);
            } else if (mUpdatedDisplayGroups.get(groupId) != changeCount) {
                mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_CHANGED);
            }
            mUpdatedDisplayGroups.put(groupId, changeCount);
        }

        // Send the display and display group updates in order by message type. This is important
        // to ensure that addition and removal notifications happen in the right order.
        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
        sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
        sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
        sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED);

        mLogicalDisplaysToUpdate.clear();
        mDisplayGroupsToUpdate.clear();
    }
}

四、注册额外屏幕适配器

前面我们有提到,DMS有在systemReady方法中调用registerAdditionalDisplayAdapters方法来进行额外屏幕适配器的注册。

public final class DisplayManagerService extends SystemService {
    //当前已经注册的屏幕适配器集合
    private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
    //The Wifi display adapter, or null if not registered.
    private WifiDisplayAdapter mWifiDisplayAdapter;
    //注册额外的屏幕适配器对象
    private void registerAdditionalDisplayAdapters() {
        synchronized (mSyncRoot) {
            if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
                registerOverlayDisplayAdapterLocked();//注册模拟辅助设备屏幕适配器
                registerWifiDisplayAdapterLocked();//注册WIFI屏幕适配器
            }
        }
    }
    //注册模拟辅助屏幕设备被适配器
    private void registerOverlayDisplayAdapterLocked() {
        registerDisplayAdapterLocked(new OverlayDisplayAdapter(
                mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
    }
    
	//注册Wifi屏幕设备适配器
    private void registerWifiDisplayAdapterLocked() {
        if (mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableWifiDisplay)
                || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
            mWifiDisplayAdapter = new WifiDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
                    mPersistentDataStore);
            registerDisplayAdapterLocked(mWifiDisplayAdapter);
        }
    }
    
    private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
        mDisplayAdapters.add(adapter);//将适配器对象添加到mDisplayAdapters集合中
        adapter.registerLocked();//进行适配器注册操作
    }

}

4.1 模拟辅助设备适配器

注册模拟辅助设备适配器需要先创建OverlayDisplayAdapter对象,然后调用该对象的registerLocked方法。

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

final class OverlayDisplayAdapter extends DisplayAdapter {

    public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener, Handler uiHandler) {
        super(syncRoot, context, handler, listener, TAG);
        mUiHandler = uiHandler;
    }
    
    @Override
    public void registerLocked() {
        super.registerLocked();
        getHandler().post(new Runnable() {
            @Override
            public void run() {
            	//监听global数据库overlay_display_devices字段的变化
                getContext().getContentResolver().registerContentObserver(
                        Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
                        true, new ContentObserver(getHandler()) {
                            @Override
                            public void onChange(boolean selfChange) {
                                updateOverlayDisplayDevices();
                            }
                        });
                //更新当前模拟辅助设备
                updateOverlayDisplayDevices();
            }
        });
    }
  
}

OverlayDisplayAdapter的registerLocked方法仅仅是对global数据库overlay_display_devices字段的变化进行了监听,初次以及后续该字段变化的时候都会调用updateOverlayDisplayDevices方法。

final class OverlayDisplayAdapter extends DisplayAdapter {

    private final ArrayList<OverlayDisplayHandle> mOverlays = new ArrayList<OverlayDisplayHandle>();
            
    //更新模拟辅助屏幕设备
    private void updateOverlayDisplayDevices() {
        synchronized (getSyncRoot()) {
            updateOverlayDisplayDevicesLocked();
        }
    }
    
    private void updateOverlayDisplayDevicesLocked() {
        //获取当前overlay_display_devices的属性值
        String value = Settings.Global.getString(getContext().getContentResolver(),
                Settings.Global.OVERLAY_DISPLAY_DEVICES);
        //如果为空直接返回
        if (value == null) {
            value = "";
        }
        //如果没有发生变化直接返回
        if (value.equals(mCurrentOverlaySetting)) {
            return;
        }
        mCurrentOverlaySetting = value;
        //清理缓存
        if (!mOverlays.isEmpty()) {
            Slog.i(TAG, "Dismissing all overlay display devices.");
            for (OverlayDisplayHandle overlay : mOverlays) {
                overlay.dismissLocked();
            }
            mOverlays.clear();
        }
		//对overlay_display_devices字段的内容进行解析
        int count = 0;
        for (String part : value.split(DISPLAY_SPLITTER)) {
            Matcher displayMatcher = DISPLAY_PATTERN.matcher(part);
            if (displayMatcher.matches()) {
                if (count >= 4) {
                    Slog.w(TAG, "Too many overlay display devices specified: " + value);
                    break;
                }
                String modeString = displayMatcher.group(1);
                String flagString = displayMatcher.group(2);
                //将字符串转化为OverlayMode集合
                ArrayList<OverlayMode> modes = new ArrayList<>();
                for (String mode : modeString.split(MODE_SPLITTER)) {
                    Matcher modeMatcher = MODE_PATTERN.matcher(mode);
                    if (modeMatcher.matches()) {
                        try {
                            int width = Integer.parseInt(modeMatcher.group(1), 10);
                            int height = Integer.parseInt(modeMatcher.group(2), 10);
                            int densityDpi = Integer.parseInt(modeMatcher.group(3), 10);
                            if (width >= MIN_WIDTH && width <= MAX_WIDTH
                                    && height >= MIN_HEIGHT && height <= MAX_HEIGHT
                                    && densityDpi >= DisplayMetrics.DENSITY_LOW
                                    && densityDpi <= DisplayMetrics.DENSITY_XXXHIGH) {
                                modes.add(new OverlayMode(width, height, densityDpi));
                                continue;
                            } else {
                                Slog.w(TAG, "Ignoring out-of-range overlay display mode: " + mode);
                            }
                        } catch (NumberFormatException ex) {
                        }
                    } else if (mode.isEmpty()) {
                        continue;
                    }
                }
                //解析OverlayMode集合
                if (!modes.isEmpty()) {
                    int number = ++count;
                    String name = getContext().getResources().getString(
                            com.android.internal.R.string.display_manager_overlay_display_name,
                            number);
                    int gravity = chooseOverlayGravity(number);
                    OverlayFlags flags = OverlayFlags.parseFlags(flagString);
                    Slog.i(TAG, "Showing overlay display device #" + number
                            + ": name=" + name + ", modes=" + Arrays.toString(modes.toArray())
                            + ", flags=" + flags);
					//为其创建OverlayDisplayHandle对象,并将该对象添加到mOverlays集合中
                    mOverlays.add(new OverlayDisplayHandle(name, modes, gravity, flags, number));
                    continue;
                }
            }
            Slog.w(TAG, "Malformed overlay display devices setting: " + value);
        }
    }

    private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
        private static final int DEFAULT_MODE_INDEX = 0;

        private final String mName;
        private final List<OverlayMode> mModes;
        private final int mGravity;
        private final OverlayFlags mFlags;
        private final int mNumber;

        private OverlayDisplayWindow mWindow;
        private OverlayDisplayDevice mDevice;
        private int mActiveMode;

        OverlayDisplayHandle(
                String name,
                List<OverlayMode> modes,
                int gravity,
                OverlayFlags flags,
                int number) {
            mName = name;
            mModes = modes;
            mGravity = gravity;
            mFlags = flags;
            mNumber = number;

            mActiveMode = 0;

            showLocked();
        }

        private void showLocked() {
            mUiHandler.post(mShowRunnable);
        }

        public void dismissLocked() {
            mUiHandler.removeCallbacks(mShowRunnable);
            mUiHandler.post(mDismissRunnable);
        }

        private void onActiveModeChangedLocked(int index) {
            mUiHandler.removeCallbacks(mResizeRunnable);
            mActiveMode = index;
            if (mWindow != null) {
                mUiHandler.post(mResizeRunnable);
            }
        }

        // Called on the UI thread.
        @Override
        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
                long presentationDeadlineNanos, int state) {
            synchronized (getSyncRoot()) {
                IBinder displayToken = SurfaceControl.createDisplay(mName, mFlags.mSecure);
                mDevice = new OverlayDisplayDevice(displayToken, mName, mModes, mActiveMode,
                        DEFAULT_MODE_INDEX, refreshRate, presentationDeadlineNanos,
                        mFlags, state, surfaceTexture, mNumber) {
                    @Override
                    public void onModeChangedLocked(int index) {
                        onActiveModeChangedLocked(index);
                    }
                };

                sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
            }
        }

        // Called on the UI thread.
        @Override
        public void onWindowDestroyed() {
            synchronized (getSyncRoot()) {
                if (mDevice != null) {
                    mDevice.destroyLocked();
                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
                }
            }
        }

        // Called on the UI thread.
        @Override
        public void onStateChanged(int state) {
            synchronized (getSyncRoot()) {
                if (mDevice != null) {
                    mDevice.setStateLocked(state);
                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_CHANGED);
                }
            }
        }

        public void dumpLocked(PrintWriter pw) {
            pw.println("  " + mName + ":");
            pw.println("    mModes=" + Arrays.toString(mModes.toArray()));
            pw.println("    mActiveMode=" + mActiveMode);
            pw.println("    mGravity=" + mGravity);
            pw.println("    mFlags=" + mFlags);
            pw.println("    mNumber=" + mNumber);

            // Try to dump the window state.
            if (mWindow != null) {
                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
                ipw.increaseIndent();
                DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, "", 200);
            }
        }

        // Runs on the UI thread.
        private final Runnable mShowRunnable = new Runnable() {
            @Override
            public void run() {
                OverlayMode mode = mModes.get(mActiveMode);
                OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
                        mName, mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity,
                        mFlags.mSecure, OverlayDisplayHandle.this);
                window.show();

                synchronized (getSyncRoot()) {
                    mWindow = window;
                }
            }
        };

        // Runs on the UI thread.
        private final Runnable mDismissRunnable = new Runnable() {
            @Override
            public void run() {
                OverlayDisplayWindow window;
                synchronized (getSyncRoot()) {
                    window = mWindow;
                    mWindow = null;
                }

                if (window != null) {
                    window.dismiss();
                }
            }
        };

        // Runs on the UI thread.
        private final Runnable mResizeRunnable = new Runnable() {
            @Override
            public void run() {
                OverlayMode mode;
                OverlayDisplayWindow window;
                synchronized (getSyncRoot()) {
                    if (mWindow == null) {
                        return;
                    }
                    mode = mModes.get(mActiveMode);
                    window = mWindow;
                }
                window.resize(mode.mWidth, mode.mHeight, mode.mDensityDpi);
            }
        };
    }

    private static final class OverlayMode {
        final int mWidth;//宽度
        final int mHeight;//高度
        final int mDensityDpi;//像素密度

        OverlayMode(int width, int height, int densityDpi) {
            mWidth = width;
            mHeight = height;
            mDensityDpi = densityDpi;
        }
    }
}

4.2 WIIFI屏幕设备适配器

注册WIFI设备适配器需要先创建WifiDisplayAdapter对象,然后调用该对象的registerLocked方法。

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

final class WifiDisplayAdapter extends DisplayAdapter {

    private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
    private WifiDisplayController mDisplayController;

    public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener,
            PersistentDataStore persistentDataStore) {
        super(syncRoot, context, handler, listener, TAG);

        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
            throw new RuntimeException("WiFi display was requested, "
                    + "but there is no WiFi Direct feature");
        }

        mHandler = new WifiDisplayHandler(handler.getLooper());
        mPersistentDataStore = persistentDataStore;
        mSupportsProtectedBuffers = context.getResources().getBoolean(
                com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
    }
    
    @Override
    public void registerLocked() {
        super.registerLocked();
        updateRememberedDisplaysLocked();
        getHandler().post(new Runnable() {
            @Override
            public void run() {
            	//创建WIFI屏幕设备控制器
                mDisplayController = new WifiDisplayController(
                        getContext(), getHandler(), mWifiDisplayListener);
				//注册广播接受者
                getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                        new IntentFilter(ACTION_DISCONNECT), null, mHandler);
            }
        });
    }

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(ACTION_DISCONNECT)) {
                synchronized (getSyncRoot()) {
                	//请求断开连接
                    requestDisconnectLocked();
                }
            }
        }
    };
    
      public void requestDisconnectLocked() {
        getHandler().post(new Runnable() {
            @Override
            public void run() {
                if (mDisplayController != null) {
                	//请求断开连接
                    mDisplayController.requestDisconnect();
                }
            }
        });
    }
}

WifiDisplayAdapter的registerLocked方法先是创建WifiDisplayController对象,然后注册广播接受者,监听android.server.display.wfd.DISCONNECT广播事件,当收到该广播的时候执行requestDisconnectLocked方法,该方法最终是调用WifiDisplayController的requestDisconnect方法。

五、总结

方法调用时序图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2053560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

43.x86游戏实战-DXX寻找吸怪坐标

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

Xshell中弹出“ssh服务器拒绝了密码请再试一次”时,如何解决

在使用Xshell连接Ubuntu系统时&#xff0c;可能会弹出这个错误 可能原因如下​ 密码输入错误Ubantu系统默认禁止root用户登录ssh。 解决方法&#xff1a; 1. 先用root登录 (由于我买的是云服务器&#xff0c;所以拿这个来举例&#xff09; 注&#xff1a;要在本地shell中登录…

基于jqury和canvas画板技术五子棋游戏设计与实现(论文+源码)_kaic

摘 要 网络五子棋游戏如今面临着一些新的挑战和机遇。一方面&#xff0c;网络游戏需要考虑到网络延迟和带宽等因素&#xff0c;保证游戏的实时性和稳定性。另一方面&#xff0c;网络游戏需要考虑到游戏的可玩性和趣味性&#xff0c;以吸引更多的玩家参与。本文基于HTML5和Canv…

银河麒麟V10忘记Root密码怎么办?

银河麒麟V10忘记Root密码怎么办&#xff1f; 一&#xff1a;进入GRUB模式二&#xff1a;输入GRUB账号密码三&#xff1a;修改启动参数四&#xff1a;修改root密码五&#xff1a;重启系统六&#xff1a;验证root密码 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收…

就想刷题过?新手必看的华为认证题库最强背题经验技巧

华为认证作为网络和IT领域的重要资格认证&#xff0c;其难度不容小觑。许多考生为了顺利通过考试&#xff0c;选择背题库作为备考策略。 &#xff08;重点说一下啊&#xff0c;不提倡刷题&#xff0c;能学知识&#xff0c;把技术学透&#xff0c;肯定是最佳的。&#xff09; …

Java基于数据库、乐观锁、悲观锁、Redis、Zookeeper分布式锁的简单案例实现(保姆级教程)

1. 分布式锁的定义 分布式锁是一种在分布式系统中用来协调多个进程或线程对共享资源进行访问的机制。它确保在分布式环境下&#xff0c;多个节点&#xff08;如不同的服务器或进程&#xff09;不会同时访问同一个共享资源&#xff0c;从而避免数据不一致、资源竞争等问题。 2…

简单记录:两台服务器如何超快速互传文件/文件夹

在服务器间传输文件和文件夹是一个常见的任务&#xff0c;尤其是在需要同步数据或进行备份时。以下是使用 scp 命令在两台服务器之间进行文件传输的基本步骤。 服务器A 至 服务器B&#xff1a;文件传输指南 前提条件 确保服务器A和服务器B之间网络互通。确认您有权限访问目标…

如何让孩子喜欢上读书?

1.选择合适的书籍&#xff1a;根据孩子的兴趣和年龄选择合适的书籍&#xff0c;让孩子参与选书的过程&#xff0c;这样可以增加他们对阅读的主动性和兴趣。同时&#xff0c;避免过分强调阅读的功利性&#xff0c;让孩子自由选择他们感兴趣的书籍。   2.定期的阅读时间和活动&…

谷粒商城实战笔记-211~212-商城业务-认证服务-环境搭建

这一部分的主要内容是开发商城的认证服务。 文章目录 一&#xff0c;211-商城业务-认证服务-环境搭建1&#xff0c;创建模块2&#xff0c;引入相关依赖3&#xff0c;各种配置3.1 注册中心配置3.2 启用注册中心3.3 本节域名配置 4&#xff0c;页面模板4.1 html模板4.2 静态资源上…

python---数据可视化篇

目录 1.matplotlib简介 2.安装并且导入对应的模块 3.设置中文字体 4.创建画布 5.绘制折线图 6.对于折线图的美化 7.散点图的绘制 8.双y轴叠加图 9.簇形柱状图 10.百分比堆积柱状图 11.绘制多个子图&#xff08;一个画布上面&#xff09; 1.matplotlib简介 matplotl…

C盘扩容遇到恢复分区怎么办?

文章目录 1.0 问题描述2.0 了解恢复分区是啥3.0 恢复分区可以删除吗&#xff1f;&#xff08;需确认好&#xff01;&#xff09;4.0 删除恢复分区&#xff08;需要谨慎操作&#xff09;4.0.1 管理员打开CMD4.0.2 查看磁盘 给C盘扩容 1.0 问题描述 想要给C盘扩容&#xff0c;但…

Hyper-v ubuntu22 上外网方法

1. 前置步骤 步骤一&#xff0c;首先新建一个虚拟网络交换机&#xff0c;我这里名称为vEthernet (hyper-v-ubuntu)&#xff0c;选【内部网络】 步骤二&#xff0c; 在网络设置中&#xff0c;找到可以上网的网卡&#xff0c;这里我用的是无线网卡WLAN&#xff0c;设置共享连接…

【SpringBoot】SpringBoot的运行原理

SpringBoot项目中都有一个如下的启动类。 SpringBootApplication public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class,args);} }其中SpringBootApplication是这个启动类的核心注解&#xff0c;在它下面又有三个子…

Spring Cloud Gateway动态路由及路由插件实现方案

前言 sim-framework之前使用Zuul作为网关&#xff0c;结合Eureka实现了动态路由及灰度路由&#xff0c;但是存在以下几个问题&#xff1a; 性能问题&#xff1a;Zuul基于线程隔离&#xff0c;一个请求需要一个线程处理&#xff0c;而Gateway基于事件驱动&#xff0c;少量线程…

Go项目布局

Go项目布局&#xff0c;自举语言&#xff0c;源码是靠Go自己实现的 所以Go源码可以参考作为项目布局 源码放在src目录下 cmd放main internal目录下放不希望外部访问的代码&#xff08;业务&#xff09; common目录下可以放直接 import外部访问的 etc放配置文件yaml

第二届海南大数据创新应用大赛 - 算法赛道冠军比赛攻略_海南新境界队

关联比赛: 第二届海南大数据创新应用大赛 - 智能算法赛 第二届海南大数据创新应用大赛 - 算法赛道冠军比赛攻略 首先很幸运能拿到这次初赛冠军&#xff0c;本着积极学习和提升自我的态度&#xff0c;团队成员通力合作是获胜关键&#xff0c;再次感谢。 赛题背景分析和理解 …

gpio的使用----->4412的裸机的使用(第三节)

这一节主要是 4412 的裸机的使用 0 4412 的硬件原理图 数据手册 然后是数据手册的解析&#xff1a; 每一组都有这几个 寄存器。 需要注意&#xff1a; 1、 4412 的中断是 与输入&#xff0c;输出在同一个级别的&#xff0c;与stm32不同。 2、 我是在uboot 上进行编程的&#x…

重头开始嵌入式第二十二天(Linux系统编程 进程)

进程 目录 进程 1.进程的概念 2.PCB&#xff08;process control block&#xff09; 3.进程和程序有什么区别&#xff1f; 4.进程的内存分布 5.进程的分类 守护进程 6.进程的作用 7.进程的状态 8.进程的调度 9.查询进程的相关指令 1.ps aux 2.top 3.kill和killa…

12 Text 组件

12 Text 组件 Tkinter 是 Python 的标准 GUI 库&#xff0c;而 Text 组件是其中用于显示和编辑多行文本的控件。以下是对 Text 组件的详细说明和一个使用案例。 Text 组件属性 基本属性 width: 文本框的宽度&#xff0c;通常以字符数为单位。height: 文本框的高度&#xff…

亚世光电:消费电子年度表演

机圈风云再起&#xff0c;消费电子乘风而起&#xff1f; 今天我们来聊——亚世光电 最近&#xff0c;华为mate60突然降价&#xff0c;被大家怀疑是为新品上市做准备&#xff0c;算算时间&#xff0c;下半年的消费电子大战也即将拉开帷幕&#xff0c;而亚世光电所在的光电显示领…