赞
踩
在处理android双屏异显项目,发现异显副屏(HDMI)显示竖屏内容时是拉伸的,在解决问题的过程中跟了WMS和DisplayManagerService的流程,也接触了转屏的过程,在此记录下来。
先来看看系统流程:
- //SystemServer.java
- private void run() {
- // Display manager is needed to provide display metrics before package manager
- // 1.初始化DisplayManagerService
- mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
-
- // We need the default display before we can initialize the package manager.
- //2.保证lcd已经初始化
- mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
-
- //3.初始化WMS,并将其加入ServiceManager中
- wm = WindowManagerService.main(context, inputManager,
- mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
- !mFirstBoot, mOnlyCore);
- ServiceManager.addService(Context.WINDOW_SERVICE, wm);
- ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
-
- mActivityManagerService.setWindowManager(wm);
-
- inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
- inputManager.start();
-
- //4.在wms和ims初始化后,调用DMS的方法,设置DMS
- mDisplayManagerService.windowManagerAndInputReady();
-
- try {
- //5.调用WMS的方法,
- wm.displayReady();
- } catch (Throwable e) {
- reportWtf("making display ready", e);
- }
- }

关于displaymanagerservice的初始化可以参考前文 https://blog.csdn.net/ywlyg/article/details/79584916 的第一部分,主要作用就是向系统提供IDisplayManager.Stub的接口,我们接着链接文章继续讨论关于显示设备的部分。
- public void onStart() {
- mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
-
- publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
- true /*allowIsolated*/);
- publishLocalService(DisplayManagerInternal.class, new LocalService());
- }
-
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
- registerDefaultDisplayAdapter();
- break;
-
- }
进入函数
- private void registerDefaultDisplayAdapter() {
- // Register default display adapter.
- synchronized (mSyncRoot) {
- registerDisplayAdapterLocked(new LocalDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
- }
- }
LocalDisplayAdapter构造函数比较简单,就是传入些基本的变量,可以自己看代码。DMS中有很多类型的的DisplayAdapter:
LocalDisplayAdapter是针对本地已经存在的物理显示屏设备;WifiDisplayAdapter针对WiFi Display;VirtualDisplayAdapter 显示一个虚拟屏幕,在这里我们只关注物理显示设备。
进入注册函数:
- private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
- mDisplayAdapters.add(adapter);
- adapter.registerLocked();
- }
- public void registerLocked() {
- super.registerLocked();
- //创建对热插拔设备的处理
- mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
- //尝试连接物理设备,其中TO_SCAN为2,即build-in和HDMI
- for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
- tryConnectDisplayLocked(builtInDisplayId);
- }
- }
-
- private void tryConnectDisplayLocked(int builtInDisplayId) {
- //通过SurfaceControl向SurfaceFlinger获取displayToken
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
- //displayToken不为空,证明底层已经检测到该设备了
- if (displayToken != null) {
- //通过SurfaceControl向SurfaceFlinger获取物理设备的信息数组
- SurfaceControl.PhysicalDisplayInfo[] configs =
- SurfaceControl.getDisplayConfigs(displayToken);
- //获取数组的哪个index是当前的配置
- int activeConfig = SurfaceControl.getActiveConfig(displayToken);
- //从mDevices中获取LocalDisplayDevice,如果为空,就说明还没有构造
- LocalDisplayDevice device = mDevices.get(builtInDisplayId);
- if (device == null) {
- // Display was added.
- //底层有这个物理设备的,上层还是空,则构造一个物理设备在上层的抽象,就是LocalDisplayDevice
- device = new LocalDisplayDevice(displayToken, builtInDisplayId,
- configs, activeConfig);
- mDevices.put(builtInDisplayId, device);
- //发送设备添加广播
- sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
- // Display properties changed.
- 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.
- }
- }

LocalDisplayDevice的构造函数不复杂,最重要的就是将从底层获取的显示设备信息保存到mPhys变量中,接着看DISPLAY_DEVICE_EVENT_ADDED信息的处理,这个处理在DMS中:
- private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
- //将这个LocalDisplayDevice保存到数组中
- mDisplayDevices.add(device);
- //添加LogicalDisplay
- addLogicalDisplayLocked(device);
- Runnable work = updateDisplayStateLocked(device);
- if (work != null) {
- work.run();
- }
- scheduleTraversalLocked(false);
- }
- private void addLogicalDisplayLocked(DisplayDevice device) {
- //获取DisplayDeviceInfo信息,就是根据LocalDisplayDevice的mPhys参数构造DisplayDeviceInfo
- DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
- boolean isDefault = (deviceInfo.flags
- & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
- //根据是否主设备来分配显示id和layerstack
- final int displayId = assignDisplayIdLocked(isDefault);
- final int layerStack = assignLayerStackLocked(displayId);
- //构造LogicalDisplay
- LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
- //更新LogicalDisplay的信息
- display.updateLocked(mDisplayDevices);
-
- mLogicalDisplays.put(displayId, display);
-
- // Wake up waitForDefaultDisplay.
- if (isDefault) {
- mSyncRoot.notifyAll();
- }
- //发送消息
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
- }

具体来看每个方法:
1.getDisplayDeviceInfoLocked
- public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
- //构造mInfo,就是将mPhys的参数设置到mInfo中
- if (mInfo == null) {
- mInfo = new DisplayDeviceInfo();
- mInfo.width = mPhys.width;
- mInfo.height = mPhys.height;
- mInfo.refreshRate = mPhys.refreshRate;
- mInfo.supportedRefreshRates = mSupportedRefreshRates;
- mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
- mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
- mInfo.state = mState;
- mInfo.uniqueId = getUniqueId();
-
- if (mPhys.secure) {
- mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
- | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
- }
- //针对主副屏做不同处理,可以看出,副屏(HDMI)是没有dpi和density的
- if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
- mInfo.name = getContext().getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display_name);
- mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
- | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
- mInfo.type = Display.TYPE_BUILT_IN;
- mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
- mInfo.xDpi = mPhys.xDpi;
- mInfo.yDpi = mPhys.yDpi;
- mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
- } else {
- mInfo.type = Display.TYPE_HDMI;
- mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
- mInfo.name = getContext().getResources().getString(
- com.android.internal.R.string.display_manager_hdmi_display_name);
- mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
- mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
-
- // For demonstration purposes, allow rotation of the external display.
- // In the future we might allow the user to configure this directly.
- //注意,在HDMI中,如果有这个属性,则将内容旋转270度,这个属性会影响hdmi的显示方向
- if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
- mInfo.rotation = Surface.ROTATION_270;
-
- }
-
- // For demonstration purposes, allow rotation of the external display
- // to follow the built-in display.
- if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
- }
- }
- }
- return mInfo;
- }
-

其实这个方法就是将mPhys中的信息同步给mInfo,然后根据属性配置方向;若mInfo已经被配置了,则直接返回。
2.LogicalDisplay构造方法
- public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
- mDisplayId = displayId;
- mLayerStack = layerStack;
- mPrimaryDisplayDevice = primaryDisplayDevice;
- }
3.LogicalDisplay.updateLocked
- /**
- * Updates the state of the logical display based on the availab
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。