navigator.audioSession 让网页声明音频类型并观察 interrupted 状态。它会泄露哪些设备信息,边界又在哪里。
正在播放音频的网页,可以得知浏览器之外的某件事夺走了音频通道——比如来电,或者另一个应用独占了音频。整个过程不需要权限弹窗,也不需要麦克风权限。这是一个出于合理目的设计的 API 带来的副作用:它本是为了告诉操作系统“这个页面在发出哪一类声音”。本文介绍 Audio Session API 暴露了什么、不能告诉页面什么,以及它为什么和其他“本为适配设备、却兼作被动观察”的 API 属于同一类。
核心要点
navigator.audioSession让页面通过type属性声明音频类别:auto、playback、transient、transient-solo、ambient或play-and-record。操作系统据此决定是压低、暂停还是混合其他音频。- 会话还有
state——active、inactive或interrupted——状态变化时触发statechange事件。 interrupted由浏览器之外的事件触发,例如来电或其他应用抢占音频焦点。页面只知道“有东西优先了”以及时间点,不知道是谁来电、被什么打断。- 各引擎支持不同。 WebKit 在 Safari 16.4 中发布了该 API 的子集,MDN 至今仍将其标为实验性、可用性有限,而什么算作“中断”由平台决定,因此该 API 是否存在及其行为本身就是一种粗粒度的引擎信号。
- 没有关闭它的开关。 只要页面上有音频,这种行为就是固有的,现实的防御只有了解它的存在,再加上通用的反指纹措施。
Audio Session API 的本意
在这个 API 出现之前,浏览器只能猜测页面的音频该如何与设备上的其他声音相处。视频通话页面、播客播放器和只播放一声提示音的页面,从外部看起来很相似,但手机应当区别对待:通话要优先并让其他应用静音,播客在来电时应暂停,提示音则只需让其他音频短暂压低。
AudioSession 让页面自己说明在做什么。type 属性可取以下值:
| 类型 | 预期用途 |
|---|---|
auto | 由浏览器根据播放内容自行决定 |
playback | 音乐、视频、播客——用户主动想听的音频 |
transient | 通知等短促声音,其他音频会被压低 |
transient-solo | 需要短暂让其他一切静音的短促声音 |
ambient | 可与其他应用混合的背景声音 |
play-and-record | 双向音频,例如通话 |
W3C 规范名为“Audio Session”,WebKit 在 Safari 16.4 发布说明中宣布支持其子集。声明类型是这个故事合法的一半,设计也很好:它把信息交给平台,而不是让平台去推测意图。
另一半:会话状态
同一个对象还会报告会话当前所处的状态:
if ("audioSession" in navigator) {
console.log(navigator.audioSession.type); // e.g. "playback"
console.log(navigator.audioSession.state); // "active", "inactive" or "interrupted"
}
共定义了三种状态:active 表示页面正在播放或录制音频,inactive(默认值)表示两者都没有,interrupted 表示平台暂时挂起了一个原本活跃的会话。状态转换时会触发 statechange 事件,页面无需轮询。会话被中断期间,浏览器会自动暂停页面的媒体元素、挂起其 AudioContext 并将麦克风轨道静音,待状态恢复为 active 后再自动恢复。
真正有隐私分量的是 interrupted。MDN 给出的触发示例是来电,以及另一个应用独占音频。相关的 Web Audio 属性 AudioContext.state 也用了同一个词,定义为“网页应用无法控制的外部事件”造成的中断,MDN 还在该页注明:各浏览器触发 interrupted 状态的方式可能不同。具体触发条件由平台和浏览器决定,所以各处并不一致。
为什么这是隐私问题
关于用户在设备上做什么,大多数信号要么需要权限,要么网页根本拿不到。你是否正在通话,本不该是页面知道的事。而 Audio Session API 作为播放声音的副作用,却泄露了这一信息的模糊版本。
需要精确划定边界,因为这件事很容易被夸大:
- 页面无法得知是谁来电,也不知道中断的原因。它看到的是状态变化,而不是起因。
- 只有页面持有音频会话时才有效。 没有音频的页面没有什么可被打断。
- 单次事件是很弱的证据。 来电和其他应用抢占音频焦点,看起来可能一模一样。
剩下的是一条行为时间线。由于页面也能看到状态恢复为 active,它可以给每次中断计时。反复出现的中断、它们的时间点,以及会话保持中断的时长,合起来就是设备何时在忙别的事的记录。它本身不是标识符,但正是分析与反欺诈系统乐于收集的那类低强度、无需提示的信号,也是用户不会想到页面能看到的信号。
更隐蔽的信号:特性检测
还有第二条更常规的泄露途径。navigator.audioSession 是否存在,以及 interrupted 状态如何触发,取决于引擎。WebKit 在 Safari 16.4 中发布了子集,MDN 将该 API 标为实验性、尚非 Baseline,并在与之密切相关的 AudioContext.state 页面上明确指出各浏览器触发中断的方式可能不同。这种差异正是引擎版本与特性检测一文所讲的模式:某项能力在一个引擎中有、在另一个中没有,就能缩小你所用浏览器和操作系统的范围,而无需读取任何 user-agent 字符串。
BrowserInsight 的内核检测对 Chromium 就是这样做的。它的特性矩阵会检测 AudioContext.setSinkId() 等音频 API,把浏览器定位到版本时间线上。需要说明范围:本站不会探测 navigator.audioSession、AudioSession.state 或 AudioContext.state,这里也没有任何工具会报告来电或中断事件。setSinkId() 只是“音频 API 的存在可作为引擎信号”的一个具体例子。
与其他设备状态 API 同一模式
这并非音频独有。反复出现的教训是:为了让页面适配设备而设计的 API,同时也成了对设备的被动观察:
- Battery Status API 无需提示就把电量和充电状态交给页面,后来 Firefox 将其移除,WebKit 从未发布。
- Network Information API 暴露连接类型与估算值,足迹偏向 Chromium。
- 媒体设备枚举会揭示连接了哪些摄像头和麦克风。
- 设备传感器会暴露运动和方向读数。
每个案例的初衷都是真实的。隐私代价来自三者的叠加:无权限提示、持续更新,以及各浏览器实现不一。
为什么移动端更严重
中断在手机上频繁且有意义,在桌面上则少见。来电、其他应用争夺音频焦点,在移动端时刻都在发生,所以这里的信号既更丰富也更有区分度。这也是本文归入移动端专题的原因:同一个 API 在桌面浏览器上大多保持安静。关于手机差异的整体情况,请参阅移动浏览器指纹。
你实际能做什么
老实说,很少。Audio Session API 没有开关,这种行为源自页面上存在音频本身。没有播放音频的页面,也就没有什么可观察的。除此之外,现实的答案就是适用于所有小信号的通用做法:
不一致同样重要:声称是某个平台、却暴露另一个平台 API 表面的配置,正是指纹一致性一文所讲的那类不匹配。
常见问题
网站能知道我正在打电话吗?
不能直接知道。正在播放音频的页面可以看到自己的音频会话变为 interrupted,来电可能导致这一点。它能给这次中断计时,但无法得知是谁来电,也无法判断起因是来电还是其他事件,比如另一个应用接管了音频。
Audio Session API 需要权限吗?
不需要。它是 navigator 的属性,读取其类型和状态不会触发提示。这在很大程度上就是它对隐私重要的原因。
哪些浏览器支持它?
WebKit 在 Safari 16.4 中发布了子集。MDN 将该 API 标为实验性、可用性有限,所以请查看 MDN 页面上的最新兼容性表,而不要想当然。
我能关掉它吗?
没有设置项。页面不播放音频就没有可被打断的内容,屏蔽不需要的脚本则能限制谁在观察。
结论
Audio Session API 是对一个真实问题的合理回答:操作系统应当知道页面在发出哪类音频。它的 interrupted 状态则是意外的附赠品:让页面在没有提示、也不知道起因的情况下,察觉到浏览器之外有事物夺取了优先权。这个信号单独来看很弱,在手机上比桌面更丰富,并与电量、网络和传感器 API 一样提醒我们:适配功能和观察功能往往是同一个功能。
推荐阅读:


