TargetsdkVersion 升级31(Android12)适配

我们升级到Targetsdk29有大半年时间了,今年为了满足审查去除蓝牙的精确定位权限,以及满足上架Google Play的要求,需要将Targetsdkversion升级到31,适配到Android12。这个过程遇到不少坑,这里记录一下,希望能对大家有所帮助。转载请注明来源「申国骏」

由于我们在适配Android 29的时候已经适配了Scoped storage,因此这篇文章里面没有这部分的描述。

安全组件输出,exported

编译时报错:

android:exported needs to be explicitly specified for element <activity#xxxActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for android:exported when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

问题描述:

在target到Android12之后,所有设置了intent filters的activity、services、broadcast receivers都需要设置 android:exported ,否则会导致编译异常。

解决办法:

如果需要被外部其他app访问的component(例如设置了android.intent.category.LAUNCHER 的页面),那么需要exported=true,其他情况设置为exported=false

  • Activity

    true表示当前Activity需要被外部应用调用,例如桌面和应用需要打开当前应用首页,false表示当前Activity只能被当前的应用,或者具有相同userID的应用,或者有调用特权的系统components

  • Service

    true表示可以跟外部应用的component进行交互,false表示只有自己应用内的component以及具有相同userID的应用的component可以启动并绑定这个服务。

  • Receiver

    true表示可以非系统的其他应用的广播,false表示只能收到系统的、自己应用的、具有相同userID应用的广播

对于一些aar或者依赖库有里面component的报错,有两个解决办法:1. 尝试升级对应的依赖库版本,并看看是否已经进行了target android12适配;2. 在主工程中xml拷贝相关component声明,并覆盖exported设置,例如:

android:exported="true"
tools:replace="android:exported"

PendingIntent mutability

运行时报错:

java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx}: java.lang.IllegalArgumentException: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.

问题描述:

在target到Android12之后,PendingIntent创建需要指定可变性FLAG_IMMUTABLE 或者 FLAG_MUTABLE

解决办法:

大部分情况下如果不希望创建的PendingIntent被外部应用修改,那么需要设置成PendingIntent.FLAG_IMMUTABLE既可。一些特殊情况可以设置成FLAG_MUTABLE(参考:https://developer.android.com/guide/components/intents-filters#DeclareMutabilityPendingIntent

PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);

传感器刷新频率问题

运行时报错:

java.lang.SecurityException: To use the sampling rate of 0 microseconds, app needs to declare the normal permission HIGH_SAMPLING_RATE_SENSORS.
at android.hardware.SystemSensorManager$BaseEventQueue.enableSensor(SystemSensorManager.java:884)
at android.hardware.SystemSensorManager$BaseEventQueue.addSensor(SystemSensorManager.java:802)
at android.hardware.SystemSensorManager.registerListenerImpl(SystemSensorManager.java:272)
at android.hardware.SensorManager.registerListener(SensorManager.java:835)
at android.hardware.SensorManager.registerListener(SensorManager.java:742)

问题描述:

当使用SensorManager时,如果监听的频率太快,例如sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);,且没有改定义permission HIGH_SAMPLING_RATE_SENSORS权限的话会有这个崩溃。

解决办法:

大部分情况下我们并不需要太快的监听频率,可以设置成SensorManager.SENSOR_DELAY_UI。在某些确实需要快速频率监听的话,需要加上HIGH_SAMPLING_RATE_SENSORS权限

ijkplayer

运行时崩溃:

运行时的native崩溃

问题描述:

在target到Android11并且在64位的安卓系统版本11及以上的手机,使用ijkplayer会产生崩溃。这里的原因是Android11对于64位的处理器中,每个指针的第一个字节将被用作标记位,用于ARM的内存标记扩展(MTE)支持。在释放内存的时候如果修改这个标记位程序就会崩溃。

那么ijkplayer在哪里会导致第一个字节被修改了呢,查看这个issues https://github.com/bilibili/ijkplayer/issues/5206 以及提交记录 https://github.com/bilibili/ijkplayer/commit/e99d640e5fe94c65132379307f92d7180bcde8e7 可以看出,主要的原因是之前将指针转换成了int64_t类型导致了精度丢失,修改的地方是将指针转成String或者无符号整形,避免精度丢失导致的首位字节丢失。

Memory_Tagging_Blog_1040x1040.png-1040x0.png

例如,在上面的图中,访问0x8000的内存是可行的,因为用于进行访问的指针具有与被访问的内存相同的标签(用颜色表示)。但是,对0x9000的访问将会失败,因为指针对内存有不同的标记。

解决办法:

解决办法有两个,一个是拉一下ijkplayer最新的代码重新build一个依赖库更新一下,因为ijkplayer已经修改了这个错误。第二个办法是通过设置<application android:allowNativeHeapPointerTagging="false">暂时禁用Pointer Tagging功能。

TelephonyManager.getNetworkType

运行时崩溃:

************* Crash INFO AT 04/01/2022 10:16 *************java.lang.SecurityException: getDataNetworkTypeForSubscriber
android.os.Parcel.createExceptionOrNull(Parcel.java:2389)
android.os.Parcel.createException(Parcel.java:2373)
android.os.Parcel.readException(Parcel.java:2356)
android.os.Parcel.readException(Parcel.java:2298)
com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:8762)
android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3024)
android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2988)

问题描述:

我们使用到的一个一键登录的库调用的TelephonyManager.getNetworkType被标记为deprecated,需要改成使用 getDataNetworkType ,并且需要加上权限READ_PHONE_STATE 或者 READ_BASIC_PHONE_STAT

解决办法:

升级一键登录的库,并且加上对应权限

webview访问文件

运行时问题:

加载file://data目录底下数据时webview报错: 网页无法加载,net:ERR_ACCESS_DENIED

