Android Target 31 升级全攻略 —— 记阿里首个超级 App 的坎坷升级之路

行业发展看趋势:

  • 厂商跟进快: 近年来对于 Target 升级的要求表现了“趋紧”和“趋严”,通过此手段,可从系统层面约束各 App 满足隐私合规、统一用户体验等要求;其中:

a、针对预装应用,作为 CTS(Compatibility Test Suite,谷歌的兼容性测试套件)集成的必要一环,若不能及时响应 Target 升级诉求,则很有可能导致预装下架,进而对厂商合作、应用带量等造成严重影响;

  • 隐私力度强:无论政府监管部门,还是厂商、Google,其满足“隐私合规”的要求越加频繁,曾经“粗放”的App 权限已成过去,从长远看,此种限制对用户是有显著收益的,但对于应用开发者而言,需要及时响应、明确趋势,充分理解和执行;
  • 碎片设备多:谷歌和各厂商/ROM 对于隐私、API 调整等的理解不同,其不同版本、不同设备的实施效果有较大差异,且“碎片化”愈演愈烈。如“大致位置”、“启动图”等,各厂商会根据自己的需求来做二次开发,导致在谷歌原生的适配方法,到其它 ROM 中则存在问题

技术看问题:

  • 变化频繁:以 Android 12 为例,Release 发布后至少有 5 次文档变动(包括启动图、模糊定位、文件存储等),并下发过 Release Patch 至厂商,厂商再根据自己的需求、节奏来看是否“实施”Patch,导致适配成本成倍增加;加之各 ROM 的碎片化,过程中需要持续调整对焦
  • 材料不全:当时业内无较好的分析文档,主流 App 也未适配到 31,很多需要自己探索,如新增权限判断、外置存储使用、启动图等,官方要么未提及,要么只说大概,需要自己分析源码 + 不断的实践才能明确。
  • 适配困难:每一个权限调整,其涉及 API 众多、用户影响高,且适配量很大,以我们为例,相比过去 Target 21 → 26 的 20+ 系统 API,本次涉及 300+ 系统 API,上千处调用,涵盖多个技术栈,改造成本高、影响范围广,为我们的适配带来了不小挑战

作为高速发展的超级 App,高德需要做到既保持内部“持续的业务增长”,又能消化外部“要求高、变化快、难度大”。经过大家的不懈努力,最终圆满完成了 Target 28 → 31 的升级。

1.3 收益

  1. 为满足应用市场、厂商预装合规要求,政府、厂商、电信终端产业协会公约 打好提前量,为后续市场先发、预装合作赢得的时间窗口,避免卡脖子;
  2. 在专项过程中沉淀了升级、合规相关经验,设计并落地系统隔离层,降低后续改动对业务的影响,提升后续对新系统、华米 OV ROM、鸿蒙等的应对效率;
  3. 通过源码分析+自研脚本,成功识别 13 个谷歌未提及的改动,减少了 119 个潜在崩溃、不可用风险。

隐私权限

2.1 外置存储、分区存储与限制【29,30】

背景

为了更好保护用户数据并限制设备冗余文件增加,若应用升级到 Target 29,在默认情况下被赋予了对外部存储设备的分区访问权限(即分区存储),应用只能看到本应用专有的沙箱目录以及特定类型的媒体(通过MediaStore)。

现状(SDK=28为目标平台的应用)

当用户授予“存储”权限,允许读写外置非沙盒目录的内容,并在卸载重装后不会被清除;此外,一些用户相册、敏感信息,在授予权限后也可以读取到。

Target 升级后不同访问方式表现

前提:

  • 设置requestLegacyExternalStorage=true 和 PreserveLegacyExternalStorage=true
  • APK targetSDK升级到31
  • 新装/卸载后重装 APK

目录:

  • 共享目录:存储其他应用可访问文件, 包含媒体文件、文档文件以及其他文件,对应设备DCIM、Pictures、Alarms, Music, Notifications,Podcasts, Ringtones、Movies、Download等目录;

  • 沙箱内目录:

    • /sdcard/Android/data/{packagename}

    • /data/data/{packagename}

    • /sdcard/Android/media/{packagename}

  • 其他目录:系统或其他应用在外置SD卡创建的目录;

坑点&避坑建议(已在小米、ov等主流机型验证):

  • 坑点:在Android 11+、Target 为 30+ 且用户新装/卸载重装时,即便没有存储权限,写入超过sdcard两级子目录(比如:/sdcard/xxx/yyy)系统返回“可读写”仍为 true,不符合预期;直到对文件内容做读写时,系统才抛出写入异常,导致失败。
  • 避坑建议:由于单一依靠系统返回结果已不可信,因此对系统返回结果做双重校验。如:Android 11 及以上且“可读写”返回 true 后,写入临时文件,若写入失败则仍放入沙箱。

