Skip to content
Back to skills

Hmos Push Kit Voip

ASecurity

推送应用内通话消息助手(VOIP)。当开发者需要实现语音/视频来电通知、voip功能、或呼叫接听界面时触发。 ============================================================ 触发条件(只有满足以下意图时才触发): ============================================================ ✅ 正确触发场景: - "帮接入voip消息" - "实现语音来电通知" - "添加推送应用内通话消息功能" - "接入视频通话功能" - "应用内通话消息" - "voip呼叫接听界面" - "需要接入voip功能" ❌ 不触发场景: - 询问/概念:"voip是什么"、"语音通话怎么实现" - 否定意图:"不需要voip" - 仅输入关键词:"voip"、"通话" - 测试/调试:"voip测试" - 带引号输入:"voip"、"语音通话" - 配置咨询:"voip怎么配置" ⚠️ 重要提醒: - 只要需要接入 voip 功能,**必须加载本 skill** - 本 skill 提...

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 22, 2026
developmenttypescriptapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 22, 2026

npx -y skills add IsKenKenYa/skills --skill hmos-push-kit-voip --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hmos Push Kit Voip?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hmos Push Kit Voip
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iskenkenya-hmos-push-kit-voip/badge)](https://www.skillsdirectory.com/skills/iskenkenya-hmos-push-kit-voip)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hmos-push-kit-voip
description: |
  推送应用内通话消息助手(VOIP)。当开发者需要实现语音/视频来电通知、voip功能、或呼叫接听界面时触发。

  ============================================================
  触发条件(只有满足以下意图时才触发):
  ============================================================

  ✅ 正确触发场景:
  - "帮接入voip消息"
  - "实现语音来电通知"
  - "添加推送应用内通话消息功能"
  - "接入视频通话功能"
  - "应用内通话消息"
  - "voip呼叫接听界面"
  - "需要接入voip功能"

  ❌ 不触发场景:
  - 询问/概念:"voip是什么"、"语音通话怎么实现"
  - 否定意图:"不需要voip"
  - 仅输入关键词:"voip"、"通话"
  - 测试/调试:"voip测试"
  - 带引号输入:"voip"、"语音通话"
  - 配置咨询:"voip怎么配置"

  ⚠️ 重要提醒:
  - 只要需要接入 voip 功能,**必须加载本 skill**
  - 本 skill 提供完整的 voip 接入指导,包括 VoipCallService、CalleePage、CallComponent 等组件的创建
  - voip 接入涉及来电处理、呼叫界面、状态上报等完整流程,不能凭其他 skill 的通用说明(如"同一 ability 接收多种消息类型")自行编写

  此 Skill 专注于帮助开发者实现应用内通话消息的推送功能。
---

# 推送应用内通话消息助手(voip)

本 Skill 帮助开发者实现华为推送应用内通话消息功能。

---

## ⚠️ AI 必做检查清单(强制检查)

> **AI 必须在开始开发前逐一确认以下所有项目,确保不遗漏任何关键步骤!**

### 接入 voip 消息必须完成的文件清单:

| 序号 | 必做项 | 文件路径 | 用途 |
|-----|-------|---------|-----|
| ☐ 1 | 创建 PushMessageAbility | `src/main/ets/entryability/PushMessageAbility.ets` | 处理应用内通话消息主流程 |
| ☐ 2 | 创建 VoipCallService | `src/main/ets/service/VoipCallService.ets` | 处理来电逻辑,上报通话状态 |
| ☐ 3 | 创建视频接听页面 | `src/main/ets/pages/CalleePage.ets` | **视频接听页面,用户接听时拉起** |
| ☐ 4 | 创建呼叫控制组件 | `src/main/ets/component/CallComponent.ets` | 接听/挂断按钮UI |
| ☐ 5 | 添加头像图片 | `src/main/resources/rawfile/example.png` | 来电时显示的用户头像 |
| ☐ 6 | 配置 main_pages.json | `src/main/resources/base/profile/main_pages.json` | 添加 CalleePage 页面 |
| ☐ 7 | 配置 module.json5 | `src/main/module.json5` | 添加 actions 配置 |
| ☐ 8 | 确认已开通推送服务 | AppGallery Connect | ⭐必须已开通 |
| ☐ 9 | 确认已申请 voip 权益 | AppGallery Connect | ⭐必须已申请 |

> **⚠️ 特别注意**:视频接听页面(CalleePage)是**必须**创建的,否则用户在解锁状态下点击视频接听时会找不到页面!即使只做语音通话,也建议创建该页面以备将来扩展。

---

## Token 接入检测与处理流程

### 第一步:AI 自动检测(默认执行)

AI 首先自动检查项目中是否已经接入了 Push Token:

**检测内容**:
1. 搜索项目中是否引入了 `@kit.PushKit`
2. 搜索是否调用了 `pushService.getToken()`
3. 检查是否在 `onCreate()` 生命周期中获取 Token
4. 检查是否有将 Token 上报到服务器的逻辑

### 第二步:根据检测结果处理

| 检测结果 | 处理方式 |
|---------|---------|
| ✅ **已接入 Token** | 直接进入 voip 消息开发流程 |
| ❌ **未接入 Token** | **参考 token skill 代码,直接帮开发者接入** |

### 第三步:未接入 Token 时直接接入(参考 hmos-push-kit-token Skill 的代码)

**当 AI 确定项目未接入 Token 时,参考 token skill 的标准代码帮开发者接入**:

**参考以下代码模板,将相关 import 和方法添加到 EntryAbility.ets 中**:

```typescript
import { pushService } from '@kit.PushKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 其他 import 如果已有则不需要重复添加

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 将 getToken 调用添加到现有 onCreate 逻辑中
    pushService.getToken().then((token: string) => {
      hilog.info(0x0000, 'testTag', 'Succeeded in getting push token');
      this.reportTokenToServer(token);
    }).catch((err: BusinessError) => {
      hilog.error(0x0000, 'testTag', 'Failed to get push token: %{public}d %{public}s', err.code, err.message);
    });
  }

  // 如果已有此方法,需要合并 Token 上报逻辑
  reportTokenToServer(token: string): void {
    hilog.info(0x0000, 'testTag', 'Reporting token to server');
    // TODO: 将 Token 上报到您的服务器(必需步骤)
  }
}
```

**⚠️ 重要**:
- 保持原有代码不变,只添加 Token 相关逻辑
- 合并到现有 onCreate 和 reportTokenToServer 中

**说明提醒事项**:
- 告知开发者必须将 Token 上报到服务器
- 服务器需要保存 Token 才能向设备发送推送

**完成 Token 接入后**:继续执行 voip 消息开发流程

```
┌─────────────────────────────────────────────────────────────────────┐
│            未接入 Token 时的处理流程                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. AI 自动检测到项目未接入 Token                                    │
│                                                                     │
│  2. ⭐ 参考 hmos-push-kit-token Skill 的代码                        │
│     - 添加缺失的 import                                            │
│     - 将 getToken 调用合并到现有 onCreate 中                        │
│     - 合并或添加 reportTokenToServer() 方法                         │
│                                                                     │
│  3. 接入完成后,自动继续 voip 消息开发                               │
│     - 创建 PushMessageAbility                                            │
│     - 创建 VoipCallService                                          │
│     - 配置视频接听页面等                                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
```

> **说明**:AI 不再需要切换到 hmos-push-kit-token Skill,而是直接在本 Skill 中完成 Token 接入,然后再继续 voip 消息的开发。

---

## voip 开发步骤

按照华为官方文档,开发步骤如下:

### 步骤一:获取 Push Token(已在上面流程中完成)

如果 AI 检测到未接入 Token,已经在前面流程中完成了 Token 接入,请继续下一步。

### 步骤二:创建 PushMessageAbility

在您的工程内创建一个UIAbility类型的组件,如PushMessageAbility.ets(在项目工程的 `src/main/ets/entryability` 目录下),负责处理应用内通话消息的主流程,并完成 `onCreate()`、`onWindowStageCreate()`、`onDestroy()` 方法的覆写。

> **⚠️ 重要**:UIAbility.onCreate是同步接口,不支持异步回调,需要在onCreate生命周期的入口,完成 `pushService.receiveMessage()` 注册,并且保证在注册前没有等待异步方法执行的调用。

**文件位置**:`src/main/ets/entryability/PushMessageAbility.ets`

```typescript
import { UIAbility } from '@kit.AbilityKit';
import { pushService } from '@kit.PushKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { VoipCallService } from '../service/VoipCallService';
import { BusinessError } from '@kit.BasicServicesKit';

export default class PushMessageAbility extends UIAbility {
  onCreate(): void {
    hilog.info(0x0000, 'testTag', `PushMessageAbility onCreate`);

    try {
      pushService.receiveMessage('VoIP', this, async (data) => {
        // process message,并建议对Callback进行try-catch
        try {
          await VoipCallService.processVoIPMainMsg(data.data, this.context);
        } catch (error) {
          hilog.error(0x0000, 'testTag', 'Failed to process VoIP message: %{public}d %{public}s',
            error.code,
            error.message);
        }
      });
    } catch (e) {
      hilog.error(0x0000, 'testTag',
        `Failed to register VOIP, error: ${e.code}, ${e.message}.`);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(0x0000, 'testTag', `PushMessageAbility onWindowStageCreate`);

    windowStage.loadContent('pages/CalleePage').catch(
      (err: BusinessError) => {
        hilog.error(0x0000, 'testTag',
          `Failed to load content, error: ${err.code}, ${err.message}.`);
      });
  }

  onDestroy(): void {
    hilog.info(0x0000, 'testTag', 'PushMessageAbility onDestroy');
  }
}
```

### 步骤三:创建 VoipCallService

在项目工程 `src/main/ets/service` 目录下创建 `VoipCallService.ets`,处理应用内通话消息。

> **注意**:需要在项目工程的 `src/main/resources/rawfile` 目录下添加 `example.png`,表示来电时的用户头像。

**文件位置**:`src/main/ets/service/VoipCallService.ets`

```typescript
import { voipCall } from '@kit.CallServiceKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { image } from '@kit.ImageKit';
import { resourceManager } from '@kit.LocalizationKit';
import { BusinessError } from '@kit.BasicServicesKit';

export interface VoipScene {
  scene: string;
}

export interface Content {
  data: string;
  header: string;
  callId: string;
}

export class VoipCallService {
  private static callId: string | undefined;

  public static async processVoIPMainMsg(data: string,
    context: common.UIAbilityContext): Promise<void> {
    hilog.info(0x0000, 'testTag',
      `Process VoIP message: ${data}`);

    let content: Content = JSON.parse(data);
    let scene: VoipScene = JSON.parse(content.data);
    let callId: string = content.callId;
    if (!callId) {
      hilog.error(0x0000, 'testTag', `CallId is null`);
    }
    VoipCallService.callId = callId;

    try {
      // 注册voipCallUiEvent事件
      voipCall.on('voipCallUiEvent', async (event) => {
        hilog.info(0x0000, 'testTag',
          `Process voip call ui event: ${JSON.stringify(event)}.`);

        await VoipCallService.processVoipCallEvent(event.voipCallUiEvent);
      });
    } catch (err) {
      let e: BusinessError = err as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to register event: %{public}d %{public}s', e.code, e.message);
    }

    const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
    // example.png表示用户头像,取值为"/resources/rawfile"路径下的文件名
    let fileData: Uint8Array = new Uint8Array(0);
    try {
      fileData = await resourceMgr.getRawFileContent('example.png');
    } catch (e) {
      hilog.error(0x0000, 'testTag', 'Failed to get raw file: %{public}d %{public}s', e.code, e.message);
    }
    const buffer = fileData.buffer;
    const imageSource: image.ImageSource = image.createImageSource(buffer);
    const pixelMap: image.PixelMap = await imageSource.createPixelMap();
    if (pixelMap) {
      pixelMap.getImageInfo((err, imageInfo) => {
        if (imageInfo) {
          hilog.info(0x0000, 'testTag',
            `User profile imageInfo: ${imageInfo.size.width} * ${imageInfo.size.height}.`);
        }
      });
    }

    // 构造上报来电的参数。注意,voipCallType.scene为您自定义的场景类型字段,从云侧推送消息时,请注意与端侧取值保持一致
    let call: voipCall.VoipCallAttribute = {
      callId: callId,
      voipCallType: scene?.scene === 'video' ? voipCall.VoipCallType.VOIP_CALL_VIDEO :
      voipCall.VoipCallType.VOIP_CALL_VOICE,
      userName: 'push',
      userProfile: pixelMap,
      abilityName: 'PushMessageAbility', // 与调用pushService.receiveMessage('VoIP'...的文件名一致
      voipCallState: voipCall.VoipCallState.VOIP_CALL_STATE_RINGING
    };

    try {
      // 上报来电
      let error = await voipCall.reportIncomingCall(call);
      hilog.info(0x0000, 'testTag',
        `ReportIncomingCall result: ${error}.`);
    } catch (err) {
      let e: BusinessError = err as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to report incoming call: %{public}d %{public}s', e.code, e.message);
    }

    // ...应用播放振动和铃声
  }

  public static async processVoipCallEvent(event: voipCall.VoipCallUiEvent) {
    try {
      switch (event) {
        case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VOICE_ANSWER:
        case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VIDEO_ANSWER:
          // 立即向Call Service Kit上报answered状态
          await voipCall.reportCallStateChange(VoipCallService.callId,
            voipCall.VoipCallState.VOIP_CALL_STATE_ANSWERED);

          // ...在应用内完成接听

          // 应用内接听后,向Call Service Kit上报active状态
          await voipCall.reportCallStateChange(VoipCallService.callId,
            voipCall.VoipCallState.VOIP_CALL_STATE_ACTIVE);
          break;
        case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_REJECT:
        case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_HANGUP:
          // ...应用内完成挂断

          // 向Call Service Kit上报通话状态
          await voipCall.reportCallStateChange(VoipCallService.callId,
            voipCall.VoipCallState.VOIP_CALL_STATE_DISCONNECTED);
          break;
        default: {
          break;
        }
      }
    } catch (err) {
      let e: BusinessError = err as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to report call state change: %{public}d %{public}s', e.code, e.message);
    }
  }

  public static close(): void {
    hilog.info(0x0000, 'testTag', `Close VoIP`);

    VoipCallService.processVoipCallEvent(voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_HANGUP);
    try {
      voipCall.off('voipCallUiEvent');
    } catch (err) {
      let e: BusinessError = err as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to unregister event: %{public}d %{public}s', e.code, e.message);
    }
  }
}
```

**关键说明**:
1. 应用需要在10秒内调用 `voipCall.reportIncomingCall()` 接口上报通话来电状态
2. `voipCall.reportIncomingCall()` 接口入参中的callId需要使用 `receiveMessage()` 回调中的callId
3. 如果应用来电消息建立失败,需要调用 `voipCall.reportIncomingCallError()` 通知来电消息建立失败
4. 在接听状态回调中,应用在建立连接成功之后,需要调用 `voipCall.reportCallStateChange()` 接口上报通话激活状态
5. 在拒绝接听状态回调中,应用断开和服务器的连接之后,需要调用 `voipCall.reportCallStateChange()` 接口上报通话断开状态

### 步骤四:创建视频接听页面 CalleePage ⭐必做

> **⚠️ 强制要求**:此步骤**必须**完成!用户在解锁状态下点击视频接听时会拉起此页面,如果页面不存在会导致接听失败!

在项目工程的 `src/main/ets/pages` 目录添加:视频接听页面CalleePage.ets。

**文件位置**:`src/main/ets/pages/CalleePage.ets`

```typescript
import CallComponent from '../component/CallComponent';
import { hilog } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
struct CalleePage {
  @StorageLink('close') @Watch('close') end: boolean | undefined = undefined;

  aboutToAppear() {
    hilog.info(0x0000, 'testTag', `CalleePage aboutToAppear`);

    this.end = false;
  }

  private close() {
    if (this.end) {
      hilog.info(0x0000, 'testTag', `CalleePage close`);

      this.getUIContext().getRouter().back();
    }
  }

  aboutToDisappear() {
    hilog.info(0x0000, 'testTag', `CalleePage aboutToDisappear`);
  }

  build() {
    Column() {
      CallComponent({})
    }
  }
}
```

### 步骤五:创建 CallComponent 呼叫控制组件

在项目工程的 `src/main/ets/component` 目录创建 CallComponent.ets。

**文件位置**:`src/main/ets/component/CallComponent.ets`

```typescript
import { VoipCallService } from '../service/VoipCallService';
import { voipCall } from '@kit.CallServiceKit';

@Component
export default struct CallComponent {
  @StorageLink('close') end: boolean | undefined = undefined;

  build() {
    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
      Row() {
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)

      Row({ space: 30 }) {

        Column() {
          Button()
            .width(80)
            .height(80)
            .backgroundColor(Color.Green)
            .onClick(() => {
              VoipCallService.processVoipCallEvent(voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VIDEO_ANSWER);
            })

          Text('Answer').fontColor(Color.White).padding({ top: 5 })
        }

        Column() {
          Button()
            .width(80)
            .height(80)
            .backgroundColor(Color.Red)
            .onClick(() => {
              this.end = true;
              VoipCallService.close();
            })

          Text('Hang Up').fontColor(Color.White).padding({ top: 5 })
        }

      }
      .width('100%')
      .justifyContent(FlexAlign.Center)
    }
    .padding('30 10')
    .backgroundColor(Color.Black)
  }
}
```

### 步骤六:配置 main_pages.json ⭐必做

> **⚠️ 强制要求**:此步骤**必须**完成!必须在 main_pages.json 中添加 CalleePage 页面,否则页面无法被系统找到!

在项目工程的 `src/main/resources/base/profile/main_pages.json` 添加page目录:

```json
{
  "src": [
    "pages/Index",
    "pages/CalleePage"
  ]
}
```

### 步骤七:配置 module.json5

在项目工程的 `src/main/module.json5` 文件的 `abilities` 模块中配置PushMessageAbility的 `actions` 信息:

```json5
"abilities": [
  {
    "name": "PushMessageAbility",
    "srcEntry": "./ets/entryability/PushMessageAbility.ets",
    "launchType": "singleton",
    "startWindowIcon": "$media:startIcon",
    "startWindowBackground": "$color:start_window_background",
    "exported": false,
    "skills": [
      // 保持现有skill对象不变
      // 新增一个独立的skill对象,配置actions参数
      {
        "actions": ["action.ohos.push.listener"]
      }
    ]
  }
]
```

> **⚠️ 配置警告**:
> - **label 字段**:默认**不要添加**!如果需要添加,必须使用 `$string:xxx` 格式,并在 `src/main/resources/base/element/string.json` 中添加对应的资源名
> - **description 字段**:可以使用普通字符串,也可以不添加
> - **⭐ startWindowIcon 和 startWindowBackground 字段**:**必须填写**,不能遗漏!
>   - `startWindowIcon`:应用启动图标,如 `$media:startIcon`
>   - `startWindowBackground`:启动背景色,如 `$color:start_window_background`
>   - 如果遗漏会导致应用无法正常启动
>
> 正确示例(不添加 label):
> ```json5
> {
>   "name": "PushMessageAbility",
>   "srcEntry": "./ets/entryability/PushMessageAbility.ets",
>   "launchType": "singleton",
>   "exported": false,
>   "skills": [{ "actions": ["action.ohos.push.listener"] }]
> }
> ```
> 
> 正确示例(添加 label):
> ```json5
> // 1. 先在 string.json 中添加资源
> {
>   "string": [{ "name": "PushMessageAbility_label", "value": "VoIP来电" }]
> }
> 
> // 2. 然后在 module.json5 中使用
> {
>   "name": "PushMessageAbility",
>   "label": "$string:PushMessageAbility_label",
>   ...
> }
> ```
> 
> 错误示例(会编译报错):
> ```json5
> {
>   "label": "PushMessageAbility"  // ❌ 错误!必须用 $string:xxx 格式
> }
> ```

> **说明**:actions内容为 `action.ohos.push.listener`,有且只能有一个ability定义该action,若同时添加uris参数,则uris内容需为空。

### 步骤八:服务端调用 REST API 发送 voip 消息

应用服务端调用REST API推送消息,请求示例如下:

```json
// Request URL
POST "https://push-api.cloud.huawei.com/v3/[projectId]/messages:send"

// Request Header
Content-Type: application/json
Authorization: Bearer eyJr*****OiIx---****.eyJh*****iJodHR--***.QRod*****4Gp---****
push-type: 10

// Request Body
{
  "pushOptions": {
    "ttl": 30
  },
  "payload": {
    "extraData": "{\"scene\": \"voice\"}"
  },
  "target": {
    "token": ["MAMzLg**********aZW"]
  }
}
```

**参数说明**:
- push-type:10表示应用内通话消息场景
- ttl:消息缓存时间,建议设置为30~60秒
- extraData:携带的额外数据,字符串类型。`scene` 字段为 `voice` 表示语音通话,为 `video` 表示视频通话

---

## 重要提醒

> **⚠️ 权益申请已在「权益申请提醒」章节中详细说明,请在开始开发前确认已完成申请!**

### callKit (Call Service Kit) 接入说明

本功能使用 **Call Service Kit**(`@kit.CallServiceKit`)的 `voipCall` 模块实现,**不是** callKit。

- 正确导入:`import { voipCall } from '@kit.CallServiceKit';`
- 主要接口:
  - `voipCall.on('voipCallUiEvent', callback)` - 注册通话状态监听
  - `voipCall.reportIncomingCall(call)` - 上报来电状态
  - `voipCall.reportCallStateChange(callId, state)` - 上报通话状态变化
  - `voipCall.reportIncomingCallError(callId, errorCode)` - 上报来电错误

> **参考**:华为官方文档 - [推送应用内通话消息](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-voip)

---

## ⚠️ 重要:多个场景化消息的配置

根据华为官方文档,一个项目中**有且只能有一个 ability** 配置 `action.ohos.push.listener`。

**这意味着**:
- 不要为每种消息类型创建单独的 Ability
- 所有的 `receiveMessage` 注册应该放在**同一个 Ability** 中
- module.json5 中只配置一个 ability 包含 `action.ohos.push.listener`

**⚠️ 特别提醒:接入通知消息或后台消息时,请加载对应的专门 Skill**

虽然同一个 Ability 中可以注册多种消息类型,但 **每种消息类型都有各自完整的开发流程和注意事项**,不能互相替代:

- **通知消息**:调用 `receiveMessage('DEFAULT', ...)`,配合 `requestEnableNotification()` 授权
- **voip 消息**:调用 `receiveMessage('VoIP', ...)`,还需要创建 VoipCallService、CalleePage 页面、注册 voipCallUiEvent 事件、上报来电状态等完整流程
- **后台消息**:调用 `receiveMessage('BACKGROUND', ...)`,还需要实现数据静默更新和缓存策略

因此:
- 接入**通知消息** → 请加载 **hmos-push-kit-notification** Skill
- 接入**后台消息** → 请加载 **hmos-push-kit-background** Skill
- **不要**仅凭本 skill 中关于"同一 ability 接收多种消息类型"的说明就自行编写通知消息或后台消息代码

**⚠️ 示例:不能这样做**

以下示例展示的是**错误做法**——仅凭 voip skill 的说明就自行添加其他消息类型的代码:

```typescript
// ❌ 错误示例:仅凭 voip skill 的说明就自行添加后台消息代码
pushService.receiveMessage('BACKGROUND', this, (payload) => {
  // 没有实现数据静默更新逻辑
  // 没有实现缓存策略
  // 后台消息有完整独立的开发流程,必须参考专门的 background skill
});
```

**正确做法**:请明确告诉我具体需要接入哪种消息类型,我会加载对应的专门 Skill,在本项目的同一个 PushMessageAbility 中添加相应逻辑。

---

## 常见问题排查

| 问题 | 可能原因 | 处理步骤 |
|-----|---------|---------|
| 收不到 voip 消息 | 未申请 voip 权益 | 在 AppGallery Connect 申请「推送应用内通话消息」权益 |
| 来电通知不显示 | 10秒内未调用reportIncomingCall | 确保在10秒内调用 |
| 设备不支持 voip | 设备不支持 voip 功能 | 检查设备是否支持 voip 功能 |

---

## 常见错误码

接入 VOIP 消息后,如遇到以下错误码,请参考:

| 错误码 | 可能原因 | 解决方法 |
|-------|---------|---------|
| 1000900010 | 推送服务未开通或 voip 权益未申请 | 在 AppGallery Connect 开通推送服务并申请 VOIP 权益 |
| 1000900011 | 应用签名不匹配 | 检查签名配置是否正确 |
| 1000900012 | 网络问题 | 检查网络连接 |
| 1000900013 | 请求超时 | 增加超时时间或检查网络 |
| 1000900014 | 权限不足 | 检查 module.json5 权限配置 |
| 1000900001 | 推送服务内部错误 | 重试或联系华为支持 |
| 1000900002 | 参数错误 | 检查请求参数是否正确 |
| 1000900003 | Token 无效 | 检查 Token 是否正确 |

> 完整错误码列表请参考 `references/push-error-codes.md`

---

## ✅ AI 开发完成后的自检清单

> **AI 必须在完成 voip 接入开发后,逐项确认以下所有内容:**

### 代码文件检查:
- [ ] `src/main/ets/entryability/PushMessageAbility.ets` 已创建
- [ ] `src/main/ets/service/VoipCallService.ets` 已创建
- [ ] `src/main/ets/pages/CalleePage.ets` 已创建 ⭐重点检查
- [ ] `src/main/ets/component/CallComponent.ets` 已创建

### 资源配置检查:
- [ ] `src/main/resources/rawfile/example.png` 已添加
- [ ] `src/main/resources/base/profile/main_pages.json` 已添加 `pages/CalleePage` ⭐重点检查
- [ ] `src/main/module.json5` 的 abilities 中已配置 `action.ohos.push.listener`

### 权益检查:
- [ ] 已在 AppGallery Connect 开通推送服务 ⭐
- [ ] 已在 AppGallery Connect 申请「应用内通话消息」权益并审核通过 ⭐

### 关键逻辑检查:
- [ ] `pushService.receiveMessage('VoIP', ...)` 已在 `onCreate()` 入口同步调用
- [ ] `voipCall.reportIncomingCall()` 在收到消息后 10 秒内调用
- [ ] `voipCall.on('voipCallUiEvent', ...)` 已注册通话状态监听
- [ ] 语音/视频场景判断逻辑正确(`scene === 'video'` 为视频)

### ⚠️ module.json5 配置检查:
- [ ] **label 字段**:默认不添加,如果添加必须用 `$string:xxx` 格式
- [ ] description 字段:可以使用普通字符串,也可以不添加
- [ ] 如果添加了 label,确认已在 `string.json` 中添加对应资源名

---

> **⚠️ 如果 AI 遗漏了上述任何一项,请立即补充!**

---

## ⚠️ 重要提醒汇总

> **⭐ 接入 voip 完成后,请务必逐项完成以下所有提醒事项!**

```
┌─────────────────────────────────────────────────────────────────────┐
│           ⭐ 重要:接入 voip 后必须完成的权益申请和Token上报          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  【权益申请 - 必须完成】⭐                                            │
│                                                                     │
│  1. 开通推送服务(必须)⭐                                            │
│     您必须前往 AppGallery Connect 开通推送服务,否则无法获取有效 Token │
│     参考文档:                                                       │
│     https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-config-setting  │
│                                                                     │
│  2. 申请应用内通话权益(必须)⭐                                      │
│     VoIP 消息需要额外的「应用内通话消息」权益才能正常使用              │
│     参考文档:                                                       │
│     https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-apply-right  │
│                                                                     │
│  ⚠️ 未申请权益的应用,默认推送的都是资讯营销类消息,                   │
│     会受到「每日每设备 2-5 条」的限制,且来电通知不会正常显示!        │
│                                                                     │
│  ───────────────────────────────────────────────────────────────    │
│                                                                     │
│  【Token 上报 - 必须完成】⭐                                          │
│                                                                     │
│  ⚠️ 如果本次接入过程中,因项目未接入Token而自动帮您接入了Token,       │
│     您必须完成以下工作:                                              │
│                                                                     │
│  在本次接入过程中,AI 已自动将 `getToken()` 调用添加到您的项目中。    │
│  但是 `reportTokenToServer()` 方法中的上报逻辑尚未完成!              │
│                                                                     │
│  您必须实现 Token 上报到服务器的逻辑:                                │
│                                                                     │
│  ```typescript                                                      │
│  reportTokenToServer(token: string): void {                         │
│    hilog.info(0x0000, 'testTag', 'Reporting token to server: %{public}s', token);    │
│    // TODO: 将 Token 上报到您的服务器(必需步骤)                      │
│  }                                                                   │
│  ```                                                                 │
│                                                                     │
│  ⚠️ 重要提示:完成 Token 上报后,您的推送功能才能真正工作!            │
│                                                                     │
│  ───────────────────────────────────────────────────────────────    │
│                                                                     │
│  【频控规则】                                                        │
│  • 调测阶段:每个项目每日全网最多可推送1000条测试消息,                 │
│    发送测试消息需设置testMessage为true                                │
│  • 正式发布阶段:单设备单应用下每日推送消息总条数受设备消息频控限制     │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
```

> **⚠️ 请务必完成上述所有提醒事项,遗漏任何一项都可能导致推送功能无法正常工作!**

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…