Google CTS case对android non-public属性乱用的问题

最近看了一个Android CTS的问题,最后发现是Google CTS package里的一个case 乱用了一个 Android Non-public attrs而引起的,写下这篇文章记录一下。

转载请标明来处:http://www.jianshu.com/p/2d54c41c0dfa

一、环境

OS: Android 7.0
CTS version: android-cts-7.0_r1

重现步骤:

run cts -m CtsServicesHostTestCases -t android.server.cts.ActivityManagerPinnedStackTests#testAlwaysFocusablePipActivity
     --logcat-on-failure

观察到的现象:

Test failing with error 'junit.framework.AssertionFailedError: 
    Pinned stack must be the focused stack. expected:<4> but was:<0>'

二、testAlwaysFocusablePipActivity

在CTS code里找到 ActivityManagerPinnedStackTests.java 里的testAlwaysFocusablePipActivity 方法, 这个Case主要是执行adb shell命令,然后 dumpsys 一些信息来检查是否满足预期。 整理如下:

adb shell am start -n android.server.app/.AlwaysFocusablePipActivity
adb shell am stack move-top-activity-to-pinned-stack 1 0 0 500 500
adb shell dumpsys activity activities

测试步骤主要分为三步:

  • 启动 AlwaysFocusablePipActivity
  • 将 AlwaysFocusablePipActivity 从stack 1 移动到 PINNED stack (stack id 4).
  • dump activity的activities信息, 检查 mFocusedStack 是否 stack id为4.

最后的期望的 stack id 为4

mFocusedStack=ActivityStack{7d928bf stackId=4, 2 tasks}

其中 adb shell am stack move-top-activity-to-pinned-stack 1 0 0 500 500 这句命令,目的就是将 AlwaysFocusablePipActivity 从stack 1 移动到 PINNED stack (stack id 4), 然后将 PINNED Stack设置为 Focused 的stack.

代码跟踪

moveTopActivityToPinnedStack
    moveTopStackActivityToPinnedStackLocked
        moveActivityToPinnedStackLocked
            moveTaskToStackLocked
                moveTaskToStackUncheckedLocked
                    moveToFrontAndResumeStateIfNeeded
                        moveToFront
                            setFocusStackUnchecked

接着看下 setFocusStackUnchecked

void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
    if (!focusCandidate.isFocusable()) {
        // The focus candidate isn't focusable. Move focus to the top stack that is focusable.
        focusCandidate = focusCandidate.getNextFocusableStackLocked();
    }
}

boolean isFocusable() {
    if (StackId.canReceiveKeys(mStackId)) {
        return true;
    }
    // The stack isn't focusable. See if its top activity is focusable to force focus on the
    // stack.
    final ActivityRecord r = topRunningActivityLocked();
    return r != null && r.isFocusable();
}

public static boolean canReceiveKeys(int stackId) {
    return stackId != PINNED_STACK_ID;
}

boolean isFocusable() {
    return StackId.canReceiveKeys(task.stack.mStackId) || isAlwaysFocusable();
}

boolean isAlwaysFocusable() {
    return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
}

前面在移动 Activity Stack 的时候都是 ok 的,但是当要把当前 PINNED stack设置为 Focused stack 的时候却出现问题了,

在设置一个 stack 为 focused stack 的时候,AMS会去检查这个 stack 是否是 可 focusable 的!其中 PINNED STACK 是不能接收Key的,所以只有topRunningActivity 是可 Focusable 的时候,那么 PINNED stack 才能Focusable.

isAlwaysFocusable() 函数可以看出来,只有当 PINNED STACK 里的 Activity的 flag 设置了FLAG_ALWAYS_FOCUSABLE 才能focuse.

而 FLAG_ALWAYS_FOCUSABLE 又是个什么标志呢?

三、FLAG_ALWAYS_FOCUSABLE标志

FLAG_ALWAYS_FOCUSABLE 这个标志是在 PKMS 去解析AndroidManifest.xml里设置的。

if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
    a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
}

那么问题就转换成要为 AlwaysFocusablePipActivity 设置 alwaysFocusable 属性了。找到 AlwaysFocusablePipActivity 所在的 AndroidManifest.xml 里的描述,

<activity android:name=".AlwaysFocusablePipActivity"
          android:theme="@style/Theme.Transparent"
          android:resizeableActivity="true"
          android:supportsPictureInPicture="true"
          androidprv:alwaysFocusable="true"
          android:exported="true"
          android:taskAffinity="nobody.but.AlwaysFocusablePipActivity"/>

