Android显示设备管理以及转屏流程

在处理android双屏异显项目,发现异显副屏(HDMI)显示竖屏内容时是拉伸的,在解决问题的过程中跟了WMS和DisplayManagerService的流程,也接触了转屏的过程,在此记录下来。先来看看系统流程://SystemServer.java private void run() { // Display manager is needed to prov...
摘要由CSDN通过智能技术生成

在处理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的初始化

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

1.1进入addLogicalDisplayLocked方法:

    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
  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值