穿戴设备微信小程序框架

1. 产品介绍

微信小程序框架已适配手表穿戴类设备,可针对 RTOS(实时操作系统)等低功耗设备场景,实现最小可用的微信小程序运行。

2. 技术方案

由于市场主流穿戴设备的 CPU 性能和内存大小十分有限(整机内存小于 8M),我们针对性实现了最小可用的微信小程序框架,提供:

  1. 熟悉的 WXML + WXSS + JS 的微信小程序开发体验
  2. 最必要的微信小程序 wx 接口
  3. 基本完整的微信小程序页面框架和组件系统
  4. 基础的 WXSS 样式和内置组件支持
  5. 微信小程序代码包编译器
  6. 模拟调试环境

穿戴设备的硬件资源十分有限。为减小存储占用,该微信小程序框架只提供普通微信小程序的功能子集,请开发者严格按照本文档提供的特性列表实现微信小程序。由于接口能力的差异较大,一般需要开发者针对穿戴设备单独开发新的微信小程序。

2.1 UI能力

2.1.1 组件框架

可以使用微信小程序中 Page、Component能力。支持完整的 WXML、WXSS 语法。

2.1.1.1 支持的特性

可以在微信小程序中使用 App、getApp、Page、getCurrentPages、Component。支持 ES6 import / export 与 CommonJS require 两种 JS 模块加载机制。

2.1.1.2 使用差异

使用微信小程序组件框架时应注意下列差异:

  • 组件的 styleIsolation 需在 JSON 中配置,不支持在组件 options 中指定
  • 不再支持 addGlobalClass,请使用 styleIsolation 代替
  • 当使用组件间通信与事件时,可以直接定义 export 属性,不需要声明 wx://component-export
2.1.1.3 暂未支持的特性

以下能力暂未支持

  • Chaining API

  • behavior

  • WXS

  • 暂不支持自定义组件的下列方法:

    • createSelectorQuery
    • createIntersectionObserver
    • createMediaQueryObserver
    • getTabBar
    • animate
    • clearAnimation
    • applyAnimatedStyle
    • clearAnimatedStyle
    • setUpdatePerformanceListener

2.1.2 内置组件

目前仅支持下列内置组件

2.1.2.1 view

属性:无

2.1.2.2 image

属性:

属性 类型 默认值 必填 说明
src string 图片资源地址。如果使用代码包内图片资源,src需为绝对路径。

说明:

  • 不建议显示高清图片。部分穿戴设备的屏幕分辨率边长仅 200~300 像素,且内存有限,过大图片无法清晰渲染且可能导致内存溢出。
  • 不建议使用图片组件显示微信小程序码、二维码、条形码等,建议使用 qrcode 组件。
2.1.2.3 text

属性:无

2.1.2.4 qrcode

穿戴设备微信小程序框架的专属组件,能够根据输入的文本生成并渲染二维码。

考虑到资源消耗,穿戴设备不建议使用图片展示二维码。
对于需要展示微信小程序码的情况,可以参考扫普通链接二维码打开微信小程序

属性 类型 必填 默认值 说明
value string “” 二维码文本内容
light-color HexColor #000000 二维码颜色
dark-color HexColor #FFFFFF 二维码背景颜色
2.1.2.5 scroll-view
属性 类型 默认值 必填 说明
enable-flex boolean false 启用 flexbox 布局。开启后,当前节点声明了 display: flex 就会成为 flex container,并作用于其孩子节点。
2.1.2.6 swiper/swiper-item

swiper 支持属性:

属性 类型 默认值 必填 说明
current number 0 当前所在滑块的 index
bind:change eventhandle current 改变时会触发 change 事件,event.detail = {current, source}

2.1.3 WXSS样式

  • display: block/flex(支持弹性盒子布局),不支持 inline/inline-block/inline-flex/grid
  • position 绝对布局(top、right、bottom、left)
  • width、height、margin、padding 等组件尺寸设置
  • color 等文本属性
  • border 相关属性:border-radius 不支持分别设置四角圆角大小,不支持百分比写法。
  • background 相关属性: background-image, background-color
  • font-size 等字体设置暂不支持

不支持子元素选择器、后代选择器、接续兄弟选择器、后续兄弟选择器,若有相关开发需求,请通过普通选择器实现。
注意:WXML支持内联样式,但不支持binding(不可以通过setData()修改WXML组件的style属性值)。

2.2 微信小程序配置

2.2.1 app.json

配置项:
属性 类型 必填 说明
entryPagePath string 微信小程序默认启动首页
pages string[] 页面路径列表
networkTimeout Object 网络超时时间
window Object 全局的默认窗口表现
usingComponents Object 全局自定义组件配置
window:

用于设置微信小程序的状态栏、导航条、标题、窗口背景色。穿戴设备微信小程序框架只支持设置窗口背景色。

属性 类型 必填 默认值 说明
backgroundColor HexColor #FFFFFF 窗口的背景色
networkTimeout

各类网络请求的超时时间,单位均为毫秒。穿戴设备微信小程序框架只支持设置request的超时时间。

属性 类型 必填 默认值 说明
request number 60_000 wx.request 的超时时间,单位:毫秒。

2.2.2 component.json

配置项
属性 类型 必填 说明
component boolean 一个自定义组件由 json、wxml、wxss、js 4个文件组成。如设置component为true,将声明这一组文件为自定义组件
styleIsolation string 样式隔离
usingComponents Object 全局自定义组件配置
styleIsolation
说明
isolated 启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值)
apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面
shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。

2.2.3 page.json

每一个微信小程序页面可以使用同名.json文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json 的 window 中相同的配置项。

配置项:
属性 类型 必填 默认值 说明
backgroundColor HexColor #FFFFFF 窗口的背景色

2.3 接口能力

  • JSAPI 调用方式与完整版一致,支持回调和 Promise 两种调用形式,详情请参考相关文档。
  • 本节仅说明和完整版的差异部分,完整的 API 接口说明请参考官网文档。为避免重复,本文省略参数中 success/fail/complete 回调函数的说明,如无特殊说明,均提供与完整版一致的 success/fail/complete 回调接口

2.3.1 微信生态能力

  • wx.login:一致
  • wx.checkSession: 一致
  • wx.getAccountInfoSync:一致

2.3.2 系统信息

  • wx.getWindowInfo:一致。额外增加返回值 screenShape。
  • wx.getDeviceInfo: 仅支持返回 brand、model、system、platform、cpuType、memorySize。额外增加返回值 deviceType。

wx.getWindowInfo 新增返回值

属性 类型 说明
screenShape string rect: 方形屏幕;round 圆形屏幕

wx.getDeviceInfo 新增返回值

属性 类型 说明
deviceType string 可选值:watch, phone, tv, vehicle, pc

注意:

本框架不提供wx.getSystemInfo系列接口,请使用getWindowInfo/getDeviceInfo代替。

2.3.3 网络请求

wx.request()

  • 参数仅支持 url、data、header、timeout、method(仅支持GET, POST)、dataType、responseType、redirect。
  • 返回的 RequestTask 仅支持 abort
  • success 回调参数仅支持 data、statusCode、header
  • fail 回调参数一致

注意

  • 目前暂未提供文件系统支持,故暂未支持 wx.downloadFile/wx.uploadFile。
  • 目前仅支持https,不支持TCP、UDP、WebSocket等其他类型的网络请求。

2.3.4 页面路由

  • wx.navigateTo:参数仅支持 url
  • wx.navigateBack:一致
  • wx.redirectTo:一致
  • wx.reLaunch:一致

注意:暂未提供tabBar

2.3.5 数据存储

  • wx.getStorageInfo(sync):一致
  • wx.setStorage(Sync):参数仅支持 key、data
  • wx.getStorage(Sync):参数仅支持 key
  • wx.removeStorage(Sync):一致
  • wx.clearStorage(Sync):一致

注意

  1. 目前只提供KV存储,暂不提供文件存储。
  2. 暂不支持加密存储

2.3.6 UI

  • wx.showToast:一致
  • wx.hideToast:参数不支持 noConflict,toast 和 loading 默认不可混用
  • wx.showLoading:一致
  • wx.hideLoading:参数不支持 noConflict,toast 和 loading 默认不可混用
  • wx.showModal:参数不支持 editable 和 placeholderText
  • wx.getMenuButtonBoundingClientRect:一致

注意:暂无 navigationBar、tabBar、homeButton

2.3.7 生命周期

  • wx.getLaunchOptionsSync:参数仅支持 path、scene、query
  • wx.getEnterOptionsSync:参数仅支持 path、scene、query
  • wx.onUnhandledRejection:一致
  • wx.onError:一致
  • wx.onPageNotFound:一致
  • wx.onAppShow:参数仅支持 path、scene、query
  • wx.onAppHide:一致

2.3.8 事件

  • bind:touchstart
  • bind:touchend
  • bind:tap
  • bind:longpress

其中事件对象仅支持 type, target, currentTarget 字段,target/currentTarget 仅支持 dataset 字段。

2.3.9 其他

  • console.log/info/error/warn/debug:输出会打印在模拟器的命令行窗口中,可以通过 console.log 调试微信小程序逻辑。
  • setTimeout/clearTimeout
  • setInterval/clearInterval

2.4 其他差异

  • 暂不支持微信小程序插件
  • 暂不支持微信小程序分包

3. 开发流程

3.1 注册穿戴设备微信小程序框架专属AppID

前置条件:名下需有普通微信小程序的AppID,并作为该微信小程序的管理员。
步骤:

  1. 打开微信开发者平台
  2. 扫码登录
  3. 点击「前往控制台」

前往控制台

  1. 选择「我的业务–微信小程序」

选择小程序

  1. 页面右上角,切换到穿戴设备微信小程序要挂靠的普通微信小程序。
  2. 切换到「硬件设置」
  3. 点击「申请创建」

申请创建

  1. 创建成功,获得「穿戴端AppID」

创建成功

3.2 安装开发者工具拓展

  1. 打开「微信开发者工具」(需使用版本号 ≥ 2.01.2507252,建议下载最新 nightly 版开发者工具进行使用)
  2. 在「工具栏」选择「设置–拓展设置」
  3. 在弹出的页面中,左侧选择「模拟器插件」,右侧选择安装「低功耗硬件微信小程序模式」

低功耗硬件小程序模式

3.3 启用低功耗微信小程序模式

装好插件后,将开发模式切换到「低功耗硬件微信小程序模式」

切换模式

3.4 开发调试

点击「详情」,确保其中设置的AppID与微信开发者平台一致。

详情

我们不建议开发者将JS代码编译为ES5。穿戴设备微信小程序框架支持ES2023,减少不必要的polyfill有助于显著节省最终微信小程序代码包体积,进而节省运行时内存占用。

3.5 体验与发布

完成开发后,可在「微信开发者工具」中点击「上传」。新上传的版本将覆盖旧的「开发版微信小程序」。
开发者可以前往微信开发者平台,管理微信小程序版本、提交审核、发布上线。

4. 联系我们

有合作意向的硬件厂商、微信小程序开发者,欢迎发送邮件联系我们:wx_iot@tencent.com

设备认证 TEE 规范

1. 背景

设备身份是微信 VOIP 业务能够正常运行的基础,开发者在接入 VOIP 业务时,会通过如下两个流程来进行身份的确定:

  1. 设备注册。通过 SN + modelId 两个维度来标定这台设备。
  2. 拿票据。在进行 VOIP 通话前,需要拿到这台设备所对应的票据。

对于没有 TEE 的机器,我们要求设备系统里能够操作 EMMC 存储的 RPMB 区域,并且需要将 RPMB 的归属权给到 VOIP 业务,VOIP 在设备注册时会将与设备对应的唯一密钥写入存储的 KEY 区域,用来做身份校验。

而对于有 TEE 的机器,我们信赖 TEE 的结果,但需要 TEE 里按照规范完成 TA 的开发。

2. TA 开发

不管是 optee,还是 trusty 或 qsee 等,TEE 的使用流程通常如下:

打开 TEE 环境 > 开启一个会话 > 发送命令 > 获取信息 > 结束会话 > 关闭 TEE 环境

这里我们需要定义如下标准:

  1. 会话名称。
  2. 命令的功能。
  3. 命令的交互数据定义。
  4. TA 里的运算逻辑

设备开发者需要根据规范进行如下开发:

  1. TA 开发,需要开发者或 tee 提供商按照规范开发 TA。
  2. HAL 开发,HAL 用于与 TA 的交互,微信的系统服务基于此 HAL。
  3. 服务集成,将微信发布的 rpmbd_tee 以系统服务方式运行起来。

TA 在逻辑上将存储分为两个区域。需要注意的是,这些数据最终应都存在于 EMMC 或 UFS 的 rpmb 分区,或其它 REE 访问不到的安全器件区域。

  1. 密钥区:32个字节。TA 代码逻辑里需要将此区域实现成只能写一次,类似于硬件上的 OTP (One Time Programmable) 区域。
  2. 数据区:单位为 Block,每个 Block 有 256 个字节,最小需要 32 个,开发者可根据实际情况来确定大小,若越界则返回相应错误码即可。Block 的地址从 0 开始。

2.1 会话名称

TA 的名称统一为 ta_devauth

2.2 命令定义

定义三个命令,分别是读数据、写数据、写密钥。

#define TA_DEVAUTH_CMD_READ   0x10
#define TA_DEVAUTH_CMD_WRITE  0x11
#define TA_DEVAUTH_CMD_PROKEY 0x12

详细说明:

命令 TA_DEVAUTH_CMD_READ 0x10
功能 读 1 个 Block 的数据
参数 输入:Block 地址
输入/输出:Block 数据 BUFFER,284字节,见 2.3 数据定义。
输出:签名 BUFFER,=32字节
返回 0:成功读取,并返回 284 字节数据和 32 字节签名
-1:参数错误。
-2:地址越界。
-3:密钥区还没被写。
-5:其它错误。
特别说明 若密钥区还没被写(例如裸数据全是0x00,或没有TEE文件系统里的文件?),这种情况认为是一台全新的未激活设备,需要读取错误返回 -3。
命令 TA_DEVAUTH_CMD_WRITE 0x11
功能 写 1 个 Block 的数据
参数 输入:Block 地址
输入:Block 数据 BUFFER,=284字节,见 2.3 数据定义。
输入:对应的签名 BUFFER,=32字节
返回 0:写入成功。
-1:参数错误。
-2:地址越界。
-3:密钥区还没被写。
-4:签名错误。
-5:其它错误。
特别说明 若密钥区还没被写(例如裸数据全是0x00,或没有TEE文件系统里的文件?),这种情况认为是一台全新的未激活设备,需要返回 -3。
此功能在写数据前,需要计算数据对应的签名,并且与参数传入的签名比对,若比对不成功返回 -4
命令 TA_DEVAUTH_CMD_PROKEY 0x12
功能 写密钥
参数 密钥 BUFFER,=32字节
返回 0:写入成功。
-1:参数错误。
-3:密钥区已有数据。
-5:其它错误。
特别说明 若密钥区已有数据(例如裸数据不为0x00,或有文件?),则需要返回 -3。

2.3 数据定义

与 TA 交互时传入传出 Buffer 的大小为 284 字节,它的定义如下:

struct ta_data {
    uint8_t  data[256];    // 此 256 字节是 TA 写入到1个Block的内容
    uint8_t  nonce[16];    // 一般为随机字节,传入与传出的一定要一致。
    uint32_t reserve1;
    uint16_t reserve2;
    uint16_t reserve3;
    uint16_t reserve4;
    uint16_t reserve5;
};

// sizeof(struct ta_data) = 284

签名 Buffer 大小为 32 字节


CA 与 TA 交互消息定义:

struct ta_message {
    uint32_t        cmd;        // 命令号
    uint32_t        block;      // 读写地址
    struct ta_data  data;       // 284 字节数据
    uint8_t         key[32];    // 32 字节 Key
    uint8_t         hmac[32];   // 32 字节 hmac
    int             ret;        // 返回值
};

CA 侧参考伪代码:

static int tee_send_cmd_req(struct ta_message* ta_msg) {
    int rc = 0;

    if (ca_handle == 0) {
        printf("not connected\n");
        return -EINVAL;
    }

    if (tee_send_msg(ca_handle, ta_msg) < 0) {
        return -1;
    }

    if (tee_resp_msg(ca_handle, ta_msg) < 0) {
        return -1;
    }

    return 0;
}

2.4 运算逻辑

数据签名的算法为 HMAC_SHA256。

读数据时,TA 的流程:

  1. 若密钥区没数据,返回 -3。
  2. 读地址参数对应 Block 的 256 字节数据。
  3. 将读到的 256 字节与输入参数里的 16 字节 noce 以及其它 reserve,一共有 284 个字节。
  4. 利用密钥区的 32 字节密钥,对 284 字节进行签名,并返回 284 字节以及签名。

写数据时,TA 的流程:

  1. 若密钥区没数据,返回 -3。
  2. 得到输入参数里的 284 字节,得到输入参数中的签名1。
  3. 利用密钥区的 32 字节密钥,对 284 字节进行签名,得到签名2。
  4. 比较签名1与签名2,如果相等则将 284 字节中的 256 字节写入对应的 Block,如果不等则返回错误 -4。

写密钥时,TA 的流程:

  1. 若密钥区已有数据,返回 -3。
  2. 将 32 字节的密钥写入密钥区。

一个签名的数据示例,开发者可以此为基准来验证自己的 hmac_sha256:

char *key = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH";
uint8_t buffer[284] = {0};

int main(int argc, char **argv) {
    uint8_t hmac[32] = {0};
    memset(buffer, 0x55, 284);

    hmac_sha256(key, 32,
      buffer, 284,
      hmac, sizeof(hmac)
    );

    for (int i=0; i < 32; i++) {
        printf("%02x ", hmac[i]);
    }
    return 0;
}
// 以上代码输出:
// 61 16 67 22 a0 93 66 74 bb 75 f8 87 0e 5e d4 59 2c d6 99 c0 14 a6 93 70 bd ff ea 3e 8e 84 52 4e 

hmac_sha256 为标准算法,一般 tee 里已有此类算法,如果没有,可参考开源实现

2.5 注意事项

  1. TEE 里的密钥区和数据区需保证具有 “永久存储” 特性,并且不应该被 REE 以任何方式直接访问,不会随用户的刷机、升级或其它常规行为而丢失。
  2. 密钥区域需要实现为 OTP 特性,即仅一次写入。
  3. 密钥区域无数据时,需要按规范返回错误码。
  4. 厂商需要将 CA 测试例程和代码给到微信,微信进行验收测试。

3. HAL 开发

按照 HAL 规范完成 HAL 的开发,HAL 里使用 CA 代码与 TA 进行交互,完成 TEE 的使用。

HAL 路径:android/hardware/interface/devauth

3.1 HAL 规范

types.hal

package android.hardware.devauth@1.0;

enum TA_CMD : uint32_t {
    TA_DEVAUTH_CMD_READ = 0x10,
    TA_DEVAUTH_CMD_WRITE = 0x11,
    TA_DEVAUTH_CMD_PROKEY = 0x12,
};

struct ta_data {
    uint8_t[256] data; // 此 256 字节是 TA 写入到1个Block的内容
    uint8_t[16]  nonce; // 一般为随机字节,传入与传出的一定要一致。
    uint32_t reserve1;
    uint16_t reserve2;
    uint16_t reserve3;
    uint16_t reserve4;
    uint16_t reserve5;
};

struct ta_message {
    uint32_t        cmd;        // 命令号
    uint32_t        block;      // 读写地址
    ta_data         data;       // 284 字节数据
    uint8_t[32]     key;        // 32 字节 Key
    HMacBuffer      hmac;       // 32 字节 hmac
    int8_t          ret;        // 返回值
};

typedef uint8_t[32] HMacBuffer;
typedef uint8_t[32] ProKeyBuffer;

typedef ta_data ta_data_t;
typedef ta_message ta_message_t;

IDevauth.hal

package android.hardware.devauth@1.0;

interface IDevauth {

    /**
     * 读 Block 数据
     *
     * @param addr:     Block 地址
     * @param data:     输入的 struct ta_data
     * @return retval:  返回值,返回值说明请见规范
     * @return data:    返回的 struct ta_data,284 字节。
     * @return hmac:    返回的签名, 32 字节。
     *
     */
    read_block(uint16_t addr, ta_data data) generates (int8_t retval, vec<uint8_t> data, vec<uint8_t> hmac); 

    /**
     * 写 Block 数据
     *
     * @param addr:     Block 地址
     * @param data:     输入输出数据,对应规范里的 struct ta_data,284 字节。
     * @param hmac:     HAL 写数据时用的签名, 32 字节
     *
     * @return retval:  返回值说明请见规范
     */
    write_block(uint16_t addr, ta_data data, HMacBuffer hmac) generates (int8_t retval); 

    /**
     * 写 Key
     *
     * @param key:      32 字节key
     *
     * @return retval:  返回值说明请见规范
     */
    program_key(ProKeyBuffer key) generates(int8_t retval);
};

3.2 开发参考

hardware/interfaces/devauth/1.0/ 下放置 IDevauth.haltypes.hal,内容如上。

然后使用如下方式来生成代码:

LOC=hardware/interfaces/devauth/1.0/default
PACKAGE=android.hardware.devauth@1.0
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport ${PACKAGE}
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport ${PACKAGE}
./hardware/interfaces/update-makefiles.sh

此时 hal 目录应该如下:

root~/android> tree hardware/interfaces/devauth/
hardware/interfaces/devauth/
└── 1.0
    ├── Android.bp
    ├── default
    │   ├── Android.bp
    │   ├── Devauth.cpp
    │   └── Devauth.h
    ├── IDevauth.hal
    └── types.hal

2 directories, 6 files

再按照 HAL 接口定义,在相应的接口函数里完成 CA 代码的开发即可。

4. 验收

4.1 提交资料

  • ✓ 芯片平台,存储类型。例:MTK81xx、EMMC 64GB
  • ✓ REE 操作系统详细信息。例:Android 7 64位
  • ✓ TEE 系统详细信息。例:基于 optee 的自研 tee,提供商为 xxx
  • ✓ TEE 里数据存储位置。例:EMMC 里的 RPMB 分区
  • ✓ TEE 侧的 TA 代码。例:ta_devauth 模块代码
  • ✓ REE 侧的 CA 代码。例:测试用例代码包及相应 TEE 功能 so。
  • ✓ REE 侧的 HAL 代码。例:hardware/interface/devauth 下的代码。
  • ✓ 能 adb root 的样机。

4.2 测试用例

开发者完成 TEE 的 TA 开发后,应该进行测试用例开发,以验证 TA 的功能与逻辑。 前置条件:ta_deauth 所管理的区域无任何数据,再按顺序进行以下测试项。

  1. 读数据测试,预期返回 -3
  2. 写数据测试,预期返回 -3
  3. 写密钥测试,预期返回 0
  4. 写密钥测试,预期返回 -3
  5. 读数据测试,预期返回 0,且数据全是 0x00 且有签名。
  6. 用正确的 HMAC 写数据测试,预期返回 0
  7. 用错误的 HMAC 写数据测试,预期返回 -4
  8. 读数据测试,预期返回 0,并且返回正确的数据和签名。

可在官方测试用例代码的基础上,加上自己的 CA 实现,以快速验证 TA。

开发者完成 HAL 开发后,可用测试用例进行测试,

也可以直接下载已编译好的 tee_hal_test 进行测试,测试方法如下

  1. tee_hal_test a 进行一次全新的测试,需要一个 ta_devauth 所管理的区域无任何数据。
  2. tee_hal_test 不带任何参数,可在 1. 后运行。

4.3 集成

4.2 中的测试用例通过后,下载 rpmbd_tee 并集成到系统中以服务方式运行起来即可,参考:

service rpmbd_tee /system/bin/rpmbd_tee
  class main
  user root
  group root system

设备认证 SDK(安卓)

注意:License 计费不再支持使用设备认证 SDK 认证的设备,请尽快切换到使用 WMPF 认证设备,无需维护 deviceToken,接入更便捷。

在系统集成 rpmbd 后,开发者需要在 Launcher 应用中接入设备认证 SDK,SDK 主要提供以下能力:

  • 注册设备 registerDevice:将 model_id 和 SN 与设备绑定。一旦成功后 model_id 和 SN 不可修改
  • 获取设备凭证 getDeviceToken:进行设备认证并从微信后台获取凭证,设备端发起通话时传给 VOIP 通话插件的 initByCaller 接口的 voipToken 参数。

1. 下载 SDK

请在 此处 下载 SDK 的 aar 文件。

建议使用 1.3 及以上版本(物联网卡应使用 1.3.1 及以上版本)。低版本不支持并发调用 registerVoipDevice,请务必注意在前一次调用返回前不要重复调用。

  • v1.5.0 及以上版本需集成 voipsdk-x.x-release.aarsafeguard-release.aar 两个 aar 文件
  • v1.3.1 及以下版本只需集成 voipsdk-x.x-release.aar

注意:使用设备认证 SDK 前,需先保证 rmpbd 服务正常运行。

2. 接口文档(v1.5.0及以上版本)

2.1 注册设备 registerDevice

将 model_id 和 SN 与设备绑定,一旦成功后 appid、model_id、SN 均不能更换。

int registerDevice(String appid, String model_id, String sn, String sn_ticket) throws Exception

注意事项(调用前必读

  • 注册设备成功后,会将 SN 固化至 EMMC/RPMB 分区中,标定此设备的唯一身份。SN 和 model_id 一经写入后续即不可更改
  • 此处使用的 SN,必须经过 WMPF 的 addDevice 接口作为 deviceId 注册,并与 WMPF 设备激活 使用的 deviceId 一致。 否则后续无法正常发起通话。
  • 注册成功后会在本 APK 的存储里存放数字证书,如果 APK 有变动(Android 系统认为应用变更了),则证书失效(会报错cert fail),可以清理 APK 的数据再以相同的 appid、model_id、SN调用接口重新申请即可。
  • 注册过程会有网络请求,时长根据网络情况会有所不同。高版本 android 不允许在主线程里进行网络请求,可以加处理或在线程里来调用 sdk。

参数说明

参数 类型 说明
appid String 微信小程序的 appid
model_id String 设备接入时从「微信小程序管理后台」申请获得的 model_id
sn String 设备序列号。厂商自己生成,长度不能超过 128 字节。字符只接受数字,大小写字母,下划线(*)和连字符(-)。
此处使用的 sn 必须与 WMPF 激活设备使用的 deviceId 一致
sn_ticket String 通过获取设备票据接口获得

返回值

其他异常说明请参考设备验证常见问题

名称 描述
OK 0 成功
ERR_ARGS -1 参数错误
ERR_IO -2 通用 IO 错误
ERR_KEY_IO -3 KEY 不匹配
ERR_RESPONSE -4 网络请求无回复
ERR_PEM -5 权限错误
ERR_INVALID_KEY -6 KEY 不可用
ERR_SERVICE -7 rpmbd 服务没运行
ERR_EMMC_UFS_CONFUSED -8 EMMC/UFS 不匹配。里面已经存在 SN
ERR_EMMC_UFS_IO -9 EMMC/UFS IO 错误
ERR_REG_NOPEM -10 密钥不存在

2.2 获取设备凭证 getDeviceToken

进行设备认证,并从微信后台获取设备凭证。设备发起通话时需要将这一凭证传给 VOIP 通话插件的 initByCaller 接口的 voipToken 参数。

String getDeviceToken(String appid, String model_id) throws Exception

如果设备是使用 v1.5 及以上版本 SDK 进行注册设备的,可以使用无参数的版本。

String getDeviceToken() throws Exception

参数说明

参数 类型 说明
appid String 微信小程序的 appid
model_id String 设备接入时从「微信小程序管理后台」申请获得的 model_id

注意事项

  • 接口耗时与网络有关,正常会在 1 秒左右。高版本 android 不允许在主线程里做,可以加处理或在线程里来调用 sdk。
  • ticket 有一个小时的有效期,一个小时内可被多次通话复用。建议开发者在用户发起通话前,提前调用 getCallerTicket 并缓存,避免在发起通话时再进行获取,以缩短发起通话时的用户等待时长。

2.3 获取设备 SN getDeviceSn(仅调试用)

获取 registerDevice 接口写入的 SN。

String getDeviceSn()

2.4 获取设备 modelId getDeviceModelid(仅调试用)

获取使用 registerDevice 接口写入的 modelId。仅在设备是使用 v1.5 及以上版本 SDK 进行注册设备时有效。

String getDeviceModelid()

3. 接口文档(v1.3.1 及以下版本)

3.1 初始化 init

SDK 初始化,其它接口在调用之前需要保证 init 成功。

boolean init()

3.2 注册设备 registerVoipDevice

参考 2.1 registerDevice

  • 1.3 以下版本 SDK,此接口严禁并发执行,务必在逻辑中保证一次 registerVoipDevice 返回后才能再次调用。

3.3 获取拨打方票据 getCallerTicket

参考 2.2 getDeviceToken

3.4 获取设备 SN GetDeviceSn(仅调试用)

同 2.3 getDeviceSn

使用 WMPF 认证设备(安卓)

在系统集成 rpmbd 后,如果设备上安装了 微信小程序硬件框架(WMPF),可以直接使用 WMPF 认证设备。

相比使用设备认证 SDK,使用 WMPF 注册设备有以下优势:

  • 接入成本低:不需要额外引入设备认证 SDK,不占用包大小,接入成本更低。
  • 免维护设备凭证:deviceToken 的获取由框架按需进行,开发者只需要进行设备注册,不需要维护 deviceToken,也不需要手动传递给微信小程序,维护成本更低。

注意

  • 注1:使用 WMPF 时,需先保证 rmpbd 服务正常运行。
  • 注2:使用设备认证 SDK 注册的设备,需要重新使用 WMPF 注册,才能免维护设备凭证。

具体使用可以参考示例代码

1. 版本要求

  • WMPF:本能力需安卓 WMPF >= 1.2.0 版本支持(如果是 2023/08/19 之前下载的 wmpf-cli,需要重新下载更新下)。
  • VOIP 通话插件:需插件 >= 2.3.0 支持。

2. 注册设备

使用 registerMiniProgramDevice 进行设备注册。使用 getMiniProgramDeviceInfo 进行注册信息查询。

3. 设备凭证预拉取

当使用 WMPF 注册设备后,框架会按需自行获取设备凭证,无需开发者介入。为了优化设备凭证的获取耗时,开发者可以在可能用到设备凭证前,调用prefetchDeviceToken接口提前进行预拉取,在有效期内(目前 1 小时)框架可以直接从缓存获得。

例如,在发起音视频通话时,框架会获取 deviceToken。建议开发者在用户发起通话的前置页面(例如:联系人页面等)进行设备凭证预拉取。

4. 框架获取设备凭证的场景

目前框架会在下列时机获取设备凭证,建议提前进行预拉取:

  • 微信小程序音视频通话,使用 VOIP 通话插件,调用 initByCaller 发起通话时。

5. 从设备认证 SDK 切换到使用 WMPF 注册设备

如果之前使用设备认证 SDK,想要切换成 WMPF 方式注册,可以注意以下事项:

  1. 同一台设备不要混用设备认证 SDK 和 WMPF 注册设备
  2. APP 中可以删除 voipsdk-1.x-release.aar 和 safeguard-release.aar。
  3. 对于之前注册过的设备,需要重新调用一次 registerMiniProgramDevice 接口以刷新设备密钥。
  4. 开发者不再需要获取和传入 deviceToken/callerTicket,使用插件时不能传 voipToken 参数。
  5. 如果之前开发者做了提前获取 deviceToken/callerTicket 的逻辑,可以替换为提前调用 prefetchDeviceToken

设备认证

在使用微信小程序提供的部分硬件能力时,需要提前将设备在微信进行注册,以便于微信验证设备的真实可信。

例如:微信小程序音视频通话(for 硬件)

1. 设备要求

微信需要硬件能力来对设备身份进行校验。设备厂商需要保证设备满足一定条件。

1.1 安卓设备

设备需要满足下列条件之一:

  • 设备 EMMC/UFS 存储上的 RPMB(Replay Protected Memory Block) 分区未被使用;
  • 设备支持 TEE,并能按照《设备认证 TEE 规范》开发 TA 并提交验收。

此外,设备厂商需要内置一个 RPMB 分区读写及通信的 RPMBD 服务(由微信提供,参考第 3 节),并保证服务能够开机正常启动。

1.2 Linux 设备

设备需要满足下列条件:

  • 设备 EMMC/UFS 存储上的 RPMB(Replay Protected Memory Block) 分区未被使用;

2. 安全策略

对于同一个 modelId,每一台物理设备应分配唯一且不变的 SN。 如果检测到包括但不限于下列情况,可能会导致设备能力被封禁:

  • 多台设备共用同一个 SN;
  • 同一台设备交替使用多个不同的 SN;
  • 使用虚假设备进行设备注册;
  • 其他伪造或滥用设备的行为。

3. 设备认证(安卓)

3.1 部署 RPMBD 服务

设备认证需要使用 EMMC/UFS 存储上的 RPMB 分区来保证设备的身份,需要设备厂商内置一个 RPMB 分区读写及通信的服务,并保证服务能够开机正常启动。

3.1.1 下载服务

请在 此处 下载对应平台、版本的 rpmbd 二进制文件。

注意:ARM 64 位版本(TEE)需要设备商按照规范开发 TEE 对应的 TA 模块,详细规范与流程参考设备认证 TEE 规范。

3.1.2 运行服务

将下载的 rpmbd 二进制(以下假设文件名为 rpmbd,下载后可以重命名)集成至系统里并以服务的方式运行起来。

注意

  • RPMBD 服务不仅用于注册设备过程,后续使用相关硬件能力时,都需要保证 RPMBD 服务一直运行
  • 每一颗 EMMC/UFS 存储芯片的 RPMB KEY 只能被写一次,不能修改。 如果被写入错误的值 (非注册时的 model_id 和 sn),那么这颗芯片就无法继续使用。
  • 高版本的 android 安全性较强,可能还需要配置 SELinux,且只支持在 system 分区启动。可参考 SELinux 参考配置

运行方式为:

rpmbd /dev/mmcblk1rpmb # /dev/mmcblk1rpmb 为rpmb分区路径, 开发者需要根据自己设备的情况具体填写(高通平台不需要指定)

参考如下 rc 的启动方式:

  • /system/etc/init,放到 system 分区启动(建议)

    service rpmbd /system/bin/rpmbd /dev/mmcblk1rpmb
      class main
      user root
      group root system
    
  • /vendor/etc/init,放到 vendor 分区启动(仅 Android < 8 支持)

    service rpmbd /vendor/bin/rpmbd
      class main
      user root
      group root system
    

3.2 注册设备

在完成 RPMBD 服务部署后,需要使用 WMPF 认证设备。

4. 设备认证(Linux)

  • 使用「微信小程序音视频通话 SDK(直连 Linux 设备)」的设备,请使用wx_device_register注册设备。

5. 常见问题

(1) 注册设备报错 emmc write fail00

检查 rpmbd 服务启动参数里的 rpmb 分区路径是否正确。 若路径正确,确认此路径对应的 rpmb 分区在 Android OS 下能否被访问。


(2) 报错 cert fail

应用缓存被清理,或 Android 认为 APK 有变动导致 keystone 中数字证书失效导致。

需要清理 apk 数据缓存再使用相同的 appid、model_id、SN 调用 registerDevice/registerVoipDevice 刷新密钥。


(3) 接口报错 ticket 1 invalid rpmb_buffer

当前 rpmbd 与 SDK aar 的版本不兼容,应保持二者使用相同版本。例如:rpmbd 服务使用了 1.3 以下版本,而 SDK 使用了 1.3 或以上的版本。


(4) 注册设备返回 -7,或调用接口报错 failed to get native service 或其他获取 rpmbd 服务失败的错误

  • 确认已部署 rpmbd 服务,且服务正常运行。(可以通过 ps 查看)
  • Android >= 8 版本,请确认 rpmbd 是在 system 分区启动
  • 如果启用了 SELinux,需确认 SELinux 的相关规则已正确配置


(5) 注册设备报错 register: null

高版本 android 不允许在主线程里进行网络请求,需要单独开线程里来调用 SDK 接口


(6) 使用物联网卡时,网络请求一直失败

物联网卡请使用 WMPF 注册设备,或设备认证 SDK >= 1.3.1 版本,并确保 servicewechat.com 域名能够正常访问。


(7) 注册设备报错 9800004,device xxx is not confirmed

绝大多数情况是因为注册设备时使用了 1.3 以下版本的 设备认证 SDK,且同时发起了多次 registerVoipDevice 请求,此时有概率设备端使用的密钥与后台不同步,导致设备再也无法成功注册,且该过程不可逆。

建议开发者使用 WMPF 注册设备,或升级到设备认证 SDK 1.3 及以上版本,使用低版本时请务必保证前一次 registerVoipDevice 返回前不要重复调用。


(8) 注册设备报错 9800004,device xxx not registered

绝大多数情况是当前设备之前使用不同的 modelId/sn 进行了注册。如使用 WMPF 注册设备,可以使用 getMiniProgramDeviceInfo 检查下当前设备内的 sn 和 modelId,和传入的是否一致。


(9) 获取票据 getCallerTicket/getDeviceToken 报错 9800004

一般是因为传入的 mode_id 与最初注册设备时不一致。


(10) 报错 ticket 0 digital-sig check fail

多数是因为当前设备已经在这台设备的另一个 App 中注册过,目前设备验证只能用于单个应用。需要再使用相同的 appid、model_id、SN 重新调用 registerDevice/registerVoipDevice 刷新密钥。

例如,同时混用「使用 WMPF 认证设备」和「设备认证 SDK」,可能会导致 WMPF 和 开发者应用互相抢占密钥,导致这个错误。


(11) 注册设备报错 40234 hmac check fail

可能有以下原因

  • 设备已经使用其它的 model_id/sn 注册过,此次注册传入了不同的 model_id;
  • 设备曾经注册过,且注册设备时使用了 1.3 以下版本的设备认证 SDK,且同时发起了多次 registerVoipDevice 请求,此时有概率设备端使用的密钥与后台不同步,导致设备再也无法成功注册,且该过程不可逆。


(12) 获取 deviceToken 时报错 register info invalid

当从「设备认证 SDK」切换到「使用 WMPF 认证设备」后,需要调用 WMPF registerMiniProgramDevice 重新进行设备注册,若未调用或调用未成功,则在需要获取 deviceToken 的场景会报这个错误。


微信小程序设备消息

能力介绍

「微信小程序设备消息」是一种长期订阅类型的「微信小程序订阅消息」,且需要完成「设备接入」才能够使用。

用户在使用设备过程中,需要关注某些由设备触发且需要人工介入的事件。例如安防摄像头检测到异常,设备耗材不足,设备发生故障等等。

「微信小程序设备消息」能力指的是,只要用户在微信小程序内订阅通知,开发者就可以将这些事件以订阅消息的形式发送给用户。消息在微信内的产品形态,目前以「服务通知」形式呈现。

开发流程

1. 设备接入

微信小程序想要使用设备消息能力,首先需要接入设备,详见「设备接入」文档。

完成接入后,开发者可获得由平台分配的 model_id 。model_id 对应一种设备类型,也是调用微信小程序设备能力相关接口的重要凭证。

2. 获取模版 ID

登录「微信小程序管理后台」——「功能」——「订阅消息」——「公共模板库」——「长期订阅」,查看可选用的设备消息模板。

选择设备消息模板中需要的关键词,并提交。

注意:设备消息模版的关键词内容由平台生成,为枚举值,开发者不能够自定义内容。

提交后,可在「我的模板」中找到对应模板的模板 ID ,每个模板以 template_id 标记。

3. 获取设备票据

获取 snTicket 用于「发起订阅」步骤。

详见服务端设备票据接口 hardwareDevice.getSnTicket 。

4. 发起订阅

调用 wx.requestSubscribeDeviceMessage 接口会有以下授权弹窗出现,用户同意订阅消息后,才会有设备消息发送至用户的微信会话。

微信小程序内完成设备消息订阅

用户订阅设备消息时,需要手动点击“添加提醒”,设备触发消息后才会出现“响铃+振动”的强提醒状态,开发者可以在前端界面进行引导。

示例代码

wx.requestSubscribeDeviceMessage({
    sn: 'xxxx',
    snTicket: 'xxxxx',
    modelId: 'xxxxx',
    tmplIds: ['xxxxx'],
    success(res) {
        console.log('[wx.requestSubscribeDeviceMessage success]: ', res)
        // { 'QCpBsp1TGJ1ML-UIwAIMkdXpPGzxSfwJqsKsvMVs3io': 'accept' }
    },
    fail(res) {
        console.log('[wx.requestSubscribeDeviceMessage fail]: ', res)
    }
})

5. 发送设备消息

开发者通过微信服务端接口向用户推送设备消息。

详见服务端设备消息发送接口 hardwareDevice.send 。

服务通知 – 设备消息

设备消息具体形式

硬件设备接入指引

提供硬件设备联网、控制、通讯等能力的微信小程序,在完成设备接入后,才可以使用微信小程序提供的硬件能力(例如「设备消息」、「音视频通话」)等。

接入条件

经过微信认证的非个人主体微信小程序。

面向智能硬件生产企业或开发者。

接入步骤

1. 申请设备类目

登录「微信小程序管理后台」——「设置」——「基本设置/服务类目」,点击「申请更多类目」(一个微信小程序最多可申请5个服务类目)。

添加「工具——设备管理」为微信小程序类目。

2. 开通硬件设备能力

登录「微信小程序管理后台」——「功能」——「硬件设备」,阅读设备使用条件和接入流程等,点击「开通」。

管理员扫码确认后开通成功,进入设备管理页面。

3. 添加设备类型

点击添加设备,按照每个字段对应的说明填写信息,如实填写设备相关信息,否则会导致审核不通过。

每次可注册一种设备类型,例如“空调—空调1号”和“空调—空调2号”需要分别进行注册。

注意:

  • 选择设备类型时,请认真判断注册的设备类型是否已经是已有的设备类型,比如“洗拖一体机”属于“生活电器——扫地机器人”,请不要重复添加平台设备库中已有的设备品类。如果是设备库中缺失的设备类型,可以选择“其他”。

4. 获取设备 model_id

设备注册成功后,可以获得平台分配的 model_id,model_id 是调用微信小程序设备能力相关接口的重要凭证。获取 model_id 后,微信小程序可以按照相关文档指引调用「设备消息」等硬件能力。

无线局域网 (Wi-Fi)

在微信小程序中支持搜索周边的 Wi-Fi 设备,同时可以针对指定设备,传入密码发起连接。

该系列接口为系统原生能力,如需查看「微信连 Wi-Fi」能力及配置跳转微信小程序,请参考文档

1. 连接指定 Wi-Fi 设备

如果知道 Wi-Fi 设备名称和密码,并确认设备在附近,可以直接在微信小程序中连接指定 Wi-Fi。

接口调用时序为:

  1. startWifi: 初始化 Wi-Fi 模块
  2. connectWifi: 连接 Wi-Fi(iOS 需 11 及以上版本支持)
  3. onWifiConnected: 连接上 Wi-Fi 的事件回调

2. 连接周边 Wi-Fi 设备

微信小程序可以通过扫描附近的 Wi-Fi 设备,让用户选择某个设备进行连接。

由于系统限制,不同平台下接口调用时序有所差异:

Android

  1. startWifi: 初始化 Wi-Fi 模块
  2. getWifiList: 请求获取周边 Wi-Fi 列表
  3. onGetWifiList: 获取到 Wi-Fi 列表数据事件
  4. connectWifi: 连接 Wi-Fi
  5. onWifiConnected: 连接上 Wi-Fi 的事件回调

iOS

  1. startWifi: 初始化 Wi-Fi 模块
  2. getWifiList: 请求获取周边 Wi-Fi 列表。本接口会跳转到系统设置中的微信设置页,需引导用户进入「无线局域网」设置页,手动连接设备。(iOS 11.0 及 11.1 版本因系统问题失效)
  3. onGetWifiList: 获取到 Wi-Fi 列表数据事件
  4. setWifiList: 设置 Wi-Fi 列表 中 AP 的相关信息,辅助用户进行连接
  5. onWifiConnected: 连接上 Wi-Fi 的事件回调

3. Wi-Fi 网络下的设备通信

通过 wx.getConnectedWifi 可以获取当前系统连接 Wi-Fi 信息,在确认当前连接是设备 Wi-Fi 后(手机与设备处于同一局域网),便可以使用相关接口与设备进行通信。

  • 使用 wx.startLocalServiceDiscovery 等一系列 mDNS API ,可以获取局域网内提供 mDNS 服务的设备 IP 。然后通过 wx.request / wx.connectSocket 并传入格式为 ${IP}:${PORT}/${PATH} 的 url 参数,就可以进行本地 HTTP / WebSocket 通信。详细文档参考「局域网通信」。

  • 开发者根据具体设备的情况,在知道与设备通信的 ip address 和 port 之后,使用 TCPSocket.connect 或 UDPSocket.connect 就能与设备进行 TCP 或 UDP 通信。详细文档参考「TCP 通信」与「UDP 通信」。

4. 注意事项

  • Android 系统 6.0 以上版本,在没有打开定位开关的时候会导致设备不能正常获取周边的 Wi-Fi 信息。
  • Wi-Fi 相关接口暂不可用 wx.canIUse 接口判断。

NFC

支持平台:Android

支持 HCE(基于主机的卡模拟)模式,即将安卓手机模拟成实体智能卡。 支持 NFC 读写,即手机作为读卡器使用。

  • 适用机型:支持 NFC 功能,且系统版本为 Android 5.0 及以上的手机
  • 适用卡范围:符合 ISO 14443-4 标准的 CPU 卡
  • 支持 Reader / Writer(读取器 / 写入器)模式,即支持 NFC 设备读取或写入被动 NFC 标签和贴纸
  • 适用机型:支持 NFC 功能,且系统版本为 Android 5.0 及以上的手机
  • 适用范围:
    • 支持 NFC-A (ISO 14443-3A) / NFC-B (ISO 14443-3B) / NFC-F (JIS 6319-4) / NFC-V (ISO 15693) / ISO-DEP (ISO 14443-4) 标准的读写
    • (部分 Android 手机)支持 MIFARE Classic / MIFARE Ultralight 标签的读写
    • 支持对 NDEF 格式的 NFC 标签上的 NDEF 数据的读写

蓝牙信标 (Beacon)

基础库 1.2.0 开始支持。

蓝牙信标 (Beacon) 是建立在蓝牙低功耗 (BLE) 协议基础上的一种广播协议。

Beacon 设备作为蓝牙低功耗协议中的外围设备,持续向周围广播包含设备标识的特定数据包,但不能和中心设备建立连接。微信小程序运行的设备作为中心设备,可以收到 Beacon 设备的广播包,实现数据交互。常用于室内定位、消息推送等场景。

微信小程序中,开发者可以通过 wx.startBeaconDiscovery 开始搜索 Beacon 设备,并通过 wx.onBeaconUpdate 接收设备更新事件。

1. 设备标识

每个 Beacon 设备的广播包中,至少携带了以下信息,共同组成了设备的唯一标识符。

  • UUID (16 字节):128 位的 UUID,用于唯一标识微信小程序所识别的一系列信标设备。
  • major (2 字节):0 – 65535 的无符号整数,可以用来区分相同 UUID 的一组设备。
  • minor (2 字节):0 – 65535 的无符号整数,可以用来区分有相同 UUID 和 major 的设备。

2. 设备状态

当微信小程序接收到 Beacon 设备的信号时,还会提供下列信息

  • rssi: 信号强度,单位为 dBm。
  • proximity: Beacon 标识设备距离的枚举值(仅 iOS)。
  • accuracy: Beacon 设备的距离,单位为米。

3. 注意事项

  • Beacon 相关接口可以直接使用,不需要使用 wx.openBluetoothAdapter 初始化蓝牙适配器模块
  • 由于 Beacon 可以被用来进行定位,因此需要微信有系统的位置权限时才能使用。