1. 简介
传感器数据在应用开发中的使用日益增多,为诸如地理定位、计步或头部跟踪等新用例提供了支持。 这在移动设备上尤为明显,新型传感器被不断引入。
将传感器数据暴露到 Web 目前一直进展缓慢且零散。 目前可被 Web 获取的传感器很少。 即使可用,往往以限制其用例的方式暴露 (例如,仅暴露过于高层的抽象,且性能不佳)。 各传感器的 API 设计也大相径庭, 增加了 Web 应用开发者的认知负担, 并减缓了开发速度。
通用传感器 API 的目标是 促进各类传感器 API 之间的一致性, 通过高性能的低层API支持高级用例, 并通过简化规范和实现流程, 加快新传感器在 Web 上的开放速度。
基于通用传感器 API 的具体传感器列表、适用用例及代码示例详见 [GENERIC-SENSOR-USECASES] 和 [MOTION-SENSORS] 说明文档。
2. 范围
本节为非规范性内容。
本规范目前的范围仅限于定义用于暴露来自设备传感器数据的基础原语。
暴露远程传感器 或个人局域网(如蓝牙)中的传感器 不在当前规范范围之内。 随着相关领域工作的推进, 可能可以找到通用、较低层的原语, 届时本规范会相应更新。 不过,这对具体实现几乎没有影响。
此外,本规范目前尚未提供 传感器发现 API。 由于用户代理当前可用的传感器数量有限, 无需提供类似 API。 可依靠 §3 关于硬件特性检测的说明 所述的特性检测方法, 目前已足够。 未来规范版本可能会定义发现 API, 当前 API 的设计也已为此预留空间。
3. 关于硬件特性检测的说明
本节为非规范性内容。
特性检测是 Web 开发中公认的最佳实践。 相关资料非常丰富, 本节不再赘述, 仅着重将其置于检测硬件相关特性的背景下。
请参考以下特性检测示例:
if ( typeof Gyroscope=== "function" ) { // run in circles... } if ( "ProximitySensor" in window) { // watch out! } if ( window. AmbientLightSensor) { // go dark... } // etc.
以上代码都能反映出某 API 的存在 及其部分特征, 但无法判断该 API 是否与真实硬件传感器相连, 传感器本身是否正常工作, 是否依然连接, 甚至用户是否允许你访问该传感器。 关于后者,可借助 Permissions API [PERMISSIONS] 进行检测。
理想情况下,关于底层状态的信息 能被事先获得。 然而现实中存在两方面问题: 首先,从硬件获取这些信息代价较大, 既影响性能也耗电, 且会处于关键路径上。 其次,底层硬件状态随时可能变化。 用户可能撤销权限、传感器连接中断、 操作系统也可能因电量临界而限制传感器使用等。
因此,有效策略为结合特性检测 (检测是否存在目标传感器对应的 API), 并辅以防御性编程,包括:
-
实例化
Sensor对象时捕获异常, -
监听该对象发出的错误事件,
-
优雅处理以上情形,让用户因使用传感器体验更佳,而非因缺失变差。
let accelerometer= null ; try { accelerometer= new Accelerometer({ frequency: 10 }); accelerometer. addEventListener( 'error' , event=> { // Handle runtime errors. if ( event. error. name=== 'NotAllowedError' ) { console. log( 'Permission to access sensor was denied.' ); } else if ( event. error. name=== 'NotReadableError' ) { console. log( 'Cannot connect to the sensor.' ); } }); accelerometer. addEventListener( 'reading' , () => reloadOnShake( accelerometer)); accelerometer. start(); } catch ( error) { // Handle construction errors. if ( error. name=== 'SecurityError' ) { console. log( 'Sensor construction was blocked by the Permissions Policy.' ); } else if ( error. name=== 'ReferenceError' ) { console. log( 'Sensor is not supported by the User Agent.' ); } else { throw error; } }
4. 安全与隐私注意事项
传感器读数属于敏感数据,可能被恶意网页用于各类攻击。在讨论缓解策略前, 我们简要列出传感器的主要隐私与安全威胁类型。 [MOBILESENSORS] 将主要威胁分为 位置跟踪、窃听、按键监控、设备指纹 以及 用户识别。 这一分类与本规范完全契合。
当多个传感器联合使用,或与其他功能结合使用,或长期持续使用时, 攻击成功风险会提升,尤其体现在数据相关性和通过指纹识别用户的风险。 采用这些 JavaScript API 的 Web 应用开发者应考虑信息可能被如何整合,以及潜在的隐私风险。 还应考虑长期收集此类数据所带来的风险。
传感器读数及事件触发频率 都有可能导致用户被指纹识别。 用户代理可通过 限制开发者可用的事件频率 来降低风险。
降低传感器读数精度 通常能减少指纹识别风险。 用户代理不应输出过于详细的传感器数据。 不同传感器类型应分别评估。
若同一 API 的 JavaScript 代码可 在一台设备的多个窗口上下文中并发执行, 代码间或可关联追踪用户, 带来非预期的跟踪机制。
用户代理应考虑向用户 提示 传感器的使用情况, 并允许用户禁用之。 另外,用户代理也可考虑 让用户查看过去和当前的传感器使用模式。
使用传感器的 Web 应用开发者应 对应用执行隐私影响评估, 全面考虑所有相关因素。
检测设备上全部可用传感器的能力可能形成唯一标识,进而用于指纹识别。
选定传感器的组合甚至可能作为设备间的隐式通信信道。
传感器还可能被用于跨设备的关联与跟踪。
4.1. 隐私与安全威胁类型
本节为非规范性内容。
4.1.1. 位置跟踪
在此类威胁下,攻击者可利用传感器读数定位设备, 而无需 GPS 或其他定位传感器。例如,可通过加速度计数据用统计模型估算轨迹, 进而通过地图匹配算法获取预测位置(精度可达 200 米半径以内)[MOBILESENSORS]。
4.1.2. 窃听
从陀螺仪读数恢复语音属于窃听攻击的一例。 参见 [GYROSPEECHRECOGNITION]。
4.1.3. 按键监控
许多用户输入都可由传感器读数推测,包括利用运动传感器攻击用户 PIN、密码、解锁模式 (甚至点击、滑动、缩放等触控操作)。 这些攻击通常训练机器学习算法来获取用户的此类信息。参见 [STEALINGPINSVIASENSORS]。
4.1.4. 设备指纹
传感器可泄露可用于唯一识别设备的信息。 每一型号的具体传感器在制造上都有微小差异,这些差异可被用于指纹识别设备 [ACCELPRINT] [MOBILESENSORS]。
4.1.5. 用户识别
传感器读数能用于识别用户,例如通过分析智能手机或可穿戴设备运动传感器的数据推断个人步态。
4.2. 缓解策略
本节为非规范性内容。
本节简要介绍一些在规范性章节中指定的缓解策略。
4.2.1. 安全上下文
传感器读数被《安全上下文》规范 [POWERFUL-FEATURES] 明确列为网络攻击者的高价值目标。 因此本规范 以及扩展规范定义的所有接口均只在安全上下文中使用。
4.2.2. 权限策略
为避免向用户不熟悉的上下文泄露传感器读数,传感器读数 仅对允许使用相应策略控制特性的传感器类型的文档可用。详见 [PERMISSIONS-POLICY]。
4.2.3. 聚焦区域
传感器读数仅对活跃文档可用, 前提是其通过了焦点与来源检查。
此举旨在缓解对可导航对象包含有获得焦点元素的扒窃攻击的风险, 例如用户在 iframe 内进行游戏内购买,启用第三方支付服务时。
4.2.4. 可见性状态
传感器读数仅对活跃文档可见, 且其可见性状态为 "visible"。
4.2.5. Permissions API
访问传感器读数 受到 Permissions API [PERMISSIONS] 控制。
4.3. 针对具体情况应用缓解策略
每种传感器类型都需单独评估, 考虑其启用的用例与威胁情况。 下列缓解策略适用于部分传感器, 但有时也会妨碍或完全阻止某些用例。
注:这些缓解措施可持续应用,也可临时启用, 如用户执行特定操作时、 或调用其他可能提升威胁等级的 API 时等。
4.3.1. 限制最大采样频率
用户代理及扩展规范可通过定义传感器类型的最大采样频率, 来缓解部分威胁。
上限选择取决于传感器类型、 所要防范的威胁、 以及攻击者的预期能力等因素。
对最大采样频率的限制将妨碍 依赖低延迟或高数据密度的用例。
4.3.2. 彻底停止传感器
这当然是最后的手段, 但在短暂场景下极其有效, 如当用户在不同域名([rfc6454]) 或不同应用中输入凭据时,可阻止密码扒取尝试。
4.3.3. 限制推送读数数量
与限制最大采样频率相比, 另一做法是将传给 Web 开发者的传感器读数数量限制, 无论采样频率为何。 这样一来,具备低延迟需求的用例 可以提升采样频率而不会获得更多数据。
丢弃中间结果会影响某些用例, 比如依赖特定滤波的数据处理。
4.3.4. 降低精度
降低传感器读数或读数时间戳的精度也有助于缓解威胁, 用户代理不应输出过于详细的传感器数据。
具体传感器实现可定义阈值检查算法,当新读数与最新读数差异不大时丢弃。
具体传感器实现可定义读数量化算法,减少来自设备传感器的传感器读数精度。
注:这两种缓解措施通常可互补。如果仅执行阈值检查算法,则读数依然过于精细;若仅量化读数,则可能暴露真实精度。
注:对传感器读数 的进一步操作、或据时间戳计算的时间差,将进一步放大误差, 因此此缓解措施会影响某些用例。
注:虽然给传感器读数加入随机偏差亦可达到相似效果, 但实际并不可取,因为很容易被滤波掉噪声。
4.3.5. 让用户知晓 API 的使用
用户代理可选择向用户告知 当前及历史 API 使用情况。
注:这并不意味着要记录实际的传感器读数,否则会引发其它隐私问题。
5. 概念
5.1. 传感器
设备传感器 指的是设备底层的物理传感器实例。
设备传感器 用于测量物理量,并提供相应的 传感器读数,它是环境信息的来源。
每个 传感器读数 都由 设备传感器 在时间点 tn 测得的物理量的值组成,该时间点被称为 读数时间戳。
如果 设备传感器 用于空间测量(如加速度、角速度),则必须在 本地坐标系 中进行解析,此坐标系代表 设备传感器 的 传感器读数的参考系。 能够提供此类 传感器读数 的 设备传感器 称为 空间型传感器。
空间型传感器 根据其可同时测量的正交轴的数量,可以是单轴、双轴或三轴传感器。
标量物理量(如温度)不需要 本地坐标系 来解析。
移动设备中常用的 本地坐标系 通常是直角坐标系,其相对设备屏幕定义,使得 X、Y 轴平行于屏幕,Z 轴垂直于屏幕表面。
平台传感器 指的是平台接口,用户代理通过它们获取针对单一 传感器类型、 来源自一个或多个 设备传感器 的 传感器读数。
平台传感器 可以由底层平台(如本地传感器框架)定义,也可以由用户代理定义,如果其可直接访问 设备传感器。
从实现角度看,平台传感器 可以被视为相应 设备传感器 的软件代理。如果底层平台支持,可以同时有多个 平台传感器 与同一 设备传感器 交互。
在简单情况下,平台传感器仅对应一个 设备传感器, 如果所提供的传感器读数是通过软件进行 传感器融合 得到的,那么 平台传感器 对应于参与 传感器融合 的一组 设备传感器。
传感器读数 和实际被测物理量之间的偏差可通过 校准 消除,该过程可发生在制造阶段,有些传感器还需动态校准以补偿未知误差。
注: 通过 传感器融合 创建的 平台传感器 有时也称为虚拟或合成传感器,但规范对此并未做出实际区分。
5.2. 传感器类型
不同的传感器类型 用于测量不同的物理量,比如温度、气压、心率或亮度。
以实现方式为特征的 传感器类型 被称为 低层 传感器。例如,陀螺仪就是一种 低层 传感器类型。
按其 读数 命名的传感器, 无论实现方式如何, 称为 高层 传感器。 例如,地理位置传感器用于提供用户位置的信息,但其数据来源是刻意不透明的 (数据可能来源于 GPS 芯片、蜂窝网络定位、WiFi 等,或组合), 并依赖于实现相关的启发式方法。高层传感器往往是对 低层传感器的算法处理结果——例如计步器只需陀螺仪数据即可实现——或者通过传感器融合得到。
需要指出的是,高层与低层传感器类型的划分是一种约定,界限常常模糊。 例如,压力计用于测量气压,多数情况下被归为低层,尽管其读数来自于压电式或热敏电阻等多组件的传感器融合,而对这些底层组件的暴露并无实际意义。 高度计一般也如此,但如果高度计数据既可来自压力计,也可来自 GPS,则这类“泛指”高度计显然应归入高层传感器类型。
鉴于这种界限的模糊性,扩展规范(见 §10 可扩展性)鼓励为各自关注的传感器类型单独定义高层与低层传感器。
不同传感器类型的传感器读数可通过一种称为 传感器融合 的过程进行结合。 这样能产生更高层或更精确的数据(通常以牺牲延迟为代价)。 例如,三轴磁力计的读数常需与加速度计的读数结合,才能得到正确的方位。
智能传感器和传感器集线器有内置的计算能力,可在硬件层实施 校准 和 传感器融合,降低 CPU 负担与电量消耗。
若无法在硬件层实施 传感器融合,或需特定的融合算法时,也可在软件层实现 传感器融合。
5.3. 默认传感器
通用传感器 API 设计旨在让最常见用例实现简单,同时支持更复杂的场景。
目前大多数设备并不含有多于一个可提供同类型传感器读数的 设备传感器。需要相同类型 设备传感器 集合的用例,比 较少,通常限于特定 传感器类型,如 2 合 1 笔记本中的多个加速度计。
因此,API 让开发者能直接实例化对应 Sensor
子类,便捷地访问设备对于每种类型的默认(通常也是唯一的)传感器。
确实,如果没有针对给定类型的 传感器 的确切标识信息,则由用户代理选定 默认传感器。
若底层平台可用于查找 默认传感器,用户代理须选用平台指定的传感器,否则自行确定设备上哪一个 传感器 作为 默认传感器。
let sensor= new Accelerometer({ frequency: 30 }); sensor. onreading= () => { ... } sensor. start();
注: 若某些扩展规范下定义 默认传感器 并无意义,则可不做定义。例如,地理位置 传感器类型 不需要默认 传感器,因为其接口实现可用多个后端。
若一个设备中存在多个对应同一类型的 设备传感器, 则扩展规范须定义区分各自身份的方法。
var sensor= new DirectTirePressureSensor({ position: "rear" , side: "left" }); sensor. onreading= _=> console. log( sensor. pressure); sensor. start();
5.4. 采样频率与上报频率
本规范中,平台传感器的 采样频率 指 平台传感器 从底层 设备传感器 采集 传感器读数 的频率。具体获取方式为 实现自定义。
平台传感器的 采样频率 未必等于 设备传感器 实际采样速率,后者对本规范来说是不可见的。
注: 系统层面的传感器读数API及底层硬件接口,可能基于轮询或事件。对于轮询型 设备传感器,平台传感器的 采样频率 是请求新读数的速率,而事件型 设备传感器则由 平台传感器向系统申请采样频率,并按指定频率(或更低)生成事件。若读数未变化,可能不会生成事件。
设备传感器 可为其可接受的 平台传感器 采样频率范围提供 最小采样频率 和 最大采样频率。平台传感器 的 采样频率 不得低于 设备传感器 的 最小采样频率,也不得高于其 最大采样频率。
平台传感器的 采样频率 依据其关联 集合中的激活传感器对象 [[frequency]]
值决定。具体逻辑为 实现自定义,但值必须落入 平台传感器的 传感器类型 最小和最大采样频率,以及 设备传感器最小和最大采样频率的范围内。
注: 例如,用户代理可取一组 [[frequency]]
的最小公倍数(Least Common Denominator, LCD)作为采样频率,并受限于底层平台给定的采样频率范围。
上报频率指具体 Sensor 对象上 "reading"
事件的触发频率。
Sensor 对象获取新 读数
的速率不可能高于用户代理自平台所获得的速率,因此上报频率不会超过平台传感器的 采样频率,后者同样不超过设备传感器(如有定义)的最大采样频率。
上报频率与Sensor对象的[[frequency]]属性的值有以下差异情形:
-
请求的
[[frequency]]超出 获得平台传感器采样边界 方法返回的范围。 -
操作系统和/或 设备传感器 通过硬件或系统层过滤器自动丢弃与前一次上报差异不大的读数(无论绝对还是相对尺度)。
5.5. 暴露传感器读数的条件
仅当以下所有条件成立时,用户代理 可以向 Document
document 暴露传感器读数:
注: 除上述条件外,还需要注意,各 Sensor 子类会在构造器中调用 检查传感器策略控制特性 操作,且 §7.1.7
Sensor.start() 会调用 请求传感器访问。这些检查共同组成了 §4.2 缓解策略
中描述的防护措施。
注: 为释放硬件资源,用户代理可请求底层 平台传感器 在不允许暴露读数之前暂停上报。
6. 模型
6.1. 传感器类型
一个传感器类型必须具有关联的数据如下:
一个传感器类型还可以有如下关联数据:
6.2. 传感器
每当为某平台传感器得到新的传感器读数且 用户代理可以向当前可导航对象的活动文档暴露传感器读数时, 用户代理应调用更新最新读数,传入该平台传感器及传感器读数为参数。
最新读数的映射 包含一个条目,其键为 “timestamp”,其值为高精度时间戳,表示以毫秒为单位的读数时间戳, 由 不安全当前时间估算获得。
最新读数["timestamp"] 初始值为null,除非最新读数映射缓存了之前的读数。
该映射的其它条目值则保存了 平台传感器所测量各个量的数值。 这些条目的键必须与属性标识符相匹配, 这些属性由传感器类型关联的扩展传感器接口定义。 当属性的getter方法被调用时, 可以通过调用从最新读数获取值, 传递实现扩展传感器接口的对象及其属性标识符作为参数即可获得其值。
此示例展示了上述模型的一种可能实现方式。
在下图中,多个来自两个不同浏览上下文的激活的
Sensor
对象与同一个设备传感器交互。

7. API
7.1. Sensor 接口
[SecureContext ,Exposed =(DedicatedWorker ,Window )]interface :Sensor EventTarget {readonly attribute boolean ;activated readonly attribute boolean ;hasReading readonly attribute DOMHighResTimeStamp ?;timestamp undefined ();start undefined ();stop attribute EventHandler ;onreading attribute EventHandler ;onactivate attribute EventHandler ; };onerror dictionary {SensorOptions double ; };frequency
具体的 Sensor
对象还关联一个传感器类型,即拥有将其接口包含在其扩展传感器接口集合中的传感器类型。
相关事件处理器事件类型以及 Sensor 接口对应的事件处理器属性定义请参见事件处理器章节。
navigator. permissions. query({ name: 'accelerometer' }). then( result=> { if ( result. state=== 'denied' ) { console. log( 'Permission to use accelerometer sensor is denied.' ); return ; } let acl= new Accelerometer({ frequency: 30 }); let max_magnitude= 0 ; acl. addEventListener( 'activate' , () => console. log( 'Ready to measure.' )); acl. addEventListener( 'error' , error=> console. log( `Error: ${ error. name} ` )); acl. addEventListener( 'reading' , () => { let magnitude= Math. hypot( acl. x, acl. y, acl. z); if ( magnitude> max_magnitude) { max_magnitude= magnitude; console. log( `Max magnitude: ${ max_magnitude} m/s2` ); } }); acl. start(); });
7.1.1. 传感器生命周期
注意:上图中的节点代表Sensor
对象的状态,切勿与底层平台传感器或设备传感器的可能状态混淆。
7.1.2. 传感器垃圾回收
Sensor对象,其[[state]]
为"activating"时,只要注册了“activated”、“reading”或“error”事件监听器,则不得被垃圾回收。
Sensor对象,其[[state]]
为"activated"时,只要注册了“reading”或“error”事件监听器,则不得被垃圾回收。
当Sensor
对象,其[[state]]
为"activated"或"activating"被垃圾回收时,用户代理必须用此对象作为参数调用停用传感器对象。
7.1.3. 传感器内部插槽
Sensor
的实例创建时包含以下表格里描述的内部插槽:
| 内部插槽 | 描述(非规范性) |
|---|---|
[[state]]
| Sensor
对象的当前状态,可为"idle"、"activating"或"activated"之一。
初始为"idle"。
|
[[frequency]]
| 用于计算关联平台传感器采样频率的Hz(双精度浮点数),
并作为本Sensor
对象的报告频率上限。初始值为null。
|
[[lastEventFiredAt]]
| 最近一次为观察者发出的传感器读数的高精度时间戳, 单位为自时间原点起的毫秒数。 初始为null。 |
[[pendingReadingNotification]]
| 布尔值,表示新的传感器读数上报后, 是否需要通知观察者。初始为false。 |
7.1.4. Sensor.activated
7.1.5. Sensor.hasReading
hasReading
getter 步骤如下:
7.1.6. Sensor.timestamp
timestamp
getter 步骤如下:
7.1.7. Sensor.start()
start()
方法步骤如下:
-
并行运行以下子步骤:
-
如permissionState为"denied",则:
-
令e为创建一个"
NotAllowedError"DOMException。 -
返回。
-
-
如connected为false,则
-
令e为创建一个"
NotReadableError"DOMException。 -
返回。
-
7.1.8. Sensor.stop()
7.1.9. Sensor.onreading
onreading
是EventHandler
,用于通知有新的读数可用。
7.1.10. Sensor.onactivate
onactivate
是EventHandler
,当this.[[state]]
从"activating"转变为"activated"时被调用。
7.1.11. Sensor.onerror
onerror
是EventHandler
,每当抽象操作或IDL操作发生无法同步处理的错误时调用。
7.1.12. 事件处理器
下面列出了对象实现Sensor接口时必须支持的事件处理器(及其对应事件类型):
| 事件处理器 | 事件类型 |
|---|---|
onreading
| reading
|
onactivate
| activate
|
onerror
| error
|
7.2. SensorErrorEvent 接口
[SecureContext ,Exposed =(DedicatedWorker ,Window )]interface :SensorErrorEvent Event {(constructor DOMString ,type SensorErrorEventInit );errorEventInitDict readonly attribute DOMException error ; };dictionary :SensorErrorEventInit EventInit {required DOMException ; };error
SensorErrorEvent
实例的构造步骤遵循
Event
的构造器步骤。
error 属性必须返回初始化时设置的值。它表示传递给
SensorErrorEventInit
的 DOMException
对象。
8. 抽象操作
8.1. 初始化传感器对象
- 输入
-
sensor_instance,一个
Sensor对象。options,一个
SensorOptions字典实例。 - 输出
-
无
-
对 options 的每一项 key → value 执行如下操作
-
-
将 sensor_instance.
[[frequency]]设置为 options["frequency"]。
注意: 不保证能够满足请求的 options["
frequency"]。 有关可能适用的约束可参见 § 5.4 采样频率与报告频率。 -
8.2. 检查传感器策略控制特性
8.3. 连接传感器
-
令 platformSensor 为 null。
-
令 type 为 sensor 关联的传感器类型。
-
令 virtualSensorType 为 sensor 关联的虚拟传感器类型,若未设置则为 null。
-
如果 virtualSensorType 不为 null 且 topLevelTraversable 的虚拟传感器映射 包含 virtualSensorType:
-
否则:
-
如果 platformSensor 为 null,返回 false。
-
令 bounds 为调用 获取平台传感器的采样边界 (platformSensor) 得到的结果。
-
如果 sensor.
[[frequency]]为 null,则将其设为根据 type 实现定义的值。 -
如果 sensor.
[[frequency]]小于 bounds[0],则设为 bounds[0]。 -
如果 sensor.
[[frequency]]大于 bounds[1],则设为 bounds[1]。 -
返回 true。
8.4. 激活传感器对象
- 输入
-
sensor_instance,一个
Sensor对象。 - 输出
-
无
8.5. 停用传感器对象
- 输入
-
sensor_instance,一个
Sensor对象。 - 输出
-
无
-
从与 sensor_instance 相关的任务队列以及 传感器任务源 中移除所有与 sensor_instance 有关的任务。
-
令 sensor 为与 sensor_instance 关联的 平台传感器。
-
如 sensor 的激活的传感器对象集合包含 sensor_instance,则:
-
调用 设置传感器配置,以 sensor 为参数。
-
将 sensor_instance.
[[pendingReadingNotification]]设为 false。 -
将 sensor_instance.
[[lastEventFiredAt]]设为 null。
8.6. 通用传感器权限撤销算法
- 输入
-
permissionName,一个强大功能名称
- 输出
-
无
8.7. 设置传感器配置
- 输入
-
platformSensor,一个 平台传感器。
- 输出
-
无
-
将 platformSensor 的采样频率设为其激活的传感器对象集合 中各项
[[frequency]]值实现定义的方式计算出。 -
令 bounds 为调用 获取平台传感器的采样边界 (platformSensor) 得到的结果。
8.8. 更新最新读数
8.9. 报告最新读数已更新
- 输入
-
sensor_instance,一个
Sensor对象。 - 输出
-
无
-
如 sensor_instance.
[[pendingReadingNotification]]为 true,-
返回。
-
-
将 sensor_instance.
[[pendingReadingNotification]]设为 true。 -
令 lastReportedTimestamp 为 sensor_instance.
[[lastEventFiredAt]]的值。 -
如 lastReportedTimestamp 未设值
-
排入任务队列,调用 通知新读数,参数为 sensor_instance。
-
返回。
-
-
断言:sensor_instance.
[[frequency]]不为 null。 -
断言:sensor_instance.
[[frequency]]大于 0。 -
令 reportingInterval = 1 / sensor_instance.
[[frequency]]。 -
令 timestampDelta = 最新读数["timestamp"] − lastReportedTimestamp。
-
如 timestampDelta 大于或等于 reportingInterval
-
排入任务队列,调用 通知新读数,参数为 sensor_instance。
-
返回。
-
-
令 deferUpdateTime = reportingInterval − timestampDelta。
-
循环事件循环,持续时间为 deferUpdateTime。
-
如 sensor_instance.
[[pendingReadingNotification]]仍为 true,-
排入任务队列,调用 通知新读数,参数为 sensor_instance。
-
8.10. 通知新读数
- 输入
-
sensor_instance,一个
Sensor对象。 - 输出
-
无
-
将 sensor_instance.
[[pendingReadingNotification]]设为 false。 -
将 sensor_instance.
[[lastEventFiredAt]]设为 最新读数["timestamp"]。 -
触发一个名为 "reading" 的事件,目标为 sensor_instance。
8.11. 通知激活状态
- 输入
-
sensor_instance,一个
Sensor对象。 - 输出
-
无
-
将 sensor_instance.
[[state]]设为 "activated"。 -
触发一个名为 "activate" 的事件,目标为 sensor_instance。
-
令 sensor 是与 sensor_instance 关联的 平台传感器。
-
如果 sensor 的 最新读数["timestamp"] 不为 null,
-
排入任务队列,运行 通知新读数,参数为 sensor_instance。
-
8.12. 通知错误
- 输入
-
sensor_instance,一个
Sensor对象。error,一个
DOMException。 - 输出
-
无
-
将 sensor_instance.
[[state]]设为 "idle"。 -
触发一个名为 "error" 的事件,目标为 sensor_instance,事件类型为
SensorErrorEvent并将其error属性初始化为 error。
8.13. 从最新读数获取值
-
若 sensor_instance.
[[state]]为 "activated", -
否则,返回 null。
8.14. 请求传感器访问
8.15. 焦点与源检查
- 输入
-
document,一个
Document。 - 输出
-
布尔值。
9. 自动化
通用传感器 API 及其扩展规范给测试作者带来了挑战,因为要完整地测试这些接口需要可预测响应的物理硬件设备。 为了解决这一问题,本规范定义了一些[WEBDRIVER2] 扩展命令,允许定义和控制行为类似于设备传感器的虚拟传感器。这些虚拟传感器代表具有特定属性的设备,其读数可完全由用户定义。
9.1. 虚拟传感器
虚拟传感器 以受控方式模拟设备传感器的行为。它会向零个或多个与之连接的平台传感器上报传感器读数。
虚拟传感器有以下关联数据:
-
可供读数标志(布尔值)。
-
请求的采样频率(数字)。就像普通设备传感器的实际采样频率是不可见的一样,虚拟传感器的请求的采样频率也是一个由实现定义、在最小采样频率 和最大采样频率界定范围内的值。 如果虚拟传感器未向任何平台传感器提供读数,则其请求的采样频率为 0。
注意: 请求的采样频率的值会依赖于连接的平台传感器是否请求了特定的采样频率(且每个平台传感器的频率可能各不相同),或是它们是否轮询虚拟传感器,后者情况下可能根本没有采样频率的需求。
虚拟传感器类型 是表示某一类型传感器的字符串。
按类型虚拟传感器元数据 是 有序映射,将虚拟传感器类型 映射到虚拟传感器元数据。其初始值为空,扩展规范应为其定义一个或多个映射项,对应其定义的传感器类型。
每个顶层可遍历对象都拥有一个虚拟传感器映射, 其为有序映射 ,将虚拟传感器类型映射到 虚拟传感器。
注意: 虚拟传感器映射 结构体 保存了同一类型所有虚拟传感器共有的数据。虚拟传感器自身的数据在每次创建和使用时都可变化。
注意: 虚拟传感器映射绑定于顶层可遍历对象,而非任何导航对象, 因为在同一顶层可遍历对象下的所有平台传感器 (同一导航对象内的传感器类型)都应连接至同一个虚拟传感器。 这样更贴近现实世界的情况,即同一个硬件(或融合)传感器会为不同导航对象提供读数。
注意: 该机制还便于通过web-platform-tests测试本规范, 因为所有 WebDriver 通信皆由承载测试工具的帧发出。
9.2. 扩展命令
9.2.1. 创建虚拟传感器
| HTTP 方法 | URI 模板 |
|---|---|
| POST | /session/{session id}/sensor |
该扩展命令用于创建某种传感器类型的新虚拟传感器。对于同一传感器类型的
Sensor 实例,调用 start()
时会让该 虚拟传感器 作为其底层 设备传感器,直到执行
§ 9.2.4 删除虚拟传感器。
注意:该 扩展命令 的工作方式允许同类型的 Sensor 实例
共存且可拥有不同的 设备传感器。在调用本扩展命令之前,一个 Sensor
sensor 可能已经被创建且连接到真实硬件传感器。它会继续从硬件传感器获取读数,只有在再次调用 connect to sensor 时,
才会切换到从虚拟传感器接收读数。
| 参数名 | 值类型 | 是否必需 |
|---|---|---|
"type"
| String
| 是 |
"connected"
| Boolean
| 否 |
"maxSamplingFrequency"
| Number
| 否 |
"minSamplingFrequency"
| Number
| 否 |
-
令 virtualSensorType 为调用获取属性 "
type" 从 parameters 获得的值。 -
如果 virtualSensorType 不是
String, 则返回 错误,WebDriver 错误码为 invalid argument。 -
如果 按类型虚拟传感器元数据不包含 virtualSensorType,则返回 错误,WebDriver 错误码为 invalid argument。
-
如果 topLevelVirtualSensorMapping 包含 virtualSensorType,则返回错误,WebDriver 错误码为 invalid argument。
-
令 connected 为调用带默认值获取属性,参数 "
connected" 和 true,作用于 parameters。 -
令 maxSamplingFrequency 为调用带默认值获取属性,参数为 "
maxSamplingFrequency" 和实现定义的值,作用于 parameters。 -
如果 maxSamplingFrequency 不是
Number或值为 NaN、+∞ 或 −∞,则返回 错误,WebDriver 错误码为 invalid argument。 -
令 minSamplingFrequency 为调用带默认值获取属性,参数为 "
minSamplingFrequency" 和实现定义的值,作用于 parameters。 -
如果 minSamplingFrequency 不是
Number或值为 NaN、+∞ 或 −∞,则返回 错误,WebDriver 错误码为 invalid argument。 -
如果 minSamplingFrequency 大于 maxSamplingFrequency,则返回 错误,WebDriver 错误码为 invalid argument。
-
新建 virtualSensor,类型为虚拟传感器。
-
将 virtualSensor 的 可供读数标志设置为 connected。
-
将 virtualSensor 的 最小采样频率设置为minSamplingFrequency。
-
将 virtualSensor 的 最大采样频率设置为maxSamplingFrequency。
-
将 virtualSensor 赋值给 topLevelVirtualSensorMapping[virtualSensorType]。
-
返回 成功,数据为
null。
/session/23/sensor 发送 POST,内容如下:
{ "type" : "ambient-light" , "maxSamplingFrequency" : 60 , "minSamplingFrequency" : 5 }
注意:在同一个传感器类型下, 顶层可遍历对象只能创建一个虚拟传感器,否则将返回invalid argument WebDriver 错误码。
9.2.2. 获取虚拟传感器信息
| HTTP 方法 | URI 模板 |
|---|---|
| GET | /session/{session id}/sensor/{type} |
该扩展命令用于获取通过§ 9.2.1 创建虚拟传感器创建的 某个虚拟传感器的信息。
成功时,success返回的数据对象类型为 Object,
属性如下:
| 属性名 | 值类型 | 描述(非规范性) |
|---|---|---|
"requestedSamplingFrequency"
| Number
| 该虚拟传感器的请求的采样频率 |
注意:有关采样频率的更多约束和请求的采样频率为何是实现定义的值的说明, 请见 § 5.4 采样频率与报告频率及 § 9.1 虚拟传感器。一般情况下,只能保证该值处于虚拟传感器的最小采样频率与最大采样频率之间。
-
令 virtualSensorType 为
typeurl 变量的值。 -
如果 topLevelVirtualSensorMapping 不包含 virtualSensorType,则返回错误,WebDriver 错误码为 invalid argument。
-
令 virtualSensor 为 topLevelVirtualSensorMapping[virtualSensorType]。
-
新建 info,类型为
Object。 -
调用设置属性,在 info 上设 "
requestedSamplingFrequency" 属性值为 virtualSensor 的请求的采样频率。 -
返回 success,数据为 info。
9.2.3. 更新虚拟传感器读数
| HTTP 方法 | URI 模板 |
|---|---|
| POST | /session/{session id}/sensor/{type} |
注意: 虚拟传感器的行为类似于设备传感器,所以这里产生的传感器读数还需经过平台传感器的处理,例如可能会因传感器类型的阈值检查算法或是否可暴露传感器读数的结果而被丢弃。
| 参数名 | 值类型 | 是否必需 |
|---|---|---|
"reading"
| Object
| 是 |
-
令 reading 为调用获取属性 "
reading" 从 parameters 获得的值。 -
如果 reading 不是
Object类型,则返回 错误,WebDriver 错误码为 invalid argument。 -
令 virtualSensorType 为
typeurl 变量的值。 -
如果按类型虚拟传感器元数据不包含virtualSensorType,则返回错误,WebDriver 错误码为invalid argument。
-
令 metadata 为 按类型虚拟传感器元数据[virtualSensorType]。
-
如果 topLevelVirtualSensorMapping 不包含 virtualSensorType,则返回错误,WebDriver 错误码为 invalid argument。
-
令 virtualSensor 为 topLevelVirtualSensorMapping[virtualSensorType]。
-
令 parsedReading 为调用 metadata 的 读数解析算法,以 reading 为参数的结果。
-
如 parsedReading 为 undefined,则返回 错误,WebDriver 错误码为 invalid argument。
-
以实现定义的方式,让 parsedReading 可被连接到 virtualSensor 的平台传感器获取。
-
返回 success,数据为
null。
9.2.3.1. 解析读数的算法
本规范定义了一些可供扩展规范在为虚拟传感器元数据 (用于按类型虚拟传感器元数据)定义时使用的算法。
9.2.3.1.1. 解析单值数字读数
9.2.3.1.2. 解析 XYZ 读数
-
令 x 为调用获取属性,从parameters取"
x"的结果。 -
如果 x 不是
Number或其值为 NaN、+∞ 或 −∞,则返回 undefined。 -
令 y 为调用获取属性,从parameters取"
y"的结果。 -
如果 y 不是
Number或其值为 NaN、+∞ 或 −∞,则返回 undefined。 -
令 z 为调用获取属性,从parameters取"
z"的结果。 -
如果 z 不是
Number或其值为 NaN、+∞ 或 −∞,则返回 undefined。 -
令reading为新建的传感器读数。
-
设置 reading["
x"] = x。 -
设置 reading["
y"] = y。 -
设置 reading["
z"] = z。 -
返回 reading。
9.2.4. 删除虚拟传感器
| HTTP 方法 | URI 模板 |
|---|---|
| DELETE | /session/{session id}/sensor/{type} |
-
令 virtualSensorType 为
typeurl 变量的值。 -
如果按类型虚拟传感器元数据不包含virtualSensorType,则返回 错误,WebDriver 错误码为 invalid argument。
-
移除 topLevelVirtualSensorMapping[virtualSensorType]。
-
返回 success,数据为
null。
注意:当一个正在使用的设备传感器不可用(例如被物理断开,
或因非用户代理的原因停止)时,平台传感器和
Sensor
实例的行为未指定。实现可以选择保持现有Sensor实例不变
(它们将不会再上报新读数),停用传感器对象,
或让平台传感器上报错误,
最终触发通知错误。
10. 可扩展性
本节为非规范性内容。
注意:本节及其子节使用规范性语言为扩展规范的 作者提供指导。对于实现者来说,本节及其子节均视为非规范性内容。
本节描述了如何扩展本规范以为不同的传感器类型指定 API。
此类扩展规范鼓励聚焦于单一传感器类型, 并按需暴露高、低级别能力。
扩展规范
可以显式为对应传感器类型定义
局部坐标系,或使其在每个Sensor对象上可配置。
要获取 扩展规范的最新列表,请参阅 [GENERIC-SENSOR-USECASES] 与 [MOTION-SENSORS] 文档。
10.1. 安全与隐私
扩展规范 应当:
-
遵循通用缓解策略,
-
考虑针对具体情况的缓解策略,
-
参考《安全与隐私自查问卷》 [SECURITY-PRIVACY-QUESTIONNAIRE] 进行评估,
-
尤其要评估 同源策略被绕开带来的风险,特别是传感器若暴露了新的、未受同源策略约束的通信通道。
10.2. 命名
面向低级传感器的
Sensor 接口应以
其所关联平台传感器命名。
例如,与陀螺仪关联的接口就应命名为 Gyroscope。
面向高级传感器的
Sensor 接口
应以该平台传感器测量的物理量名加 Sensor 后缀命名。
例如,测量距离的 平台传感器
相关接口可命名为 ProximitySensor。
Sensor
子类中保存传感器读数值的属性名
应以其值的完整含义命名。例如 Thermometer 接口应包含 传感器读数值的 temperature 属性(而不是 value 或
temp)。
可参考 QUDT
[QUDT] 的命名实践。
10.3. 单位
根据 TAG API 设计原则 [API-DESIGN-PRINCIPLES] ,所有时间测量均应采用毫秒为单位。 其它单位应优先按以下顺序采用(除了温度推荐用摄氏而非开尔文): 国际单位制(SI)、SI 派生单位、SI 可接受的非SI单位。参见 SI Brochure [SI]。
10.4. 暴露高级与低级传感器
截至目前,暴露传感器给网页平台的各种规范 主要侧重于高级传感器 API。参见 [GEOLOCATION-API] , [ORIENTATION-EVENT]
之所以采用此做法,原因包括: 高级传感器:
-
能够清晰传达开发者意图,
-
无需了解底层硬件工作原理,
-
易于使用,
-
有助于用户代理在性能与电池续航上做大量优化,
-
通过减少暴露的信息量和类型,有助于避免部分隐私与安全问题。
但越来越多的场景(如虚拟现实、增强现实) 出于性能等考虑需要低级别的传感器访问能力。
提供低级别访问权 有助于 Web 应用开发者结合领域属性进行更高性能系统设计。
根据 Extensible Web Manifesto 的理念 [EXTENNNNSIBLE] ,扩展规范 应重点暴露低级传感器 API; 但如果有明显优势,也应暴露高级API。
10.5. 不该开启同类型多传感器的情形?
不建议为同一传感器类型
用完全相同参数创建多个Sensor实例,
否则会消耗不必要的硬件资源。
如果有多个观察者希望接收新传感器读数的通知,可以在一个
Sensor 实例上添加事件监听器,
而无需为同一传感器类型创建多个实例并使用简单的 onreading
事件处理器。
反过来,如须用不同设置(如
frequency、
精度等,由扩展规范定义的设置),
可以为同类传感器类型创建多个
Sensors 实例。
10.6. 定义要求
扩展规范必须定义 § 6.1 传感器类型中列明的所有关联数据。
本节给出扩展规范 需指明的部分关联数据详细说明。
-
扩展传感器接口,即其继承接口包含
Sensor的 接口。 扩展传感器接口必须可被构造。它的 [Constructor] 参数 必须为一个可选字典,其继承字典包含SensorOptions。扩展传感器接口有一个支持选项集合,称为支持的传感器选项。 除非扩展规范另有规定,支持的传感器选项 仅包含一个条目 "frequency"。
如用户代理不支持对应选项,必须从支持的传感器选项中移除该选项。
扩展传感器接口暴露传感器读数的 属性必须是只读的,其 getter 必须返回传入 this 和属性 标识符后,调用从最新读数获取值的结果。
-
一个字典,其继承字典包含
SensorOptions。 -
一个默认传感器。 一般情况下,每种类型设备只配备一个 平台传感器, 因此定义默认传感器较为直接。若某类型常存在多个设备传感器, 扩展规范可选择不定义默认传感器,尤其不合理时。
10.7. 自动化
为了支持用户代理自动化与应用测试,扩展规范鼓励:
-
在按类型虚拟传感器元数据中添加一个或多个条目。
-
据此定义一个或多个虚拟传感器元数据实例。
-
指定传感器类型的虚拟传感器类型,其值应与对应按类型虚拟传感器元数据条目的键保持一致。
距离传感器 是一个传感器类型,只关联一个 扩展传感器接口ProximitySensor。其关联的虚拟传感器类型是 "proximity"。[...]
距离读数解析算法,给定一个 JSON
Objectparameters,必须调用解析单值数字读数,参数为 parameters 和 "distance"。按类型虚拟传感器元数据 映射必须包含一个键为"
proximity"的条目, 其值是一个虚拟传感器元数据,其读数解析算法 即 距离读数解析算法。
10.8. 扩展 Permission API
每个 Sensor 接口对应的
传感器类型
必须用其关联的 读数 来保护相应的
名称
或 PermissionDescriptor。
低级 sensor
可以直接用接口名作为 名称,例如 "gyroscope" 或 "accelerometer"。
融合传感器必须请求访问用作融合源的所有传感器的权限。
即使从融合数据重建 低级 传感器读数较为困难,但部分原始信息仍可能被推断。 例如,若使用绝对或地磁朝向传感器,很容易推断用户在空间中的朝向,因此这类传感器必须 请求使用 magnetometer 权限, 因为其暴露了设备与地磁场的相对位置。而相对朝向传感器则不会暴露这类信息,因此不需 请求使用 magnetometer。
PermissionDescriptor
也可用于设置最大精度或采样频率等限制。下例为加速度计权限扩展的一个可能实现:
dictionary AccelerometerPermissionDescriptor :PermissionDescriptor {boolean highAccuracy =false ;boolean highFrequency =false ; };
10.9. 扩展 Permissions Policy API
每个 Sensor 接口对应的 传感器类型
(若未进行传感器融合时为1个,否则为多个)
需对应一个或多个策略控制功能,用于控制是否允许文档使用该接口。
注意: 默认允许列表设置为 'self',
允许同源嵌套框架使用 Sensor
接口,
但禁止第三方内容访问 传感器读数。
低级 sensor 可以用接口名作策略控制功能令牌,
如 "gyroscope" 或 "accelerometer"。
若扩展规范
未作修改,传感器功能名等于其类型对应权限名。
allow
属性实现:
< iframe src = "https://third-party.com" allow = "accelerometer" /></ iframe >
< iframe src = "https://third-party.com" allow = "accelerometer; magnetometer; gyroscope" />
10.10. 示例 WebIDL
以下是本规范针对距离传感器扩展的 WebIDL 示例:
[SecureContext ,Exposed =Window ]interface ProximitySensor :Sensor {constructor (optional ProximitySensorOptions proximitySensorOptions = {});readonly attribute double ?distance ; };dictionary ProximitySensorOptions :SensorOptions {double min ;double max ;ProximitySensorPosition position ;ProximitySensorDirection direction ; };enum ProximitySensorPosition {"top-left" ,"top" ,"top-right" ,"middle-left" ,"middle" ,"middle-right" ,"bottom-left" ,"bottom" ,"bottom-right" };enum ProximitySensorDirection {"front" ,"rear" ,"left" ,"right" ,"top" ,"bottom" };
11. 致谢
首先,衷心感谢 Anssi Kostiainen 在本规范开发过程中持续地投入和支持, 还要感谢 Mikhail Pozdnyakov、Alexander Shalamov、Rijubrata Bhaumik 和 Kenneth Rohde Christiansen 在实现反馈、建议与研究上的宝贵贡献,这些都极大推动了规范的完善。
特别感谢 Rick Waldron,他推动了 Web 通用传感器 API 设计的讨论, 勾画了本规范的最初 API,并基于他在 Johnny-Five 项目的工作不断反馈实现建议, 在整个规范开发过程中持续贡献观点。
特别感谢 Boris Smus、Tim Volodine 与 Rich Tibbett 早期将传感器以一致方式暴露给 Web 所做的工作。
感谢 Anne van Kesteren 不仅面对面也通过 IRC 持续地热心协助。
感谢 Domenic Denicola 和 Jake Archibald 的帮助。
还要感谢 Frederick Hirsch 和 Dominique Hazaël-Massieux(通过 HTML5Apps 项目) 在管理和技术层面对本规范的支持。
感谢 Tab Atkins 制作 Bikeshed 并细致讲解其细节。
感谢 Lukasz Olejnik 和 Maryam Mehrnezhad 为隐私与安全相关内容贡献力量。
以下各位在 GitHub 上反复讨论、深度参与,本规范极大受益于他们的贡献: Anssi Kostiainen、 Boris Smus、 chaals、 Claes Nilsson、 Dave Raggett、 David Mark Clements、 Domenic Denicola、 Dominique Hazaël-Massieux(通过 HTML5Apps 项目)、 Francesco Iovine、 Frederick Hirsch、 gmandyam、 Jafar Husain、 Johannes Hund、 Kris Kowal、 Lukasz Olejnik、 Marcos Caceres、 Marijn Kruisselbrink、 Mark Foltz、 Mats Wichmann、 Matthew Podwysocki、 Olli Pettay、 pablochacin、 Remy Sharp、 Rich Tibbett、 Rick Waldron、 Rijubrata Bhaumik、 robman、 Sean T. McBeth、 Tab Atkins Jr.、 Virginie Galindo、 zenparsing、 以及 Zoltan Kis。
我们还要感谢 Anssi Kostiainen、 Dominique Hazaël-Massieux、 Erik Wilde、 和 Michael[tm] Smith 对编辑工作提供的建议和帮助。