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

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



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

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



也就是在添加了这个旋转的功能,而且把屏幕设置成旋转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里面添加多一个菜单选项,来设置屏幕旋转:


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

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

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



问题解决。



更多相关文章

  1. android自定义ListView背景
  2. Android样式开发之selector
  3. 说说Android(安卓)两种为自定义组件添加属性的使用方法和区别
  4. android多屏设计、适配(来自官网)
  5. 转: Android(安卓)ListView 滑动背景为黑色的解决办法
  6. android eclipse 根据屏幕密度自动生成不同分辨率的图片
  7. Android(安卓)激活Deviceadmin,并进行锁屏和恢复出厂设置
  8. Android连接、断开蓝牙扫描枪时屏幕刷新
  9. androidmanifest.xml的一些高级属性

随机推荐

  1. 监听mysql表内容变化 mysql开启binlog
  2. CentOS7离线安装MySQL的教程详解
  3. Mysql数据库监听binlog的开启步骤
  4. centos7.2下安装mysql5.7数据库的命令详
  5. 如何将Excel文件导入MySQL数据库
  6. MySQL不停地自动重启的解决方法
  7. mysql索引最左原则实例代码
  8. 关于MySQL的sql_mode合理设置详解
  9. 关于对mysql语句进行监控的方法详解
  10. OEL7.6源码安装MYSQL5.7的教程