很明显 alwaysFocusable 已经被设置为 true (这里的 androidprv只是一个命名空间,无实际用处), 但是PKMS 竟然没有把它给解析出来。 那这肯定就是 PKMS 的问题了? 不然,请继续查看

四、PKMS解析 alwaysFocusable 属性

明明已经设置了 alwaysFocusable 属性了,为什么 PKMS 解析不出来呢???? 这么奇怪的问题。
从上一节中PKMS 获得该属性的方法得知,无非就是从 TypedArray 里去获得属性值,这还能有错?莫非是AssetManager没有把该值解析出来,赶紧 debug AssetManager.

TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);

R.styleable.AndroidManifestActivity 定义在 framework-res里的attrs_manifest.xml中

<declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">
    <!-- Required name of the class implementing the activity, deriving from
        {@link android.app.Activity}.  This is a fully
        qualified class name (for example, com.mycompany.myapp.MyActivity); as a
        short-hand if the first character of the class
        is a period then it is appended to your package name. -->
    <attr name="name" />
    <attr name="theme" />
    <attr name="label" />
    <attr name="description" />
    <attr name="icon" />
    <attr name="banner" />
    ...
    <attr name="alwaysFocusable" format="boolean" />
    <attr name="enableVrMode" />
</declare-styleable>

R.styleable.AndroidManifestActivity 是一组int型数组, 数组元素是一个一个属性Id 值,而该值是通过 aapt编译出来的ID, 具体可以从 public_resources.xml里查看,该xml文件里面包含了所有的整个系统的resource id.

out/debug/target/common/obj/APPS/framework-res_intermediates/public_resources.xml
  • 本地编译的属性值
<public type="attr" name="theme" id="0x01010000" />
<public type="attr" name="label" id="0x01010001" />
<public type="attr" name="icon" id="0x01010002" />
<public type="attr" name="name" id="0x01010003" />

<!-- Declared at frameworks/base/core/res/res/values/attrs_manifest.xml:1882 -->
<public type="^attr-private" name="systemUserOnly" id="0x011600c4" />
<!-- Declared at frameworks/base/core/res/res/values/attrs_manifest.xml:1899 -->
<public type="^attr-private" name="alwaysFocusable" id="0x011600c5" />

obtainAttributes 最终会调用 frameworks/base/core/jni/android_util_AssetManager.cpp
的 android_content_AssetManager_retrieveAttributes 函数, 而该函数获得对应属性值就是通过该属性的 id 值去编译出来的 xml文件获得。

而且很奇怪的一点,用本地编出来的CTS package去测试,结果就PASS了,而AOSP的CTS package 确是失败的。 这时通过加log调试, 发现 AOSP 编出来的CTS 里的 alwaysFocusable 的属性值与本地编出来的属性是不一样的。

  • AOSP 属性值
<!-- Declared at frameworks/base/core/res/res/values/attrs_manifest.xml:1899 -->
<public type="^attr-private" name="alwaysFocusable" id="0x011600c3" />

从这里基本就可以知道为什么 alwaysFocusable 不能被解析出来了。

五、结论

为什么本地与AOSP编译出来的 alwaysFocusable 的 ID 属性值是不一样的呢?

从 type="^attr-private 与 type="attr" 大概可以猜出来,alwaysFocusable 应该是 私有的 属性,非public的属性。

定义成 public 的属性不管是在什么平台下编出来,其 ID 值应该是一样的。而非public 的属性就不一定了,因为vendor可能自己会定义一些属性,这样 aapt 在解析这些属性, 并给他们赋值时, 可能就会不一样。

而public的属性,作为 AOSP 提供给上层 app 开发者,它们的值必须一样,否则就会导致app 在不同的厂商不兼容的情况。

public的属性值可以从 frameworks/base/core/res/res/values/public.xml 里查看

因此,从这里可以看出来这个是 Google 的问题, 因为Google 用了非public 的属性值编译出来的CTS package 来测试不同的 vendor, 这样做是不正确的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容

  • 多窗口功能介绍 概述 Android 从 Android N(7.0)版本开始引入了多窗口的功能。 关于Andro...
    i_cassell阅读 7,197评论 0 9
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,280评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,537评论 18 399
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,340评论 0 17