总结&适配建议

  • 覆盖安装不会自动开启分区存储,原有存储访问权限不受影响;
  • 应用可通过requestLegacyExternalStorage和PreserveLegacyExternalStorage两个属性禁掉分区存储机制来完成数据迁移,但这两个属性只对升级有效,在android 11后的机器新装/重装会强制开启分区存储机制;
  • 分区存储开启后无需申请存储权限即可正常访问沙箱内目录;
  • 分区存储开启后非沙箱内目录访问会受限,具体表现见上表格;
  • 分区存储开启后仍然需要申请存储权限,否则访问共享目录会受限。

2.2 蓝牙权限及不同策略【29,30,31】

涉及权限的蓝牙API

大致可以分为下面三类:

不同版本中蓝牙API与权限的对应关系,最终总结起来如下:

以下是Target SDK从28升级到31需要做的适配:

  1. 为不同Android版本申明不同的权限
  • 为了继续兼容SDK 29-30,需要保留在Manifest中申明的BLUETOOTH和BLUETOOTH_ADMIN权限,同时还应声明maxSdkVersion为30,ACCESS_COARSE_LOCATION也需要保留,如:
  • 新增BLUETOOTH_SCAN,BLUETOOTH_CONNECT 和BLUETOOTH_ADVERTISE权限的申明。如
  • 新增ACCESS_FINE_LOCATION权限申明(如果确定应用不会推导用户位置,可跳过此申明和下面的动态申请。但需在BLUETOOTH_SCAN权限申明时,添加android:usesPermissionFlags=”neverForLocation”申明)

2、动态申请运行时权限

  • SDK 29-30,需要动态申请获得ACCESS_FINE_LOCATION
  • SDK ≥ 31,先动态申请获得ACCESS_COARSE_LOCATION+ACCESS_FINE_LOCATION(模糊定位引入的新要求,详见下文),然后再组合BLUETOOTH_SCAN、BLUETOOTH_CONNECT和BLUETOOTH_ADVERTISE一起申请获得蓝牙权限。如果在未获得ACCESS_FINE_LOCATION的情况下,直接申请蓝牙权限,可能导致请求被忽略的异常结果。

3、使用API前做权限校验

使用涉及权限的蓝牙API前,需做权限校验。确定已具备相应权限,再继续调用;否则应停止调用,否则可能导致应用直接崩溃。

  • SDK 29-30,判断是否具备ACCESS_FINE_LOCATION权限即可
  • SDK ≥ 31,因为采用了组合申请方式,我们可以直接判断是否同时具备ACCESS_FINE_LOCATION和BLUETOOTH_SCAN即可

2.3 大致位置【31】

(厂商称“模糊定位”,以下做统一)

背景

升级到Target 31后,在Android 12系统的定位权限设置页和授权弹窗中,明确区分了精确定位和模糊定位,并允许用户选择仅使用模糊定位,即当开启“模糊定位”时,其“精准定位”权限被关闭。此前,小米/Vivo的部分Android 11机型已经采用了这种方式为用户提供更灵活的定位选择,Android target 31升级算是借鉴了相同的思路。可以理解为,在原先仅小米/Vivo 支持“模糊定位”(关闭精确定位)的基础上,Target 升级后,将其扩展到了 Oppo/华为/三星等其他厂商的所有 Android 12 系统。

不同厂商/版本策略

图 定位授权弹窗

以下是Target 31升级前后关于模糊定位的对比情况:

  • Target ≥ 30 时:应用需在AndroidManifest声明ACCESS_BACKGROUND_LOCATION 权限,然后用户在系统设置页面上选择“始终允许”后才能获取到后台定位能力。

  • 移除/修改保存的 WiFi:需更换 为新API removeNetworkSuggestions;
  • 主动连接 WiFi:已禁止(enableNetwork),只能交由系统策略处理或者用户去系统设置连接。

适配建议

  • 获取 WiFi 列表:适配当用户只授予“模糊定位”返回 Null 时的情况;去掉对已保存 络(getConfiguredNetworks)接口的依赖;
  • 添加/移除/修改保存的WiFi:更换新 API。考虑到有系统弹窗,如需要的话可引导用户完成。

3.3 更安全的组件导出【31】

背景

如果您的应用以 Android 12 或更高版本为目标平台,且包含使用 intent 过滤器的 activity、服务或广播接收器,您必须为这些应用组件显式声明 android:exported 属性。

警告:如果 activity、服务或广播接收器使用 intent 过滤器,并且未显式声明 android:exported 的值,您的应用将无法在搭载 Android 12 或更高版本的设备上进行安装。

影响

需要check一下activity、服务或广播中包含intent过滤器的场景。

思考&建议

官方考虑对于强制声明android:exported 属性,主要是考虑到安全性,自然也建议我们将exported 属性非必需true的都改成false,理想的角度,推荐大家逐一check一下所有的场景。

