• 作者:老汪软件技巧
  • 发表时间:2024-08-31 11:01
  • 浏览量:

CtsWindowManagerDeviceAnimations.testRightEdgeExtensionWorksDuringActivityTransition报错:

java.lang.AssertionError: No screenshot of the activity transition passed the assertions :: ColorCheckResult{isFailure=true, firstWrongPixel=Point(450, 1263), expectedColor=Color(1.0, 0.0, 0.0, 1.0, sRGB IEC61966-2.1), actualColor=Color(0.0, 0.0, 0.0, 0.0, sRGB IEC61966-2.1)},......

1 case逻辑分析

根据case的内容大概理解一下这个case的测试逻辑:

启动一个名为EdgeExtensionActivity的Activity,这个Activity重写了动画,当播放动画时,EdgeExtensionActivity在X轴方向上被缩放到50%,并且边缘像素延伸到屏幕右边。

因为这个播放动画的Activity是一半蓝,一半红的,我们预期前25%的像素列是蓝色的(来自Activity),然后剩下的75%的像素列是红色的(25%来自Activity,50%来自edge extenstion)。

然后在动画过程中截图,做个对比:

左边是我们的机器有问题,可以看到压缩后左半屏右边的红色边缘是没有延伸到右半屏的。

右边是pixel没问题,可以看到压缩后左半屏右边的红色像素延伸到了右半屏。

再看报错内容:

ColorCheckResult{isFailure=true, firstWrongPixel=Point(450, 1263), expectedColor=Color(1.0, 0.0, 0.0, 1.0, sRGB IEC61966-2.1), actualColor=Color(0.0, 0.0, 0.0, 0.0, sRGB IEC61966-2.1)}

预期在Point(450, 1263)位置的像素颜色应该是:

Color(1.0, 0.0, 0.0, 1.0, sRGB IEC61966-2.1),即红色。

但是实际上是:

Color(0.0, 0.0, 0.0, 0.0, sRGB IEC61966-2.1),即黑色。

所以case fail。

2 本地Demo模拟CTS case

这个问题刚拿到后没有什么头绪,而且没有搞懂这个动画是怎么实现的,先看下能否本地写一个Demo模拟一下case逻辑。

继续梳理case的逻辑,这个case的核心逻辑为,创建一个名为EdgeExtensionActivity的Activity,这个Activity是一半蓝色,一半红色:

"1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent">
   <View android:background="#0000ff" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1"/>
   <View android:background="#ff0000" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1"/>
LinearLayout>

就像这样:

然后这个Activity在onResume中调用overridePendingTransition重写了enter动画和exit动画:

   @Override
   protected void onResume() {
       super.onResume();
        mPendingEnterRes = R.anim.edge_extension_right;
        mPendingExitRes = R.anim.alpha_0;
       overridePendingTransition(mPendingEnterRes, mPendingExitRes);
   }

这里我们只关注enter动画,动画样式为自定义的R.anim.edge_extension_right:

这个动画样式我在aosp中没找到,反编译了CTS的CtsWindowManagerDeviceAnimations.apk后得到:

"1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
   <alpha android:interpolator="@android:interpolator/linear" android:duration="5000" android:fillBefore="true" android:fillAfter="true" android:startOffset="0" android:fromAlpha="1" android:toAlpha="1" android:fillEnabled="true"/>
   <scale android:interpolator="@android:interpolator/linear" android:duration="5000" android:startOffset="0" android:fromXScale="0.5" android:toXScale="0.5" android:fromYScale="1" android:toYScale="1"/>
   <extend android:interpolator="@android:interpolator/linear" android:duration="5000" android:startOffset="0" android:fromExtendLeft="0" android:fromExtendTop="0" android:fromExtendRight="100%" android:fromExtendBottom="0" android:toExtendLeft="0" android:toExtendTop="0" android:toExtendRight="100%" android:toExtendBottom="0"/>
set>

定义了三种动画:

1)、alpha:前后没有变化。

