Android 15 将禁止NotificationListenerService 读取 OTP
今天介绍一个在国内没什么用的 Android 15 更新,主要是看到了,就顺带介绍一下 NotificationListenerService
。
相信大家对于 NotificationListenerService
应该不会陌生吧,例如在「微信红包通知」、「消息防撤回」、「短信增强通知」、「通知栏管理」等插件都可能会看到过它的身影,而 NotificationListenerService
是在 Android 4.3 的时候被引入,可以说是一个长期存在的 API。
简单介绍一下,首先你可以通过继承 NotificationListenerService
实现一个监听服务,通过 onListenerConnected
你可以快速判断服务是否成功启用,通过 onNotificationPosted
可以读取系统通知栏的消息。
public class NotificationListener extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification nf) {
super.onNotificationPosted(nf);
Notification notification = nf.getNotification();
Bundle extras = notification.extras;
if (extras != null) {
String title = extras.getString(Notification.EXTRA_TITLE, "");
String content = extras.getString(Notification.EXTRA_TEXT, "");
Log.e("notification", "title: " + title + " content: " + content);
}
}
@Override
public void onNotificationRemoved(StatusBarNotification nf) {
super.onNotificationRemoved(nf);
}
@Override
public void onListenerConnected() {
super.onListenerConnected();
}
}
之后通过在 AndroidManifest 声明服务,就可以使用对应的通知栏内容监听能力。
<service
android:name=".view.NotificationListener"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
当然, 由于通知内容可能包含某些敏感数据,所以一般情况下应用除非获得用户手动允许,否则 App 无法直接使用该 API。
而在原生系统下,开启权限的路径在一般在 “Settings-> Notifications -> Notification read, reply & control ”
,开启时也会有对应的提示:
不过这个位置在很多第三方 OS 上面可能不一致,而且不好找,所以一般推荐可以通过 ACTION_NOTIFICATION_LISTENER_SETTINGS
来实现调整打开:
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
事实上根据 NotificationListenerService
的能力,它在国产系统里都会被定义为高危权限,正常情况下系统都不会建议用户开启它:
甚至有时候你还会看到下面这样的情况,例如左侧小米上出现受限的场景,另外右侧在我的华为上看到「平安好车主」也有对应权限描述在,也是有趣:
而且这个支持有时候很诡异,例如有时候在小米上无法触发,需要重启手机才可以正常 connected ,一般情况下可以通过 adb shell dumpsys notification
来确认 Live notification listeners 里是否包含了你定义的监听:
而在成功加入监听之后,通过这个 API App 就可以获取到各种通知的数据,例如包名、title、content 等内容,剋看到此时 App 已经拥有了极高的权限,也可以监听到你微信的通知:
可以看到当拥有 NotificationListenerService
权限之后, App 其实可以很方便地获取到需求用户的隐私内容,其中就包括 OTP 推送:
所以在 Android 15 之前,具有通知访问权限的应用能够读取所有传入的通知,甚至是带有 OTP 代码的通知,而Android 15 之后,系统会阻止不受信任的应用读取敏感通知,即使它们有权读取所有通知也是如此,gif 展示了 14 和 15 上收到 OTP 推送时系统的不同反应:
但是这个支持主要是通过 Android System Intelligence(ASI) 来完成,ASI 会在将所有通知发送到 NotificationListenerService
之前对通知进行处理,如果 ASI 检测到通知中包含如身份验证代码,它会告诉系统将其标记为“敏感内容”,并停止将通知发送到不受信任的 NotificationListenerService
。
❝不受信任的
NotificationListenerService
属于在 Android 15 中没有RECEIVE_SENSITIVE_NOTIFICATIONS
权限的应用。
这个权限只会赋予使用系统证书签名的应用或拥有特定角色的应用,大多数情况下,赋予RECEIVE_SENSITIVE_NOTIFICATIONS
权限的角色只能由系统应用持有,另外一些特定角色可以由第三方应用持有,例如 COMPANION_DEVICE_WATCH
、COMPANION_DEVICE_GLASSES
等。
❝这些特定角色分别被赋予手表配套应用、智能眼镜配套应用和默认启动器等,换句话说,在 Android 15 上唯一可以读取带有身份验证代码的通知的第三方应用:连接到智能手表的应用、连接到智能眼镜的应用或默认主屏幕启动器应用。
另外,在调试时可以通过 adb 命令来做授权处理,这个调整可以保证用户在使用 NotificationListenerService
带来的便捷时能更好保护自己的深度隐私,避免 OTP 代码泄漏,当然这个调整在国内又显得不是很有用。
adb shell cmd appops set --user 0 <PACKAGE> RECEIVE_SENSITIVE_NOTIFICATIONS allow
因为这个功能依赖于 Android System Intelligence(ASI) 来完成,Android System Intelligence 作为 Private Compute Core 中的一个系统组件,它主要是在保持数据私密性的同时为 Android 中的智能功能提供支持。
❝曾经在 Android 12 上还出现过 Android System Intelligence 反复崩溃的问题。
而 ASI 主要依赖于安装了 Google 服务,甚至一些深度功能是 Pixel 独有,所以大多数情况下,你的手机里一般是没有 ASI 的,在没有 ASI 的情况下,就会 API 35 的模拟器场景一样, NotificationListenerService
依然拥有很高的读取能力,所以国内 ROM 厂商是否最终会同样支持 NotificationListenerService
的 OTP 隐私过滤能力还不好说,这也是为什么说这个更新有点鸡肋的原因。
❝后续只能坐等国内的 android 15 版更新来测试最终效果了。