一、简介
VoIP是小米提供给IM类应用的通话管理服务,开发者可通过集成VoIP Service Kit实现便捷的来电一键接听、横幅通知、静音与取消静音等功能,提升推送服务及用户体验。
二、接入指南
VoIP申请流程及使用规范(详见第四章节)
VoIP应用服务接入
1、开发流程
| 步骤 | 说明 |
| 1、开通推送服务 | 请参考推送服务启用指南开通推送服务。 |
| 2、客户端获取RegId | 调用服务端API时,需要设置推送目标(Target),APP客户端需要先接入MiPush SDK获取RegId。注意RegId变化的场景,若设备的RegId发生变化但服务端调用推送服务REST API时未更新RegId的值,将会导致设备收不到该条消息。建议APP每次冷启动时都去尝试获取RegId。 |
| 3、服务器基于MiPush分配的AppId和AppSecret生成鉴权令牌(可选用服务端SDK) | 调用推送服务REST API推送场景发消息时,请求头需设置Authorization参数,请参考服务端API获取Authorization章节进行获取。 |
| 4、服务器调用REST API推送消息(可选用服务端SDK) | 应用服务器参考服务端API请求体结构说明、请求体参数说明发送REST API请求。若请求失败请参考响应错误描述进行问题排查。 |
| 5、消息回执(可选) | MiPush服务端会将消息送达状态以回执消息形式发送给您的应用回执服务端,方便您获取消息下达端侧后的状态,定位问题。详情请参考服务端API消息回执3.3章节。 |
2、开通推送服务
已开通了MiPush服务的开发者可忽略,未开通MiPush服务的开发者请参考推送服务启用指南开通推送服务。
3、客户端获取注册RegId
RegId(Registration ID)标识了每台设备上的每个应用,开发者参照Android客户端SDK集成指南中的接入准备与接入指导完成客户端接入并注册成功后的,可通过onReceiveRegisterResult获取注册返回结果或者调用getRegId以获取RegId,获取到之后使用RegId来推送消息。
4、推送消息
三、服务端API
1、获取Token
1.1、请求原型
| 协议 | HTTPS POST |
| 接口方向 | 开发者服务器 -> MiPush2.0服务器 |
| 接口URL | https://hyper-cloud-api.xmpush.xiaomi.com/v1/oauth2/token |
| 数据格式 | Content-Type:application/x-www-form-urlencoded |
1.2、请求参数
| 参数名 | 是否必须 | 说明 |
| grant_type | 是 | 固定值为client_credentials,表示为客户端模式。 |
| client_id | 是 | 应用的AppId。 |
| client_secret | 是 | 应用的AppSecret。 |
1.3、正常响应
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"access_token": "[返回的Access Token]",
"expires_in": 3600,
"token_type": "Bearer"
}| 参数名 | 是否必须 | 说明 |
| access_token | 是 | MiPush接口的Access Token,最大字符长度2048,包含字母(A-Z, a-z)、数字(0-9)、特殊字符(- 和 _)。 |
| expires_in | 是 | 剩余的有效期,单位秒。 |
| token_type | 是 | 固定值Bearer,代表Access Token的类型。 |
1.4、异常响应
HTTP/1.1 400 bad request
Content-Type: application/json; charset=utf-8
{
"error": {
"code": 400,
"status": "INVALID_ARGUMENT",
"message": "app have no permission"
}
}| 参数名 | 是否必须 | 说明 |
| code | 是 | 请求错误代码, 错误码列表: 1、400 请求参数无效; 2、401 认证失败; 3、500 内部错误。 |
| status | 是 | 错误简要代码。 |
| message | 是 | 错误的描述。 |
2、VoIP消息推送API
2.1、请求体结构说明
2.1.1、接口原型
| 协议 | HTTPS POST |
| 接口方向 | 开发者服务器 -> MiPush2.0服务器。 |
| 接口URL | https://hyper-cloud-api.xmpush.xiaomi.com/v1/projects/{AppId}/messages:send AppId:填充应用的AppId,开放平台分配。 |
| 数据格式 | Content-Type:application/json。 |
2.1.2、请求头
| 参数 | 取值描述 | 样例 |
| Authorization | Bearer {Access token} 参考3.1获取token接口 备注:Bearer和{Access token}之间有一个空格。 | Bearer 7olfn0o_xqEnphHP********YKxlRlal-T0DofEKUac |
| message-type | 消息类型 3:VoIP消息。 | 3 |
2.1.3、请求体
| 参数 | 是否必选 | 参数类型 | 描述 |
| message | 是 | object(Message) | 推送消息结构体,详情请参见3.2.2 请求参数说明中的Message参数说明。 |
| target | 是 | object(Target) | 发送目标,详情请参见3.2.2 请求参数说明中的Target参数说明。 |
| options | 否 | object(Options) | 发送控制参数,详情请参见3.2.2 请求参数说明中的Options参数说明。 |
2.2、请求参数说明
Target
| 参数 | 是否必选 | 参数类型 | 描述 |
| token[] | 是 | string | 推送消息的目标用户,客户端调用应用注册获取的RegId,最多支持1000个。 |
Options
| 参数 | 是否必选 | 参数类型 | 描述 |
| appMessageId | 否 | string | 消息唯一标识,App开发者自定义消息ID,主要用于消息去重,去重时间为10分钟,支持数字、大小写字母、下划线任意组合,不可包含特殊字符。 |
| ttl | 否 | integer | 消息缓存时间,单位:秒。在用户设备离线时,消息在MiPush服务器进行缓存,在消息缓存时间内用户设备上线,消息会下发,超过缓存时间后消息会被丢弃,默认值为3600秒(1小时),最大值为864000秒(10天)。 |
| collapseKey | 否 | string | 用户设备离线时,MiPush服务器对离线消息缓存机制的控制方式,用户设备上线后缓存消息会再次下发,默认值为0,取值含义如下: 0:对所有离线消息都缓存(当离线消息超过30条时,丢弃老消息,保留最新消息) -1:对每个应用发送到该用户设备的离线消息只会缓存最新的一条。 |
| callbackId | 否 | string | 回执ID: 回执地址和回执类型的映射ID,获取方式请参见3.3 消息回执。 |
| callbackParam | 否 | string | 回执参数: 开发者自定义回执参数,字符长度限制100以内,中英文均以一个单位计算。 |
Message
| 参数 | 是否必选 | 参数类型 | 描述 |
| notification | 是 | object(Notification) | 通知消息。详情请参见3.2.2 请求参数说明中的Notification参数说明。 |
| extraData | 是 | string | 自定义消息负载内容。 最大长度为4096字符。 |
Notification
| 参数 | 是否必选 | 参数类型 | 描述 |
| channelId | 是 | string | 通知消息类别。 小米推送管理的消息分类,申请及接入方法请参见推送消息分类新规 |
2.3、VoIP消息请求示例
- 请求地址
POST https://hyper-cloud-api.xmpush.xiaomi.com/v1/projects/{AppId}/messages:send- 请求头
Content-Type: application/json; charset=UTF-8
Authorization: Bearer eyJ***InR5c***.eyJle***aWF0Ij***MSJ9.d-bES8DF***
message-type: 3- 请求体
{
"message": {
"notification": {
"channelId": "xxxxx"
},
"extraData": "xxxxx"
},
"target": {
"token": ["6640Q1+Am1hIiovjF8f2NV9/bdTUZaSj+u9YTnVVVz0="]
}
}- 请求示例cURL
curl --location 'https://hyper-cloud-api.xmpush.xiaomi.com/v1/projects/{AppId}/messages:send' \
--header 'Content-Type: application/json' \
--header 'message-type: 3' \
--header 'Authorization: Bearer *********' \
--data '{
"message": {
"notification": {
"channelId": "****"
},
"extraData": "*****"
},
"target": {
"token": [
"*****************"
]
}
}'2.4、响应参数
2.4.1、 响应
| 参数 | 是否必选 | 参数类型 | 描述 |
| message | 否 | object(MessageRespDTO) | 响应消息体,见MessageRespDTO定义。 |
| error | 是 | object(ErrorInfoDTO) | 成功或失败响应消息体,见ErrorInfoDTO定义。 |
MessageRespDTO
| 参数 | 是否必选 | 参数类型 | 描述 |
| name | 否 | string | 消息ID |
ErrorInfoDTO
| 参数 | 是否必选 | 参数类型 | 描述 |
| code | 是 | integer | 成功/失败,响应码,当code为200时代表响应成功,其他代表失败,详见3.2.44响应码。 |
| status | 否 | string | 错误码。 |
| message | 否 | string | 错误描述。 |
| details[] | 否 | object(PushErrorDTO) | 消息推送过程失败的设备Token,只有在群推部分成功场景时会返回。 |
PushErrorDTO
| 参数 | 是否必选 | 参数类型 | 描述 |
| errorCode | 否 | string | 错误码,当推送消息失败时,此字段返回具体的错误码,成功时则为空 详见3.2.44响应码部分"业务响应码"。 |
| detail | 否 | string | 错误详细描述。 |
| token | 否 | string | 推送失败的设备Token。 |
2.4.2、成功响应示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"message": {
"name": "smm00192761270711242wR"
},
"error": {
"code": 200,
"details": [
{
"errorCode": ""
}
]
}
}2.4.3、失败响应示例
2.4.3.1、oauth token过期
HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8
{
"error": {
"code": 401,
"status": "accessToken is invalid",
"message": "accessToken is invalid",
"details": [
{
"errorCode": "INVALID_ARGUMENT"
}
]
}
}2.4.3.2、重复请求
HTTP/1.1 409 Conflict
Content-Type: application/json;charset=UTF-8
{
"error": {
"code": 409,
"status": "REPEATED_MESSAGE",
"message": "重复的消息,请检查",
"details": [
{
"errorCode": "REPEATED_MESSAGE",
"detail": "重复的消息,请检查options.appMessageId字段"
}
]
}
}2.4.3.3、消息体超过限制
HTTP/1.1 413 Request Entity Too Large
Content-Type: text/html;charset=utf-8
<html>
<head>
<title>413 Request Entity Too Large</title></head>
<body>
<center>
<h1>413 Request Entity Too Large</h1></center>
<hr>
<center>openresty</center>
</body>
</html>2.4.4、响应码
| HTTP响应码 | 描述 | 解决办法 |
| 200 | OK | 无。 |
| 400 | 参数错误 | 请检查业务响应码并根据业务响应码进一步排查问题。 |
| 401 | 鉴权失败 | 请检查HTTP头中Authorization参数。 |
| 403 | 禁止访问 | 检查身份信息。 |
| 409 | 业务请求去重 | 更换请求id重新发送。 |
| 413 | 请求体过大 | 请求体减小到100kb以内。 |
| 500 | 服务内部错误 | 请通过开放平台提交问题。 |
| 502 | 请求连接异常,常见于网络状况不稳定 | 建议稍后重试,或通过开放平台提交问题。 |
| 503 | 服务器故障 | 建议稍后重试,或通过开放平台提交问题。 |
业务响应码
| 业务响应码 | HTTP响应码 | 解决办法 |
| SUCCESS | 200 | 无 |
| INVALID_ARGUMENT | 400 | 请求参数无效,根据描述详情message字段排查。 |
| REPEATED_MESSAGE | 409 | 更换请求appMessageId重试或忽略。 |
| INTERNAL | 500 | 内部错误。 |
3、消息回执
3.1、回执说明
消息回执是提供给开发者获知消息状态的功能,MiPush2.0对于消息回执这块做了改进。对于已接入MiPush2.0的开发者可在小米推送开发者平台配置回执类型(一个应用可配置多个,有数量上限)。平台会生成唯一的Callback ID,开发者在推送消息时(参考消息推送API)传入对应的Callback ID,小米推送会根据配置的Callback URL和Callback Type将符合的消息推送状态反馈给开发者。
3.2、回执配置
- 进入应用信息页面,开发者会看到应用信息回执选项,点击配置进入配置页。

- 点击新建回执,按要求填写保存。
回调地址会同时支持http和https,https证书校验只支持Java内置信任库的证书。

- 列表页会显示上一步创建的回执配置记录,消息回执ID即为Callback ID,开发者推送消息时携带即可。

3.3、回执发送
请参考4.6、消息回执。
四、服务端SDK
1、快速接入
接入Java SDK前,您需要已启用小米推送服务,并且获得应用对应的AppId和AppSecret。
您可以选择接入服务端Java Http2 SDK。
1.1、获取SDK
1.2、运行环境
要求JDK版本大于或等于1.8。
1.3、导入SDK
- 解压“MiPush_SDK_Server_Http2.zip”压缩包。
- 将“MiPush_SDK_Server_Http2”目录下的jar包放入项目工程libs目录。
说明:如果运行环境为JDK 1.9以上版本可以不引入conscrypt-openjdk-uber-2.1.0.jar包。如果不想使用okhttp3组件,可以不引入conscrypt-openjdk-uber-2.1.0.jar、okhttp-3.14.2.jar和okio-1.17.2.jar这三个jar包,同时调用Constants.disableOkHttp3()方法,此时将不会启动HTTP2.0协议。
2、消息推送SDK
2.1、方法签名
com.xiaomi.xmpush.server.HyperMessageSender#pushMessage2.2、调用示例
向特定的单台或多台设备发送消息,对应HyperMessage.Target.token字段。
private static final String TEST_CLIENT_ID = "client_id";
private static final String TEST_SECRET = "secret_key";
public static void main(String[] args) throws IOException {
// HyperMessageSender可按照提供的两种构造方法分别调用
// 注:SDK调用无需关注Token获取,在HyperMessageSender发送或撤回消息时会查询并缓存Token到本地,每次请求时自动加入到请求头中
HyperMessageSender messageSender1 = new HyperMessageSender(TEST_CLIENT_ID, new HyperToken(TEST_SECRET, TEST_CLIENT_ID));
HyperResponse<Object> result1 = messageSender1.pushMessage(createNotificationMsg(), HyperMessage.MessageType.NOTIFICATION_MSG, 0);
System.out.println(result1);
HyperMessageSender messageSender2 = new HyperMessageSender(TEST_CLIENT_ID, TEST_SECRET );
// 发送VoIP消息
HyperResponse<Object> result4 = messageSender2.pushMessage(createVoipMsg(), HyperMessage.MessageType.EXTRA_MSG, 0);
System.out.println(result4);
}
// 构建VoIP消息
private static HyperMessage createVoipMsg() {
HyperMessage.Notification notification = HyperMessage.Notification.builder()
.channelId("xxxxx")
.build();
HyperMessage.Message message = HyperMessage.Message.builder()
.notification(notification)
.extraData("xxxxx")
.build();
HyperMessage.Target target = HyperMessage.Target.builder()
.token(Collections.singletonList("6640Q1+Am1hIiovjF8f2NV9/bdTUZaSj+u9YTnVVVz0="))
.build();
return HyperMessage.builder()
.message(message)
.target(target)
.build();
}2.3、参数说明
| 参数 | 是否必选 | 描述 |
| message | 是 | 推送消息结构体,详情请参考服务端api消息推送3.2.2章节。 |
| messageType | 是 | 消息类型: VOIP_MSG:应用内通话消息。 |
| retries | 是 | SDK请求重试次数。 |
2.4、响应说明
| 参数 | 是否必选 | 描述 |
| code | 否 | 请求响应码。 |
| message | 否 | 成功请求为200。 |
| data | 是 | 成功请求返回string类型的messageId:smm00296761636228145sD。 |
| timestamp | 是 | 请求结束时间戳。 |
五、客户端SDK使用
参照Android客户端SDK集成指南中 四、4、API详细说明。
1、开发者判断手机支持能力
MiPush服务及应用内通话通知功能需系统框架支持。开发者使用前需调用MiPush SDK方法确认设备兼容性。
1.1、是否支持MiPush
boolean isSupportMiPush = MiPushSdk.getInstance().isSupport(context);1.2、是否支持VoIP消息
boolean isSupportVoIP = MiPushSdk.getInstance().isPushSupport(MiPushSdk.FLAG_SUPPORT_CALLKIT);注意:以上api需要调用接入指南中的registerPush完成推送服务的注册后才能调用。
2、VoIP消息
2.1、VoIPMessage回调
| 接口名称 | onCallMessage(CallMessage callMessage) | ||
| 接口说明 | VoIP消息回调接口 | ||
| 参数 | 参数名 | 参数类型 | 参数含义 |
| callMessage | CallMessage | 通话透传的消息 | |
| 返回值 | 返回类型 | 返回值含义 | |
| void | - | ||
MiPush SDK给开发者提供的PushMessageReceiver类实现了onCallMessage这个接口,开发者可通过在继承该类的广播接收器中重载onCallMessage方法来实现自身的业务逻辑。
public class DemoMessageReceiver extends PushMessageReceiver {
/** 使用VoIP 通话消息时使用*/
@Override
public void onCallMessage(CallMessage callMessage) {
super.onCallMessage(callMessage);
//接收到来电后,开发者可自行处理业务逻辑
}
}CallMessage 实体类说明:
public class CallMessage {
private String mMessage;// 对应通过服务端api下发的extraData字段
private String mMsgId;
public CallMessage(String msgId, String message) {
mMsgId = msgId;
mMessage = message;
}
public String getMessage() {
return mMessage;
}
public String getMsgId() {
return mMsgId;
}
}