2)、scale:动画开始的时候X轴方向上缩放到0.5,并且这个缩放比例不变,一直保持到动画结束。

3)、extend,有8个特定的描述该类型动画的属性:

并且设置了fromExtendRight和toExtendRight都是100%,这个属性暂时还不清楚是如何作用。

因为pixel上是正常的,因此在Demo上做了一些修改后,看到:

进行对比,依稀对extend类型的动画有了一点理解,即将Activity的边缘像素延伸到屏幕的边缘。

逻辑分析仪__逻辑分析能力强的女人

再进行一下实验,将整个过程中将X和Y轴方向上都缩放0.5,然后fromExtendRight和fromExtendBottom设置为10%,toExtendRight和toExtendBottom设置为100%,看下是什么效果:

的确是把Activity的边缘像素延伸到屏幕的边缘的感觉。

再把这个Demo装到我们的机器,发现5s的动画过程中一直都是以下状态:

只看到了scale的动画效果,但是没有extend的动画效果。

这么一对比,看起来似乎是我们的机器,extend这个类型的动画压根就没有生效。

3 ExtendAnimation分析

在aosp中搜索fromExtendRight等关键字,看到使用的地方在ExtendAnimation:

在ExtendAnimation的构造方法中被解析,而ExtendAnimation创建的地方在AnimationUtils.createAnimationFromXml:

也很好理解,如果动画的标签是extend,那么创建ExtendAnimation类型的动画。

再看fromExtendRight和mToRightValue这些属性被解析出来后,都用在什么地方,搜索了一下代码,只有一处,在ExtendAnimation.initialize:

调用堆栈为:

用来初始化ExtendAnimation的两个Insets类型的成员变量mFromInsets和mToInsets。

而成员变量mFromInsets和mToInsets使用的地方主要是在ExtendAnimation.applyTransformation和ExtendAnimation.hasExtension:

applyTransformation这个方法很熟悉了,Animation的子类通过实现这个方法,来计算动画过程中特定时间点下的动画要应用的transformation。

看下调用堆栈为:

看到关键点在TransitionAnimationHelper.edgeExtendWindow:

这里看下edgeBounds和extensionRect的值,我的屏幕是720 * 1612,基于我们设置的extend动画参数:

   <extend android:interpolator="@android:interpolator/linear" android:duration="5000" android:startOffset="0" android:fromExtendLeft="0" android:fromExtendTop="0" android:fromExtendRight="10%" android:fromExtendBottom="0" android:toExtendLeft="0" android:toExtendTop="0" android:toExtendRight="100%" android:toExtendBottom="0"/>

得到edgeBounds=Rect(719, 0 - 720, 1612), extensionRect=Rect(0, 0 - 720, 1612)

再看TransitionAnimationHelper.createExtensionSurface方法:

大致可以理解一下,将整个屏幕截图,然后截取edgeBounds对应的区域,然后从起始位置开始,一直拓展到extensionRect区域。

回到我们的问题,既然ExtendAnimation没有生效,那么大概率是这里的edgeBuffer返回了null,后续继续在SurfaceFlinger.captureLayers打印log,果然,在SurfaceFlinger的这里返回了:

发现截图的进程对应的App没有声明该权限,android.permission.CAPTURE_BLACKOUT_CONTENT,所以截图失败。

看了下我们的SystemUI,是没有声明这个权限的,反编译pixel的SystemUIGoogle,发现:

pixel的SystemUI(据SystemUI的同事说pixel的SystemUI不是aosp的那个SystemUI)是添加了这个权限的,所以pixel的SystemUI可以截图,并且后续正常运行ExtendAnimation。

另外一提aosp的SystemUI,即“ /frameworks/base/packages/SystemUI/AndroidManifest.xml”也是没有声明这个权限的,这不是google挖坑吗?只给自己pixel的SystemUI添加这个权限?

最后再用winscope看下画红色的是什么玩意:

“Right Edge Extension”下的一个名为“bbq-wrapper”的Layer。