android debug set screen rotation

好久没更新博客,最近debug了一个比较有趣的bug,有时间闲下来,整理一下还是挺不错的。

先来看看这个bug是什么情况:

android debug set screen rotation_第1张图片


以上两张图片是android的动态logo,也就是BootAnimation logo。上面显示出来的是同一张logo,左边是正常的,但在显示了一半的时候,就成了右边的样子。

正常的情况下,无论哪个版本的android,启动的时候,logo都是好好的,但是为什么会出现上面的情况呢?那是因为在系统里面做了一个旋屏的功能。旋屏功能是怎样完成的?看看下面的思路:

android debug set screen rotation_第2张图片


也就是在添加了这个旋转的功能,而且把屏幕设置成旋转90度显示的时候出现的。

其中,在上图的第二步里面,android原生就有“ro.sf.hwrotation”这个属性来设置开机后屏幕的方向。但这个属性是“ro”的,所以这里,摒弃了这个属性,而是重新申请一个“persist”类的属性来完成此功能,这里把这个属性命名为:persist.sys.hwrotation。

下面正式来分析问题,既然是在动态logo的显示阶段图片显示异常,那么,先来看看android的“BootAnimation”服务。

看到了“readyToRun”的部分代码:

status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

    sp dtoken(SurfaceComposerClient::getBuiltInDisplay(
            ISurfaceComposer::eDisplayIdMain));
    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
    if (status)
        return -1;

    // create the native surface
    sp control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565)


很明显,这里直接决定了屏幕显示的尺寸:首先是通过“getDisplayInfo”获取屏幕分辨率(我这里设置的是1920x1080),然后创建一个“Surface”。

猜想:因为是将屏幕设置了“90”度旋转后出现的问题。而且看第一张图片,显示的情况明显是“0”度的显示,也就是没经过“90”度的选择。会不会“BootAnimation”在开始显示的时候,获取到的是“0”度的屏幕参数(也就是分辨率是1920x1080),到后面显示到一半的时候,“Surfaceflinger”检测到了“persist.sys.hwrotation”属性的值为90,然后强制性的把显示分辨率改为“1080x1920”?所有才导致了logo显示异常??

 

要证明这个设想,很简单,添加打印信息。

首先,在“BootAnimation”的“getDisplayInfo”后面打印出获取到的分辨率:

#logcat | grep BootAnimation                                       
D/BootAnimation(2526): BootAnimation getDisplayInfo get dinfo.w=1920 dinfo.h=1080 !!!


看到,果然跟猜想的一样,“BootAnimation”获取到屏幕参数是“0”度的。然而“BootAnimation”获取屏幕参数是通过“getDisplayInfo”方法来获取的,也就是“Surfaceflinger”的API。看到“getDisplayInfo”方法里面判断屏幕参数的代码:

以下代码是添加进去的,所以贴出的是patch的部分代码:

+    int displayOrientation = DisplayState::eOrientationDefault;
+    char property[PROPERTY_VALUE_MAX];
+    if (type == DisplayDevice::DISPLAY_PRIMARY) {
+        if (property_get("persist.sys.hwrotation ", property, NULL) > 0) {
+            switch (atoi(property)) {
+                case 90:
+                    displayOrientation = DisplayState::eOrientation90;
+                    break;
+                case 180:
+                    displayOrientation = DisplayState::eOrientation180;
+                    break;
+                case 270:
+                    displayOrientation = DisplayState::eOrientation270;
+                    break;
+            }
+        }


好,这里是判断屏幕参数的唯一依据,如果屏幕参数变了,那么肯定要经过这里。也就是说,就是通过判断“persist.sys.hwrotation”,除非是“persist.sys.hwrotation”的值过程中有变化。

 

“persist.sys.hwrotation”真的有变化?如果“persist.sys.hwrotation”有变化,从上面的分析看来,也只能从“0”变为“90”。

判断的依据也很简单,打印一下就知道了。

因为在android启动的过程中,console服务起来,就可以进入用串口进入“command”行进行debug。所以,这里打印“persist.sys.hwrotation”,是在显示动态logo出现问题前后打印出来:

出问题前:

# getprop | grep persist.rw.hwrotation
 [persist.rw.hwrotation]: [0]

出问题后:

# getprop | grep persist.rw.hwrotation
 [persist.rw.hwrotation]: [90]

果然,通过获取“persist.sys.hwrotation”属性的值,我们恰恰看到了“persist.sys.hwrotation”是会从“0”变为“90”。奇了个怪,怎么会从“0”变为“90”呢??“persist”类的属性,应该是被设置了之后就会被保存起来才对。难道android property在启动的过程中会对“persist”类属性做什么特殊的处理??

 

好,看看property service,没看出有异样的地方。于是进入了焦灼状态,一时难以解释。

既然是这样,那就按照上面的方法,把android所有的属性都打印出来,做个对比。看其他属性会不会像“persist.sys.hwrotation”一样。其中,有一个属性“persist.sys.first_run”,是我在系统里面申请的另外一个“persist”类属性,是用来标识系统状态的。但也没见像“persist.sys.first_run”一样,启动过程中属性值会被改变。

 

在对比属性的过程中,也发现了一些挺有意思的属性,是系统用来做标识的:

[debug.force_rtl]: [0]
[dev.bootcomplete]: [1]
[init.svc.bootanim]: [stopped]
[init.svc.dhcpcd_eth0]: [running]
[ro.runtime.firstboot]: [1475205237873]
[service.bootanim.exit]: [1]
[sys.boot_completed]: [1]
[sys.interactive]: [active]
[sys.sysctl.extra_free_kbytes]: [24300]

如上面的“sys.boot_completed”,android系统启动完成之后,会发出广播,然后把“sys.boot_completed”置1。


回到问题中来,到底“persist.sys.hwrotation”为何会被改变??这个属性是我申请的,在android里面,除了“property service”之外,应该没有什么地方能改变到它。然后想到,我在android里面还启动了一个setrotation_prop服务,见上面蓝色的流程图。服务里面会去检查文件里面保存的屏幕旋转方向的值,然后去设置“persist.sys.hwrotation”属性对应的值。证明方法也很简单,把前后的结果打印出来就知道了。

 

果然,就是这个服务,在启动的过程中把“persist.sys.hwrotation”的值改变了。但话又说回来,这个“setrotation_prop”是通过判断“/sdcard/rotaion”文件来设置“persist.sys.hwrotation”。在查看一下,在init.rc里面:

symlink /storage/emulated/legacy /sdcard

/sdcard目录是被link 到了“/storage/emulated/legacy”,详细情况,可以查看以下链接:

# See storageconfig details at http://source.android.com/tech/storage/

 

那么,在android启动的过程中,当“setrotation_prop”检查不到“/sdcard/rotaion”文件的时候,“setrotation_prop”就会使用默认的值“0”;当“/sdcard/rotaion”文件被加载上了,“setrotation_prop”检查到“/sdcard/rotaion”里面保存的“90”的时候,又从新把“persist.sys.hwrotation”设置为“90”,所以就导致了“getDisplayInfo”前后获取的屏幕参数不一样,从而导致了显示图片错位。

 

既然定位到问题了,那么就要修复。之前代码里还做了一个功能,就是在setting里面添加多一个菜单选项,来设置屏幕旋转:

android debug set screen rotation_第3张图片


这样看来,这个处理逻辑有点分散,而且“setrotation_prop”服务的方式可靠性不好。所以,直接在setting选项菜单里面,代码添加一句:

SystemProperties.set(“persist.sys.hwrotation”,”90”);

然后摈弃“/sdcard/rotaion”和“setrotation_prop”,逻辑就成了:

android debug set screen rotation_第4张图片

问题解决。



你可能感兴趣的:(技术)