当然如果大家想更快捷的去解决,推荐在编译期间,解析AndroidManifest,对于没有主动设置exported属性的统一设置,这样也可以一并解决 SDK 相关问题。

这里有个细节需要注意一下,当Activity包含intent 过滤器时,如果没有设置exported属性,系统在运行的时候会将exported解析成true使用,这在系统的源码中也是有体现的;这样我们就需要考虑历史业务场景中:可能会存在没有给exported设置属性,却将exported设为true来使用。

3.4 前台服务启动限制【31】

背景

以 Android 12 或更高版本为目标平台的应用无法在后台运行时启动前台服务,少数特殊情况除外。如果应用尝试在后台运行时启动前台服务,则会引发异常。

影响

使用到启动前台服务(API如下)的业务场景,需要check是否有从后台启动的情况,如果有看是否满足特殊情况。主要涉及:startForegroundService 和 startForeground 方法。

建议:

尽量避免,甚至杜绝(随着系统不断升级,对于从后台启动前台服务越来越严)从后台启动前台服务。建议从静态分析角度查找所有涉及前台调用的 API,梳理和 Check。

3.5 前台服务访问摄像头、麦克风需声明【30】

背景:

  1. 在前台服务中访问摄像头或麦克风,则必须添加前台服务类型 camera 和 microphone;
  2. 如果应用在后台运行时启动了某项前台服务, 则该前台服务无法访问麦克风或摄像头,如果想访问位置,需要有后台访问位置信息的权限。

影响

前台服务中使用摄像头、麦克风或位置。

建议

  1. 如果在前台服务中需要使用camera和microphone,需要在AndroidManifest.xml声明对应类型。如下:
  2. 不建议应用在后台时启动前台服务,因为如果Target升级到31的时候,除非特殊情况,否则无法从后台启动前台服务。

3.6 待处理 intent 可变性【31】

背景

如果您的应用以 Android 12 为目标平台,则需对 Pending Intent 强制设置“可变性”(即 FLAG_IMMUTABLE/FLAG_MUTABLE),这项额外的要求可提高应用的安全性。

影响

使用到PendingIntent的业务场景。

建议

根据需要为PendingIntent填写 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 标志;此外,最好提供一个适配的聚合类,其他类都直接调用适配类的方法,这样可以减少适配成本。

3.7 更新后的非 SDK 限制【29,30,31】

背景

从 Android 9(API 级别 28)开始,Android 平台对应用能使用的非 SDK 接口实施了限制。只要应用引用非 SDK 接口或尝试使用反射或 JNI 来获取其句柄,这些限制就适用。这些限制旨在帮助提升用户体验和开发者体验,为用户降低应用发生崩溃的风险,同时为开发者降低紧急发布的风险。

  • 区分 SDK 接口和非 SDK 接口: 一般而言,公共 SDK 接口是在 Android 框架软件包索引中记录的那些接口。非 SDK 接口的处理是 API 抽象出来的实现细节,因此这些接口可能会在不另行通知的情况下随时发生更改。为了避免发生崩溃和意外行为,应用应仅使用 SDK 中经过正式记录的类。这也意味着当您的应用通过反射等机制与类互动时,不应访问 SDK 中未列出的方法或字段;
  • 非 SDK API 名单: 随着每个 Android 版本的发布,会有更多非 SDK 接口受到限制。为最大程度地降低非 SDK 使用限制对开发工作流的影响,将非 SDK 接口分成了几个名单,这些名单界定了非 SDK 接口使用限制的严格程度(取决于应用的目标 API 级别)。

影响

使用google官方提供的工具和Android 12 非 SDK 接口及其对应的名单,就可以对需要的APP扫描出结果,根据结果可知道影响范围。

建议

理想情况下,我们应该只使用SDK(whitelist);但是一些App为了获得一些能力,使用了非sdk接口。因此:

  • 对于greylist,我们暂且可以使用;
  • 对于greylist-max-x,我们需要根据工程中target版本来看是否可以暂且使用,greylist-max-p等价于max-target-p;
  • 对于blacklist,则无法使用。另外所有的非sdk接口一定要加try catch保护。

写在结束前

以上是我们在 Target 升级中的思考和解法,由于篇幅所限,上述仅介绍了一些隐私安全相关的“关键要点”,具体的技术实现细节,大家若有兴趣,欢迎随时在评论区留言讨论。

Target 升级的关键之处,除了外部(厂商、政府政策)的推动,公司内部对于拥抱隐私合规,对用户负责的积极态度外,还有多方团队的合作,自上而下的重视,以及自内而外的决心,三者缺一不可。Target 升级绝非“一锤子买卖”,它需要长期、持之以恒的耕耘,才能不断结出果实。希望我们的经历,能为大家带来启发,少走弯路,轻装上阵。

关注【阿里巴巴移动技术】,阿里前沿移动干货&实践给你思考!

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年5月25日
下一篇 2022年5月25日

相关推荐