在处理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 available display devices.     * The logical display might become invalid if it is attached to a display device     * that no longer exists.     *     * @param devices The list of all connected display devices.     */    public void updateLocked(List devices) {        // Bootstrap the logical display using its associated primary physical display.        // We might use more elaborate configurations later.  It's possible that the        // configuration of several physical displays might be used to determine the        // logical display that they are sharing.  (eg. Adjust size for pixel-perfect        // mirroring over HDMI.)        DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked();//两者不同,则更新mBaseDisplayInfo,这个其实就是上层的窗口信息        if (!Objects.equal(mPrimaryDisplayDeviceInfo, deviceInfo)) {            mBaseDisplayInfo.layerStack = mLayerStack;            mBaseDisplayInfo.flags = 0;            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {                mBaseDisplayInfo.flags |= Display.FLAG_SUPPORTS_PROTECTED_BUFFERS;            }            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {                mBaseDisplayInfo.flags |= Display.FLAG_SECURE;            }            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0) {                mBaseDisplayInfo.flags |= Display.FLAG_PRIVATE;            }            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {                mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;            }//更新mBaseDisplayInfo            mBaseDisplayInfo.type = deviceInfo.type;            mBaseDisplayInfo.address = deviceInfo.address;            mBaseDisplayInfo.name = deviceInfo.name;            mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;            mBaseDisplayInfo.appWidth = deviceInfo.width;            mBaseDisplayInfo.appHeight = deviceInfo.height;            mBaseDisplayInfo.logicalWidth = deviceInfo.width;            mBaseDisplayInfo.logicalHeight = deviceInfo.height;//注意:这个rotation并没有按照deviceInfo的rotation变量            mBaseDisplayInfo.rotation = Surface.ROTATION_0;            mBaseDisplayInfo.refreshRate = deviceInfo.refreshRate;            mBaseDisplayInfo.supportedRefreshRates = Arrays.copyOf(                    deviceInfo.supportedRefreshRates, deviceInfo.supportedRefreshRates.length);            mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;            mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;            mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;            mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;            mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;            mBaseDisplayInfo.state = deviceInfo.state;            mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;            mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;            mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;            mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;            mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;            mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName;//mPrimaryDisplayDeviceInfo其实就是显示设备的硬件信息            mPrimaryDisplayDeviceInfo = deviceInfo;            mInfo = null;        }    }   

该方法将更新LogicalDisplay中的mBaseDisplayInfo,这个mBaseDisplayInfo就是显示的初始信息

因此可以知道这几个变量的含义:

final class LogicalDisplay {//设备的原始显示信息,获取LocalDisplayDevice的DisplayDeviceInfo信息,然后构造该变量    private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();//从WMS设置的显示信息,这个和窗口大小息息相关。比如旋转了,则WMS设置新的info到该变量    private DisplayInfo mOverrideDisplayInfo; // set by the window manager//用于返回的缓存变量,根据情况拷贝mOverrideDisplayInfo或者mBaseDisplayInfo,返回给调用者    private DisplayInfo mInfo;//初始化时设置为传入的LocalDisplayDevice    private DisplayDevice mPrimaryDisplayDevice;//就是LocalDisplayDevice的DisplayDeviceInfo,即物理信息    private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;}

小结下addLogicalDisplayLocked方法的内容:获取显示设备的硬件信息即displaydeviceinfo,然后构造logicaldisplay,并根据displaydeviceinfo构造mBaseDisplayInfo的信息,然后将该displaydeviceinfo保存到mPrimaryDisplayDeviceInfo变量中。

二:startBootPhase

public void onBootPhase(int phase) {        if (phase == PHASE_WAIT_FOR_DEFAULT_DISPLAY) {            synchronized (mSyncRoot) {                long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;                while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {                    long delay = timeout - SystemClock.uptimeMillis();                    if (delay <= 0) {                        throw new RuntimeException("Timeout waiting for default display "                                + "to be initialized.");                    }                    if (DEBUG) {                        Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);                    }                    try {                        mSyncRoot.wait(delay);                    } catch (InterruptedException ex) {                    }                }            }        }    }

systemserver会一直等到将lcd注册到logicaldisplay中

三:WMS初始化

wms是管理窗口的服务,它必然要根据显示设备的大小,来确定窗口的大小,我们先看看这个过程。

public static WindowManagerService main(final Context context,            final InputManagerService im,            final boolean haveInputMethods, final boolean showBootMsgs,            final boolean onlyCore) {        final WindowManagerService[] holder = new WindowManagerService[1];//在display线程创建wms实例        DisplayThread.getHandler().runWithScissors(new Runnable() {            @Override            public void run() {                holder[0] = new WindowManagerService(context, im,                        haveInputMethods, showBootMsgs, onlyCore);            }        }, 0);        return holder[0];    }

进入wms构造方法:

private WindowManagerService(Context context, InputManagerService inputManager,            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {        mContext = context;        mHaveInputMethods = haveInputMethods;        mAllowBootMessages = showBootMsgs;        mOnlyCore = onlyCore;        mInputManager = inputManager; // Must be before createDisplayContentLocked.//获取内部的dms,即DMS中的LocalService,作为内部类可以直接调用DMS中的方法        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);        mDisplaySettings = new DisplaySettings();        mDisplaySettings.readSettingsLocked();        LocalServices.addService(WindowManagerPolicy.class, mPolicy);        mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));        mFxSession = new SurfaceSession();        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);//获取display,然后创建displaycontent,即display的封装类        Display[] displays = mDisplayManager.getDisplays();        for (Display display : displays) {            createDisplayContentLocked(display);        }//ActionsCode(wh, NEW_FEATURE : support deviece default rotation)        if(ActionsConfig.ACTIONS_FEATURE_SET_USER_DEFAULT_ROTATION_DEFAULT_ON >= 1){            mRotation = SystemProperties.getInt(SYSTEM_DEFAULT_ROTATION,0);        }//.....}

3.1 getDisplays

//DisplayManager.javapublic Display[] getDisplays() {        return getDisplays(null);    } public Display[] getDisplays(String category) {//通过DisplayMangerGlobal类的方法调用DMS中的相应方法,返回显示设备id列表        final int[] displayIds = mGlobal.getDisplayIds();        synchronized (mLock) {            try {//根据id列表,创建Display                if (category == null) {                    addAllDisplaysLocked(mTempDisplays, displayIds);                } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);                }//返回创建的displays                return mTempDisplays.toArray(new Display[mTempDisplays.size()]);            } finally {                mTempDisplays.clear();            }        }    }
//DisplayManager.java    private void addAllDisplaysLocked(ArrayList displays, int[] displayIds) {        for (int i = 0; i < displayIds.length; i++) {            Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);            if (display != null) {                displays.add(display);            }        }    }    private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {        Display display = mDisplays.get(displayId);        if (display == null) {            display = mGlobal.getCompatibleDisplay(displayId,                    mContext.getDisplayAdjustments(displayId));            if (display != null) {                mDisplays.put(displayId, display);            }        } else if (!assumeValid && !display.isValid()) {            display = null;        }        return display;    }//DisplayManagerGlobal.java    public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {        DisplayInfo displayInfo = getDisplayInfo(displayId);//根据信息构造一个Display        return new Display(this, displayId, displayInfo, daj);    }

以上就是Display的构造过程,即DisplayManager通过DisplayManagerGlobal向DMS获取DisplayInfo信息,然后将该DisplayInfo赋值给Display.mDisplayInfo,并将其放到mDisplay队列中。其中,DMS获取DisplayInfo,也是从LogicalDisplay中获取的,即LogicalDisplay.mInfo如果为空,则从mOverrideDisplayInfo中获取;若mOverrideDisplayInfo也为空,则从mBaseDisplayInfo中获取。

3.2 根据display数组,构造DisplayContent

    public void createDisplayContentLocked(final Display display) {        if (display == null) {            throw new IllegalArgumentException("getDisplayContent: display must not be null");        }        getDisplayContentLocked(display.getDisplayId());    }public DisplayContent getDisplayContentLocked(final int displayId) {        DisplayContent displayContent = mDisplayContents.get(displayId);        if (displayContent == null) {//从DisplayManager中取出创建的Display            final Display display = mDisplayManager.getDisplay(displayId);            if (display != null) {//创建DisplayContent                displayContent = newDisplayContentLocked(display);            }        }        return displayContent;    }private DisplayContent newDisplayContentLocked(final Display display) {//构造时会将DisplayContent内部的mDisplayInfo变量用logicaldisplay中的displayinfo填充        DisplayContent displayContent = new DisplayContent(display, this);        final int displayId = display.getDisplayId();//在WMS中将新建的DisplayContent放入队列,以后WMS会操作DisplayContent        mDisplayContents.put(displayId, displayContent);        DisplayInfo displayInfo = displayContent.getDisplayInfo();        final Rect rect = new Rect();        mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);        synchronized (displayContent.mDisplaySizeLock) {            displayInfo.overscanLeft = rect.left;            displayInfo.overscanTop = rect.top;            displayInfo.overscanRight = rect.right;            displayInfo.overscanBottom = rect.bottom;//向DMS设置新的displayinfo            mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(                    displayId, displayInfo);        }//        configureDisplayPolicyLocked(displayContent);        // TODO: Create an input channel for each display with touch capability.        if (displayId == Display.DEFAULT_DISPLAY) {            displayContent.mTapDetector = new StackTapPointerEventListener(this, displayContent);            registerPointerEventListener(displayContent.mTapDetector);        }        return displayContent;    }    

3.2.1进入setDisplayInfoOverrideFromWindowManager

    private void setDisplayInfoOverrideFromWindowManagerInternal(            int displayId, DisplayInfo info) {        synchronized (mSyncRoot) {//获取logicaldisplay            LogicalDisplay display = mLogicalDisplays.get(displayId);            if (display != null) {                if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {                    sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);                    scheduleTraversalLocked(false);                }            }        }    }//从代码看就是将info设置到mOverrideDisplayInfo中,这样下次获取信息就是从mOverrideDisplayInfo中获取了    public boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {        if (info != null) {            if (mOverrideDisplayInfo == null) {                mOverrideDisplayInfo = new DisplayInfo(info);                mInfo = null;                return true;            }            if (!mOverrideDisplayInfo.equals(info)) {                mOverrideDisplayInfo.copyFrom(info);                mInfo = null;                return true;            }        } else if (mOverrideDisplayInfo != null) {            mOverrideDisplayInfo = null;            mInfo = null;            return true;        }        return false;    }

小结上面的过程:传入构造的display构造displaycontent,其中mDisplayInfo直接从display.mDisplayInfo拷贝的,然后获取显示的一些设置,在设置到LogicalDisplay的mOverrideDisplayInfo中。

3.2.2configureDisplayPolicyLocked

    private void configureDisplayPolicyLocked(DisplayContent displayContent) {        mPolicy.setInitialDisplaySize(displayContent.getDisplay(),                displayContent.mBaseDisplayWidth,                displayContent.mBaseDisplayHeight,                displayContent.mBaseDisplayDensity);        DisplayInfo displayInfo = displayContent.getDisplayInfo();        mPolicy.setDisplayOverscan(displayContent.getDisplay(),                displayInfo.overscanLeft, displayInfo.overscanTop,                displayInfo.overscanRight, displayInfo.overscanBottom);    } public void setInitialDisplaySize(Display display, int width, int height, int density) {        // This method might be called before the policy has been fully initialized        // or for other displays we don't care about.//注意:此时mContext并未初始化,所以此时不会设置大小        if (mContext == null || display.getDisplayId() != Display.DEFAULT_DISPLAY) {            return;        }       //......    }    

WMS要获取显示设备的信息,所以新建了DisplayContent类,而这个类是通过Display类构造的,只是比较奇怪的是WMS明明可以直接访问DMS为啥要从DisplayManager中获取。

从流程可以知道,wms是通过DisplayContent来操作和获取显示信息的,因此wms在初始化时先从DisplayContent获取显示信息,然后在根据wms和phonewindowmanager的窗口策略来设置LogicalDisplay的信息,只是此时phonewindowmanager并未初始化,真正的设置将在wm.displayready方法中设置。

 

四:windowManagerAndInputReady

    public void windowManagerAndInputReady() {        synchronized (mSyncRoot) {            mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);            mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);            scheduleTraversalLocked(false);        }    }

systemserver会在初始化了wms和ims后通知dms,然后dms通过该方法获取这两个服务

 

 

五:wm.displayReady

 

public void displayReady() {        //ActionsCode(author:wh, BUGFIXED:BUG00238382 init all displays which surfaceflinger decteced before system ready)        //displayReady(Display.DEFAULT_DISPLAY);//1.遍历每个设备,运行displayReady方法                Display[] displays = mDisplayManager.getDisplays();        for (Display display : displays) {            displayReady(display.getDisplayId());        }        synchronized(mWindowMap) {            final DisplayContent displayContent = getDefaultDisplayContentLocked();            readForcedDisplaySizeAndDensityLocked(displayContent);            mDisplayReady = true;        }        try {            mActivityManager.updateConfiguration(null);        } catch (RemoteException e) {        }        synchronized(mWindowMap) {            mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(                    PackageManager.FEATURE_TOUCHSCREEN);            configureDisplayPolicyLocked(getDefaultDisplayContentLocked());        }        try {            mActivityManager.updateConfiguration(null);        } catch (RemoteException e) {        }    }

5.1遍历设备

private void displayReady(int displayId) {        synchronized(mWindowMap) {            final DisplayContent displayContent = getDisplayContentLocked(displayId);            if (displayContent != null) {                mAnimator.addDisplayLocked(displayId);                synchronized(displayContent.mDisplaySizeLock) {                    // Bootstrap the default logical display from the display manager.//获取displayCongtent中的displayinfo                    final DisplayInfo displayInfo = displayContent.getDisplayInfo();//从DMS中获取LogicalDisplay的DisplayInfo,这个是最新的配置                    DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);                    if (newDisplayInfo != null) {                        displayInfo.copyFrom(newDisplayInfo);                    }//配置displayContent中的mInitialDisplayWidth等变量,一般情况下就是实际的大小                    displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;                    displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;                    displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;                    displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;                    displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;                    displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;                    displayContent.mBaseDisplayRect.set(0, 0,                            displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);                }            }        }    }

遍历设备,配置displayContent的基本参数。从流程看先设置mInitialDisplayWidth 等变量,这个就是实际的显示设备物理大小;然后配置mBaseDisplayHeight ,这个是上层窗口的大小。

5.2 readForcedDisplaySizeAndDensityLocked

 private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {//从setting中读取wm size设置的屏幕大小        String sizeStr = Settings.Global.getString(mContext.getContentResolver(),                Settings.Global.DISPLAY_SIZE_FORCED);        if (sizeStr == null || sizeStr.length() == 0) {            sizeStr = SystemProperties.get(SIZE_OVERRIDE, null);        }        if (sizeStr != null && sizeStr.length() > 0) {            final int pos = sizeStr.indexOf(',');            if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {                int width, height;                try {                    width = Integer.parseInt(sizeStr.substring(0, pos));                    height = Integer.parseInt(sizeStr.substring(pos+1));                    synchronized(displayContent.mDisplaySizeLock) {//将设置的屏幕大小同步到displayContent中,从这里也可以看出,mBaseDisplayWidth是当前屏幕的大小                        if (displayContent.mBaseDisplayWidth != width                                || displayContent.mBaseDisplayHeight != height) {                            Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height);                            displayContent.mBaseDisplayWidth = width;                            displayContent.mBaseDisplayHeight = height;                        }                    }                } catch (NumberFormatException ex) {                }            }        }//将设置的density同步到displayContent中        String densityStr = Settings.Global.getString(mContext.getContentResolver(),                Settings.Global.DISPLAY_DENSITY_FORCED);        if (densityStr == null || densityStr.length() == 0) {            densityStr = SystemProperties.get(DENSITY_OVERRIDE, null);        }        if (densityStr != null && densityStr.length() > 0) {            int density;            try {                density = Integer.parseInt(densityStr);                synchronized(displayContent.mDisplaySizeLock) {                    if (displayContent.mBaseDisplayDensity != density) {                        Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density);                        displayContent.mBaseDisplayDensity = density;                    }                }            } catch (NumberFormatException ex) {            }        }    }   

此方法就是读取设置中的屏幕大小并同步到displayContent中,mBaseDisplayWidth是当前屏幕的大小,而mInitialDisplayWidth 是屏幕实际物理大小。

5.3 mActivityManager.updateConfiguration(null);

 public void updateConfiguration(Configuration values) {        synchronized(this) {            if (values == null && mWindowManager != null) {                // sentinel: fetch the current configuration from the window manager//1.计算config                values = mWindowManager.computeNewConfiguration();            }//2.窗口大小改变,需要更新oom值            if (mWindowManager != null) {                mProcessList.applyDisplaySize(mWindowManager);            }            final long origId = Binder.clearCallingIdentity();            if (values != null) {                Settings.System.clearConfiguration(values);            }//3.更新config            updateConfigurationLocked(values, null, false, false);            Binder.restoreCallingIdentity(origId);        }    }    

5.3.1 mWindowManager.computeNewConfiguration

public Configuration computeNewConfiguration() {        synchronized (mWindowMap) {            Configuration config = computeNewConfigurationLocked();            if (config == null && mWaitingForConfig) {                // Nothing changed but we are waiting for something... stop that!                mWaitingForConfig = false;                mLastFinishedFreezeSource = "new-config";                performLayoutAndPlaceSurfacesLocked();            }            return config;        }    }    Configuration computeNewConfigurationLocked() {        Configuration config = new Configuration();        config.fontScale = 0;        if (!computeScreenConfigurationLocked(config)) {            return null;        }        return config;    }

进入实际函数computeScreenConfigurationLocked

//WMS.javaboolean computeScreenConfigurationLocked(Configuration config) {//在displayReady方法的前部分以及设置为true        if (!mDisplayReady) {            return false;        }        // TODO(multidisplay): For now, apply Configuration to main screen only.        final DisplayContent displayContent = getDefaultDisplayContentLocked();        // Use the effective "visual" dimensions based on current rotation//根据mRotation的值,就是默认的UI方向,来设置displayContent的大小        final boolean rotated = (mRotation == Surface.ROTATION_90                || mRotation == Surface.ROTATION_270);        final int realdw = rotated ?                displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;        final int realdh = rotated ?                displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;        int dw = realdw;        int dh = realdh;//根据旋转后的宽高,来确定是横屏还是竖屏        if (config != null) {            config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :                    Configuration.ORIENTATION_LANDSCAPE;        }        // Update application display metrics.        final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);        final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);        final DisplayInfo displayInfo = displayContent.getDisplayInfo();        synchronized(displayContent.mDisplaySizeLock) {            displayInfo.rotation = mRotation;            displayInfo.logicalWidth = dw;            displayInfo.logicalHeight = dh;            displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;            displayInfo.appWidth = appWidth;            displayInfo.appHeight = appHeight;            displayInfo.getLogicalMetrics(mRealDisplayMetrics,                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);            displayInfo.getAppMetrics(mDisplayMetrics);//通过wms和phonewindowmanager确定了窗口大小后,在通过DMS设置到LogicalDisplay的DisplayInfo中            mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(                    displayContent.getDisplayId(), displayInfo);        }        final DisplayMetrics dm = mDisplayMetrics;        mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,                mCompatDisplayMetrics);        if (config != null) {            config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation)                    / dm.density);            config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)                    / dm.density);            computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, dm.density, config);            config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);            config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);            config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);            config.densityDpi = displayContent.mBaseDisplayDensity;            // Update the configuration based on available input devices, lid switch,            // and platform configuration.            config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;            config.keyboard = Configuration.KEYBOARD_NOKEYS;            config.navigation = Configuration.NAVIGATION_NONAV;            int keyboardPresence = 0;            int navigationPresence = 0;            final InputDevice[] devices = mInputManager.getInputDevices();            final int len = devices.length;            for (int i = 0; i < len; i++) {                InputDevice device = devices[i];                if (!device.isVirtual()) {                    final int sources = device.getSources();                    final int presenceFlag = device.isExternal() ?                            WindowManagerPolicy.PRESENCE_EXTERNAL :                                    WindowManagerPolicy.PRESENCE_INTERNAL;                    if (mIsTouchDevice) {                        if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==                                InputDevice.SOURCE_TOUCHSCREEN) {                            config.touchscreen = Configuration.TOUCHSCREEN_FINGER;                        }                    } else {                        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;                    }                    if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {                        config.navigation = Configuration.NAVIGATION_TRACKBALL;                        navigationPresence |= presenceFlag;                    } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD                            && config.navigation == Configuration.NAVIGATION_NONAV) {                        config.navigation = Configuration.NAVIGATION_DPAD;                        navigationPresence |= presenceFlag;                    }                    if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {                        config.keyboard = Configuration.KEYBOARD_QWERTY;                        keyboardPresence |= presenceFlag;                    }                }            }            if (config.navigation == Configuration.NAVIGATION_NONAV && mHasPermanentDpad) {                config.navigation = Configuration.NAVIGATION_DPAD;                navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;            }            // Determine whether a hard keyboard is available and enabled.            boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;            if (hardKeyboardAvailable != mHardKeyboardAvailable) {                mHardKeyboardAvailable = hardKeyboardAvailable;                mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);                mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);            }            if (mShowImeWithHardKeyboard) {                config.keyboard = Configuration.KEYBOARD_NOKEYS;            }            // Let the policy update hidden states.            config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;            config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;                        mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);        }              return true;    }    

根据WMS和phonewindowmanager的配置,设置displayinfo,并创建app的config。其实可以看出就是根据displaycontent中的mRotation和mBaseDisplayWidth等变量值确定真正的窗口大小,然后设置到LogicalDisplay的mOverriwterDisplayInfo中。所以可以知道,Android系统中窗口的大小其实是WMS和phonewindowmanager共同确定的。

5.3.2更新oom值

void applyDisplaySize(WindowManagerService wm) {        if (!mHaveDisplaySize) {            Point p = new Point();            wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);            if (p.x != 0 && p.y != 0) {                updateOomLevels(p.x, p.y, true);                mHaveDisplaySize = true;            }        }    }    private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {        // Scale buckets from avail memory: at 300MB we use the lowest values to        // 700MB or more for the top values.        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);        // Scale buckets from screen size.        int minSize = 480*800;  //  384000        int maxSize = 1280*800; // 1024000  230400 870400  .264        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);        if (false) {            Slog.i("XXXXXX", "scaleMem=" + scaleMem);            Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth                    + " dh=" + displayHeight);        }        float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;                //ActionsCode(authro:songzhining, comment: fix BUG00139602)        //if (mTotalMemMb <= 512) scale = scaleMem;                if (scale < 0) scale = 0;        else if (scale > 1) scale = 1;        int minfree_adj = Resources.getSystem().getInteger(                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);        int minfree_abs = Resources.getSystem().getInteger(                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);        if (false) {            Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);        }        final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;        for (int i=0; i= 0) {            for (int i=0; i= 0) {            reserve = reserve_abs;        }        if (reserve_adj != 0) {            reserve += reserve_adj;            if (reserve < 0) {                reserve = 0;            }        }        if (write) {            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));            buf.putInt(LMK_TARGET);            for (int i=0; i

 

5.3.3 updateConfigurationLocked

boolean updateConfigurationLocked(Configuration values,            ActivityRecord starting, boolean persistent, boolean initLocale) {        int changes = 0;        if (values != null) {            Configuration newConfig = new Configuration(mConfiguration);            changes = newConfig.updateFrom(values);            if (changes != 0) {                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {                    Slog.i(TAG, "Updating configuration to: " + values);                }                                EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);                if (values.locale != null && !initLocale) {                    saveLocaleLocked(values.locale,                                      !values.locale.equals(mConfiguration.locale),                                     values.userSetLocale);                }                mConfigurationSeq++;                if (mConfigurationSeq <= 0) {                    mConfigurationSeq = 1;                }                newConfig.seq = mConfigurationSeq;                mConfiguration = newConfig;                Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);                mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);                //mUsageStatsService.noteStartConfig(newConfig);                final Configuration configCopy = new Configuration(mConfiguration);                                // TODO: If our config changes, should we auto dismiss any currently                // showing dialogs?                mShowDialogs = shouldShowDialogs(newConfig);                AttributeCache ac = AttributeCache.instance();                if (ac != null) {                    ac.updateConfiguration(configCopy);                }                // Make sure all resources in our process are updated                // right now, so that anyone who is going to retrieve                // resource values after we return will be sure to get                // the new ones.  This is especially important during                // boot, where the first config change needs to guarantee                // all resources have that config before following boot                // code is executed.                mSystemThread.applyConfigurationToResources(configCopy);                if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {                    Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);                    msg.obj = new Configuration(configCopy);                    mHandler.sendMessage(msg);                }                for (int i=mLruProcesses.size()-1; i>=0; i--) {                    ProcessRecord app = mLruProcesses.get(i);                    try {                        if (app.thread != null) {                            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "                                    + app.processName + " new config " + mConfiguration);                            app.thread.scheduleConfigurationChanged(configCopy);                        }                    } catch (Exception e) {                    }                }                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY                        | Intent.FLAG_RECEIVER_REPLACE_PENDING                        | Intent.FLAG_RECEIVER_FOREGROUND);                broadcastIntentLocked(null, null, intent, null, null, 0, null, null,                        null, AppOpsManager.OP_NONE, false, false, MY_PID,                        Process.SYSTEM_UID, UserHandle.USER_ALL);                if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {                    intent = new Intent(Intent.ACTION_LOCALE_CHANGED);                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);                    broadcastIntentLocked(null, null, intent,                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,                            false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);                }            }        }        boolean kept = true;        final ActivityStack mainStack = mStackSupervisor.getFocusedStack();        // mainStack is null during startup.        if (mainStack != null) {            if (changes != 0 && starting == null) {                // If the configuration changed, and the caller is not already                // in the process of starting an activity, then find the top                // activity to check if its configuration needs to change.                starting = mainStack.topRunningActivityLocked(null);            }            if (starting != null) {                kept = mainStack.ensureActivityConfigurationLocked(starting, changes);                // And we need to make sure at this point that all other activities                // are made visible with the correct configuration.                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);            }        }        if (values != null && mWindowManager != null) {            mWindowManager.setNewConfiguration(mConfiguration);        }        return kept;    }    

 

从上面五个步骤可知,系统先初始化DMS,管理显示设备;然后初始化WMS,WMS通过DisplayContent来获取显示设备的大小;当WMS displayready时,WMS会配置displaycontent的大小到LogicalDisplay中,然后将计算的config传到AMS中,AMS利用这个config来start一个activity。

 

 

更多相关文章

  1. android 9.0 10.0 修改默认字体大小
  2. 使用java获取未来7天天气信息,可用于android
  3. Android - 手机开发调试无法输出logcat信息 - 未解决
  4. Android根据包名取得指定程序包的信息(名称、图标……)
  5. 查看Android进程内存资源信息
  6. Android的参数大致分成两块:系统服务参数和平台系统信息。

随机推荐

  1. 直击php反射学习之不用new方法实例化类操
  2. 分享PHP5.5在windows安装使用memcached服
  3. 解析php性能分析之php-fpm慢执行日志slow
  4. 示例PHP购物车类Cart.class.php定义与用
  5. 最简洁的PHP生成指定长度随机数的方法
  6. PHP如何使用Redis?(常见使用场景介绍)
  7. 谈谈PHP中的 ->、=> 和 :: 符号
  8. 详解在PHP模板引擎smarty生成随机数的方
  9. 基于PHP-FPM进程池的探索
  10. 示例php实现保存周期为1天的购物车类