问题描述:

在target到Android11及以上的时候,默认setAllowFileAccess从true改成了false,无法访问到context.getDir()里面的文件,参考:https://developer.android.com/reference/android/webkit/WebSettings#setAllowFileAccess(boolean)

解决办法:

手动调用一下webSettings.setAllowFileAccess(true)

Package可见性

运行时问题:

当使用[queryIntentActivities()](https://developer.android.com/reference/android/content/pm/PackageManager#queryIntentActivities(android.content.Intent, int)), [getPackageInfo()](https://developer.android.com/reference/android/content/pm/PackageManager#getPackageInfo(java.lang.String, int))或者 getInstalledApplications()查询是其他应用信息的话会查不到

问题描述:

当应用target到Android11之后,Package可见性受到了限制,查询其他应用信息需要加上QUERY_ALL_PACKAGES权限或者使用queries方式获取。

解决办法:

  1. 在AndroidManifest.xml中加入权限<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />,这个需要谨慎使用,因为应用市场上线检查可能会需要提供使用的必要性说明,例如Google Play政策:https://support.google.com/googleplay/android-developer/answer/10158779

  2. 在AndroidMainifest.xml中定义需要访问的应用信息,例如

    • 需要访问某个应用信息,直接指定应用包名

      <queries>
        <package android:name="com.example.store" />
      </queries>
      
    • 需要访问具有某些intent的外部组件,指定需要访问的intent

      <queries>
        <intent>
          <action android:name="android.intent.action.SEND" />
          <data android:mimeType="image/jpeg" />
        </intent>
      </queries>
      
    • 需要访问某些外部content provider,指定authoritites

      <queries>
        <provider android:authorities="com.example.settings.files" />
      </queries>
      

微博SDK

运行时问题:

微博SDK更新到最新版支持适配安卓11,遇到一个初始化的报错please init sdk before use it. Wb.install()

问题描述:

在微博进行登录授权的时候,需要获取授权信息,不过获取授权信息的时候,有一个断言判断失败了。需要在初始化之后等待一段时间。

解决办法:

通过循环等待的方式等待初始化断言通过再进行其他SDK操作:

public static void waitForWeiboSDKValid() {
    // 微博sdk初始化需要等待一下
    // https://github.com/sinaweibosdk/weibo_android_sdk/issues/608
    // https://xie.infoq.cn/article/974795351e87627681cc353b5
    int retryCount = 0;
    while (retryCount <= 10) {
        try {
            a.b();
            break;
        } catch (Exception ignore) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException ignored) {
            }
            retryCount++;
        }
    }
}

private void installWbSdk() {
   WBAPIFactory.createWBAPI(getApplicationContext());
   mWBAPI.registerApp(getApplicationContext(), authInfo);
   waitForWeiboSDKValid();
}

后台启动前台服务

运行时崩溃:

Caused by: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service XXXXService 16 at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54) 17 at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50) 18 at android.os.Parcel.readParcelable(Parcel.java:3345) 19 at android.os.Parcel.createExceptionOrNull(Parcel.java:2432) 20 at android.os.Parcel.createException(Parcel.java:2421) 21 at android.os.Parcel.readException(Parcel.java:2404) 22 at android.os.Parcel.readException(Parcel.java:2346) 23 at android.app.IActivityManagerStubProxy.setServiceForeground(IActivityManager.java:8040) 24 at android.app.Service.startForeground(Service.java:733)

问题描述:

应用在target到Android12之后,如果应用在后台启用前台服务,那么就会报ForegroundServiceStartNotAllowedException

解决办法:

  1. 使用WorkManager来处理后台任务
  2. 避免在后台启动前台服务

蓝牙权限

运行崩溃:

Caused by: java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT permission for android.content.AttributionSource@db46d647: enable 37 at android.os.Parcel.createExceptionOrNull(Parcel.java:2425) 38 at android.os.Parcel.createException(Parcel.java:2409) 39 at android.os.Parcel.readException(Parcel.java:2392) 40 at android.os.Parcel.readException(Parcel.java:2334) 41 at android.bluetooth.IBluetoothManagerStubProxy.enable(IBluetoothManager.java:611) 42 at android.bluetooth.BluetoothAdapter.enable(BluetoothAdapter.java:1217)

问题描述:

在target到Android12之后,查找蓝牙设备需要添加 BLUETOOTH_SCAN 权限,与匹配的蓝牙设备传输数据需要获取BLUETOOTH_CONNECT 权限

解决办法:

在查找和匹配蓝牙设备之前,先动态申请 BLUETOOTH_SCAN 权限以及BLUETOOTH_CONNECT 权限。

其他

检查依赖的SDK中是否有新的版本,并进行更新,因为安全组件输出Exported以及包可见性的问题对于大多数SDK都可能会存在,所以最好都检查一下,例如华为小米OV相关的产商推送SDK,以及微信QQ微博等登录和分享的SDK。

参考

  1. Bluetooth permissions
  2. Target API level requirements for Play Console
  3. Sensor Rate-Limiting
  4. Behavior changes: Apps targeting Android 12
  5. Behavior changes: Apps targeting Android 11
  6. vivo Android 12应用适配指南
  7. vivo Android 11应用适配指南
  8. oppo Android 12 应用兼容性适配指导
  9. oppo Android 11 应用兼容性适配指导
  10. 小米 Android 12应用适配指南
  11. Tagged Pointers
  12. Memory Tagging Extension: Enhancing memory safety through architecture
  13. Package visibility filtering on Android
  14. Declaring package visibility needs
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,874评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,151评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,270评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,137评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,116评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,935评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,261评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,895评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,342评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,854评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,978评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,609评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,181评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,182评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,402评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,376评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,677评论 2 344

推荐阅读更多精彩内容