Android显示设备管理以及转屏流程
在处理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。
更多相关文章
- android 9.0 10.0 修改默认字体大小
- 使用java获取未来7天天气信息,可用于android
- Android - 手机开发调试无法输出logcat信息 - 未解决
- Android根据包名取得指定程序包的信息(名称、图标……)
- 查看Android进程内存资源信息
- Android的参数大致分成两块:系统服务参数和平台系统信息。