前言
在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方法。
五、总结
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |