第二节:wm命令的实现方式分析
wm命令是在Wm.java文件中实现的。我们来分析一下执行的过程:
通过上述的可以看出是在adb shell中调用wm命令,可以知道wm是一个可执行文件,那么对于java类来说入口方法时main()方法。所以来看Wm类的main()方法如下:
public static void main(String[] args) {
(new Wm()).run(args);
}
使用Wm类的默认构造方法创建了一个Wm的实例,然后调用run()方法继续执行,将命令行传入的参数args一并传入。
由于run()方法是从Wm的父类BaseCommand继承来的,代码如下。
下面来看run()方法的内容:
public void run(String[] args) {
……
onRun();
…….
}
经过一些对参数的验证,最后调用Wm实现的onRun()方法,代码如下:
public void onRun() throws Exception {
mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
Context.WINDOW_SERVICE));
…….
String op = nextArgRequired();
if (op.equals("size")) {
runDisplaySize();
} else if (op.equals("density")) {
runDisplayDensity();
} else if (op.equals("overscan")) {
runDisplayOverscan();
} else {
…….
}
}
从前面章节对wm命令的介绍,都是对屏幕的进行操作的,必然涉及到WmS的操作,所以这里要获得WmS的代理对象,赋值给mWm变量,后续的我们要调用WmS的方法,都是通过mWm来调用的。
接下来通过nextArgRequired()获得命令字符串,然后根据命令字符串,执行不同的命令。
注意:这里使用了多个if语句,其实在JDK7开始,switch的条件语句已经可以使用字符串了。
根据命令的不同分别调用runDisplaySize()、runDisplayDensity()、runDisplayOverscan()方法,见名知义,所以这里不赘述。我们以runDisplayOverscan()方法为例,进行分析,其他的类似,最后都是通过mWm调用WmS的某个方法,对DisplayContent进行设置。
下面我们上该方法的代码:
private void runDisplayOverscan() throws Exception {
String overscanStr = nextArgRequired();
Rect rect = new Rect();
int density;
if ("reset".equals(overscanStr)) {
rect.set(0, 0, 0, 0);
} else {
final Pattern FLATTENED_PATTERN = Pattern.compile(
"(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
if (!matcher.matches()) {
System.err.println("Error: bad rectangle arg: " + overscanStr);
return;
}
rect.left = Integer.parseInt(matcher.group(1));
rect.top = Integer.parseInt(matcher.group(2));
rect.right = Integer.parseInt(matcher.group(3));
rect.bottom = Integer.parseInt(matcher.group(4));
}
try {
mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
} catch (RemoteException e) {
}
}
1. 获得命令的第一个参数,如果是resize,就意味着要恢复到原始的overscan的值,看到给rect矩形变量设置了四个0,这意味着设备原始是没有overscan值的。
2. 如果第一个参数不是resize,就意味着要对overscan进行设置,但是从wm overscan的参数模式可以看出是四个使用逗号(,)的整数值,可以是负整数值。这里使用正则表达式,将该参数转换成一个数组放到到matcher中,接着依次取出,分别赋值给rect的left,top,right,bottom的值。
3.当执行以上两步后,我们已经给rect矩形变量设置完值了,接下来就是告诉WmS,可以根据这些值,更新屏幕的overscan的值了。这里调用WmS的setOverscan()方法,把rect的四个坐标值传入。
注意:这里仅仅使用rect来存储left,top,right,bottom的值,并不代表overcan是一个矩形区域,其实overscan代表的是不绘制屏幕内容的区域。如下图中的浅橙色所示:
图2-1 屏幕的绘制参数示意图
4.接下来我们来看WmS中的setOverscan()的实现:
@Override
public void setOverscan(int displayId, int left, int top, int right, int bottom) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
setOverscanLocked(displayContent, left, top, right, bottom);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
1 首先来检查执行方法的类是否有足够的权限,这里必须有android.Manifest.permission.WRITE_SECURE_SETTINGS的权限才能调用该方法。
2 接着就是获得DisplayContent对象,这里我们传入的displayId就是Display.DEFAULT_DISPLAY,值为0,其实对于手机来说只有一块屏幕,所以就是DEFAULT_DISPLAY。当displayContent不为null的时候,调用WmS的setOverscanLocked()私有方法继续进行设置。
3 我们来看看setOverscanLocked()方法
private void setOverscanLocked(DisplayContent displayContent,
int left, int top, int right, int bottom) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
synchronized (displayContent.mDisplaySizeLock) {
displayInfo.overscanLeft = left;
displayInfo.overscanTop = top;
displayInfo.overscanRight = right;
displayInfo.overscanBottom = bottom;
}
mDisplaySettings.setOverscanLocked(displayInfo.name, left, top, right, bottom);
mDisplaySettings.writeSettingsLocked();
reconfigureDisplayLocked(displayContent);
}
该方法中做了三件事情:
l 调用displayContent的getDisplayInfo()方法获得DisplayInfo对象,然后根据传入的overscan的值,设置DisplayInfo中的响应的值。
l 调用mDisplaySettings的setOverscanLocked()方法,将overscan的值保存成一条名字为diaplayInfo.name的记录,通过这个名字,我们就可以从mDisplaySettings中读出该DisplayContent的overscan的内容。接着调用那个mDisplaySettings的writeSettingsLocked()方法,将刚才设置的内容写入到display_settings.xml设置文件中。
l 调用reconfigureDisplayLocked(displayContent)方法,进一步更新DisplayContent的内容,然后调用
performLayoutAndPlaceSurfacesLocked()方法对屏幕进行绘制。这个过程不再进行介绍。