在处理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 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变量中。
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是管理窗口的服务,它必然要根据显示设备的大小,来确定窗口的大小,我们先看看这个过程。
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);
}
//.....
}
//DisplayManager.java
public 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中获取。
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;
}
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中。
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方法中设置。
public void windowManagerAndInputReady() {
synchronized (mSyncRoot) {
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
scheduleTraversalLocked(false);
}
}
systemserver会在初始化了wms和ims后通知dms,然后dms通过该方法获取这两个服务
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) {
}
}
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 ,这个是上层窗口的大小。
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 是屏幕实际物理大小。
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);
}
}
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.java
boolean 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
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。