• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用AppServiceExtensionAbility组件实现后台服务
2<!--Kit: Ability Kit-->
3<!--Subsystem: Ability-->
4<!--Owner: @yewei0794-->
5<!--Designer: @jsjzju-->
6<!--Tester: @lixueqing513-->
7<!--Adviser: @huipeizi-->
8
9## 概述
10
11从API version 20开始,支持开发者使用[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件,为应用提供后台服务能力,其他三方应用可通过启动或连接该AppServiceExtensionAbility组件获取相应的服务。
12例如,企业部署的数据防泄漏 (DLP) 软件需要能够长期无界面长期运行,持续监听文件操作、网络流量,并拦截违规行为,可以使用AppServiceExtensionAbility组件来实现其核心的后台监控服务。
13> **说明**
14>
15> 本文将被启动或被连接的AppServiceExtensionAbility组件称为服务端,将启动或连接AppServiceExtensionAbility组件的应用组件(当前仅支持UIAbility)称为客户端。
16
17## 约束与限制
18
19### 设备限制
20
21AppServiceExtensionAbility组件当前仅支持2in1设备。
22
23### 规格限制
24
25- 应用集成AppServiceExtensionAbility组件需要申请ACL权限(ohos.permission.SUPPORT_APP_SERVICE_EXTENSION)。该ACL权限当前只对企业普通应用开放申请。
26
27- AppServiceExtensionAbility组件内不支持调用[window](../reference/apis-arkui/arkts-apis-window.md)相关API。
28
29## 运作机制
30
31开发者可以在[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)中以[启动](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#startappserviceextensionability20)或[连接](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)的方式来拉起AppServiceExtensionAbility组件。
32
33- **启动:** 客户端必须为AppServiceExtensionAbility所属应用或者在AppServiceExtensionAbility支持的应用清单(即[extensionAbilities标签](../quick-start/module-configuration-file.md#extensionabilities标签)的appIdentifierAllowList属性)中的应用才能调用[startAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#startappserviceextensionability20)接口。
34- **连接:** 如果[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)实例未启动,客户端必须为AppServiceExtensionAbility所属应用或者在AppServiceExtensionAbility支持的应用清单(即[extensionAbilities标签](../quick-start/module-configuration-file.md#extensionabilities标签)的appIdentifierAllowList属性)中的应用才能调用[connectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)接口。如果实例已启动,则没有上述限制。
35
36下表展示了拉起和连接的几种场景:
37
38
39> **说明**
40>
41> “客户端是否可信”为是时,表示客户端属于服务端所属应用或已配置在appIdentifierAllowList中。为否时,表示客户端不属于服务端所属应用且未配置在appIdentifierAllowList中。
42
43| 客户端操作 | 服务端状态 | 客户端是否可信 | 结果说明 |
44| --------- | --------- | -------------------------------------------- | ---- |
45| startAppServiceExtensionAbility | 未启动     | 是                                       | 成功,服务端通过start方式启动,服务端状态变为已启动。 |
46| startAppServiceExtensionAbility | 未启动     | 否                                       | 失败,客户端不在允许列表中,无法调用启动服务。 |
47| startAppServiceExtensionAbility | 已启动     | 是                                       | 成功,服务端已经启动,start操作直接返回成功。 |
48| startAppServiceExtensionAbility | 已启动     | 否                                       | 失败,客户端不在允许列表中,无法调用启动服务。 |
49| connectAppServiceExtensionAbility | 未启动     | 是                                       | 成功,服务端通过connect方式启动,并建立连接。 |
50| connectAppServiceExtensionAbility | 未启动     | 否                                       | 失败,客户端不在允许列表中,无法启动服务端。 |
51| connectAppServiceExtensionAbility | 已启动     | 是                                       | 成功,服务端已启动,直接建立连接。 |
52| connectAppServiceExtensionAbility | 已启动     | 否                                       | 成功,服务端已启动,直接建立连接。 |
53
54
55## 实现一个后台服务
56
57在DevEco Studio工程中手动新建一个AppServiceExtensionAbility组件,具体步骤如下:
58
591. 在工程Module对应的ets目录下,右键选择“New &gt; Directory”,新建一个目录并命名为MyAppServiceExtAbility。
60
612. 在MyAppServiceExtAbility目录,右键选择“New &gt; ArkTS File”,新建一个文件并命名为MyAppServiceExtAbility.ets62![](figures/app-service-extension-ability-create-new-file.png)
63
64    其目录结构如下所示:
65
66    ```
67    ├── ets
68    │ ├── MyAppServiceExtAbility
69    │ │   ├── MyAppServiceExtAbility.ets
7071    ```
72
733. 在MyAppServiceExtAbility.ets文件中,增加导入[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)的依赖包,自定义类继承AppServiceExtensionAbility组件并实现生命周期回调。
74
75    ```ts
76    import { AppServiceExtensionAbility, Want } from '@kit.AbilityKit';
77    import { rpc } from '@kit.IPCKit';
78    import { hilog } from '@kit.PerformanceAnalysisKit';
79
80    const TAG: string = '[MyAppServiceExtAbility]';
81    const DOMAIN_NUMBER: number = 0xFF00;
82
83    class StubTest extends rpc.RemoteObject {
84      constructor(des: string) {
85        super(des);
86      }
87
88      onRemoteMessageRequest(code: number,
89        data: rpc.MessageSequence,
90        reply: rpc.MessageSequence,
91        options: rpc.MessageOption): boolean | Promise<boolean> {
92        // 处理客户端发送的消息
93        return true;
94      }
95    }
96
97    export default class MyAppServiceExtAbility extends AppServiceExtensionAbility {
98      onCreate(want: Want): void {
99        let appServiceExtensionContext = this.context;
100        hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`);
101      }
102
103      onRequest(want: Want, startId: number): void {
104        hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`);
105      }
106
107      onConnect(want: Want): rpc.RemoteObject {
108        hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`);
109        return new StubTest("test");
110      }
111
112      onDisconnect(want: Want): void {
113        hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`);
114      }
115
116      onDestroy(): void {
117        hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy');
118      }
119    };
120    ```
121
1224. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册AppServiceExtensionAbility组件,type标签需要设置为“appService”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。
123
124    ```json
125    {
126      "module": {
127        // ...
128        "extensionAbilities": [
129          {
130            "name": "MyAppServiceExtAbility",
131            "description": "appService",
132            "type": "appService",
133            "exported": true,
134            "srcEntry": "./ets/MyAppServiceExtAbility/MyAppServiceExtAbility.ets",
135            "appIdentifierAllowList": [
136              // 此处填写允许启动该后台服务的客户端应用的appIdentifier列表
137            ],
138          }
139        ]
140      }
141    }
142    ```
143
144## 启动一个后台服务
145
146应用通过[startAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#startappserviceextensionability20)方法启动一个后台服务,服务的[onRequest()](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md#onrequest)回调就会被调用,并在该回调方法中接收到调用者传递过来的[Want](../reference/apis-ability-kit/js-apis-app-ability-want.md)对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用[AppServiceExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-appServiceExtensionContext.md)的[terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-appServiceExtensionContext.md#terminateself)来自行停止,或者由另一个组件调用[stopAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#stopappserviceextensionability20)来将其停止。
147
148> **说明:**
149>
150> AppServiceExtensionAbility组件以start方式启动,并且没有连接的时候,AppServiceExtensionAbility组件进程可能被挂起(请参考[Background Tasks Kit简介](../task-management/background-task-overview.md))。
151
152- 在应用中启动一个新的[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
153
154  ```ts
155  import { common, Want } from '@kit.AbilityKit';
156  import { hilog } from '@kit.PerformanceAnalysisKit';
157  import { BusinessError } from '@kit.BasicServicesKit';
158
159  const TAG: string = '[Page_AppServiceExtensionAbility]';
160  const DOMAIN_NUMBER: number = 0xFF00;
161
162  @Entry
163  @Component
164  struct Page_AppServiceExtensionAbility {
165    build() {
166      Column() {
167        //...
168        List({ initialIndex: 0 }) {
169          ListItem() {
170            Row() {
171              //...
172            }
173            .onClick(() => {
174              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
175              let want: Want = {
176                deviceId: '',
177                bundleName: 'com.samples.stagemodelabilitydevelop',
178                abilityName: 'MyAppServiceExtAbility'
179              };
180              context.startAppServiceExtensionAbility(want).then(() => {
181                hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting AppServiceExtensionAbility.');
182                // 成功启动后台服务
183                this.getUIContext().getPromptAction().showToast({
184                  message: 'SuccessfullyStartBackendService'
185                });
186              }).catch((err: BusinessError) => {
187                hilog.error(DOMAIN_NUMBER, TAG,
188                  `Failed to start AppServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
189              });
190            })
191          }
192
193          //...
194        }
195
196        //...
197      }
198
199      //...
200    }
201  }
202  ```
203
204- 在应用中停止一个已启动的[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件。
205
206  ```ts
207  import { common, Want } from '@kit.AbilityKit';
208  import { hilog } from '@kit.PerformanceAnalysisKit';
209  import { BusinessError } from '@kit.BasicServicesKit';
210
211  const TAG: string = '[Page_AppServiceExtensionAbility]';
212  const DOMAIN_NUMBER: number = 0xFF00;
213
214  @Entry
215  @Component
216  struct Page_AppServiceExtensionAbility {
217    build() {
218      Column() {
219        //...
220        List({ initialIndex: 0 }) {
221          ListItem() {
222            Row() {
223              //...
224            }
225            .onClick(() => {
226              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
227              let want: Want = {
228                deviceId: '',
229                bundleName: 'com.samples.stagemodelabilitydevelop',
230                abilityName: 'MyAppServiceExtAbility'
231              };
232              context.stopAppServiceExtensionAbility(want).then(() => {
233                hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping AppServiceExtensionAbility.');
234                this.getUIContext().getPromptAction().showToast({
235                  message: 'SuccessfullyStoppedAStartedBackendService'
236                });
237              }).catch((err: BusinessError) => {
238                hilog.error(DOMAIN_NUMBER, TAG,
239                  `Failed to stop AppServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
240              });
241            })
242          }
243
244          //...
245        }
246
247        //...
248      }
249
250      //...
251    }
252  }
253  ```
254
255- 已启动的[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件停止自身。
256
257    ```ts
258    import { AppServiceExtensionAbility } from '@kit.AbilityKit';
259    import { BusinessError } from '@kit.BasicServicesKit';
260    import { hilog } from '@kit.PerformanceAnalysisKit';
261
262    const TAG: string = '[MyAppServiceExtAbility]';
263
264    export default class MyAppServiceExtAbility extends AppServiceExtensionAbility {
265      onCreate(want: Want) {
266      // 执行业务逻辑
267        this.context.terminateSelf().then(() => {
268          hilog.info(0x0000, TAG, '----------- terminateSelf succeed -----------');
269        }).catch((error: BusinessError) => {
270          hilog.error(0x0000, TAG, `terminateSelf failed, error.code: ${error.code}, error.message: $   {error.message}`);
271        });
272      }
273    }
274    ```
275
276## 连接一个后台服务
277
278### 客户端连接服务端
279
280客户端可以通过[connectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)连接服务端(在Want对象中指定连接的目标服务),服务端的[onConnect()](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md#onconnect)就会被调用,并在该回调方法中接收到客户端传递过来的[Want](../reference/apis-ability-kit/js-apis-app-ability-want.md)对象。
281
282服务端的AppServiceExtensionAbility组件会在onConnect()中返回[IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)对象给客户端[ConnectOptions](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md)的[onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect)方法。开发者通过该IRemoteObject定义通信接口,实现客户端与服务端的RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务端的交互后,客户端需要通过调用[disconnectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#disconnectappserviceextensionability20)来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。
283
284- 使用[connectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)建立与后台服务的连接。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
285
286  ```ts
287  import { common, Want } from '@kit.AbilityKit';
288  import { rpc } from '@kit.IPCKit';
289  import { hilog } from '@kit.PerformanceAnalysisKit';
290
291  const TAG: string = '[Page_AppServiceExtensionAbility]';
292  const DOMAIN_NUMBER: number = 0xFF00;
293
294  let connectionId: number;
295  let want: Want = {
296    deviceId: '',
297    bundleName: 'com.samples.stagemodelabilitydevelop',
298    abilityName: 'MyAppServiceExtAbility'
299  };
300
301  let options: common.ConnectOptions = {
302    onConnect(elementName, remote: rpc.IRemoteObject): void {
303      hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
304      if (remote === null) {
305        hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
306        return;
307      }
308      // 通过remote进行通信
309    },
310    onDisconnect(elementName): void {
311      hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
312    },
313    onFailed(code: number): void {
314      hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
315    }
316  };
317
318  @Entry
319  @Component
320  struct Page_AppServiceExtensionAbility {
321    build() {
322      Column() {
323        //...
324        List({ initialIndex: 0 }) {
325          ListItem() {
326            Row() {
327              //...
328            }
329            .onClick(() => {
330              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
331              // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
332              connectionId = context.connectAppServiceExtensionAbility(want, options);
333              // 成功连接后台服务
334              this.getUIContext().getPromptAction().showToast({
335                message: 'SuccessfullyConnectBackendService'
336              });
337              hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);
338            })
339          }
340
341          //...
342        }
343
344        //...
345      }
346
347      //...
348    }
349  }
350  ```
351
352- 使用[disconnectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#disconnectappserviceextensionability20)断开与后台服务的连接。
353
354  ```ts
355  import { common } from '@kit.AbilityKit';
356  import { hilog } from '@kit.PerformanceAnalysisKit';
357  import { BusinessError } from '@kit.BasicServicesKit';
358
359  const TAG: string = '[Page_AppServiceExtensionAbility]';
360  const DOMAIN_NUMBER: number = 0xFF00;
361
362  let connectionId: number;
363
364  @Entry
365  @Component
366  struct Page_AppServiceExtensionAbility {
367    build() {
368      Column() {
369        //...
370        List({ initialIndex: 0 }) {
371          ListItem() {
372            Row() {
373              //...
374            }
375            .onClick(() => {
376              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
377              // connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护
378              context.disconnectAppServiceExtensionAbility(connectionId).then(() => {
379                hilog.info(DOMAIN_NUMBER, TAG, 'disconnectAppServiceExtensionAbility success');
380                // 成功断连后台服务
381                this.getUIContext().getPromptAction().showToast({
382                  message: 'SuccessfullyDisconnectBackendService'
383                });
384              }).catch((error: BusinessError) => {
385                hilog.error(DOMAIN_NUMBER, TAG, 'disconnectAppServiceExtensionAbility failed');
386              });
387            })
388          }
389
390          //...
391        }
392
393        //...
394      }
395
396      //...
397    }
398  }
399  ```
400
401### 客户端与服务端通信
402
403客户端在[onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect)中获取到[rpc.IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)对象后便可与服务端进行通信。
404
405**客户端**:使用[sendMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#sendmessagerequest9)接口向服务端发送消息。
406
407```ts
408import { common } from '@kit.AbilityKit';
409import { rpc } from '@kit.IPCKit';
410import { hilog } from '@kit.PerformanceAnalysisKit';
411import { BusinessError } from '@kit.BasicServicesKit';
412
413const TAG: string = '[Page_AppServiceExtensionAbility]';
414const DOMAIN_NUMBER: number = 0xFF00;
415const REQUEST_CODE = 1;
416let connectionId: number;
417let want: Want = {
418  deviceId: '',
419  bundleName: 'com.samples.stagemodelabilitydevelop',
420  abilityName: 'MyAppServiceExtAbility'
421};
422let options: common.ConnectOptions = {
423  onConnect(elementName, remote): void {
424    hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
425    if (remote === null) {
426      hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
427      return;
428    }
429    let option = new rpc.MessageOption();
430    let data = new rpc.MessageSequence();
431    let reply = new rpc.MessageSequence();
432
433    // 写入请求数据
434    data.writeInt(1);
435    data.writeInt(2);
436
437    remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => {
438      if (ret.errCode === 0) {
439        hilog.info(DOMAIN_NUMBER, TAG, `sendRequest got result`);
440        let sum = ret.reply.readInt();
441        hilog.info(DOMAIN_NUMBER, TAG, `sendRequest success, sum:${sum}`);
442      } else {
443        hilog.error(DOMAIN_NUMBER, TAG, `sendRequest failed`);
444      }
445    }).catch((error: BusinessError) => {
446      hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`);
447    });
448  },
449  onDisconnect(elementName): void {
450    hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
451  },
452  onFailed(code): void {
453    hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback');
454  }
455};
456
457// 调用connectAppServiceExtensionAbility相关代码
458//...
459
460@Entry
461@Component
462struct Page_AppServiceExtensionAbility {
463  build() {
464    Column() {
465      //...
466      List({ initialIndex: 0 }) {
467        ListItem() {
468          Row() {
469            //...
470          }
471          .onClick(() => {
472            let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
473            connectionId = context.connectAppServiceExtensionAbility(want, options);
474            hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);
475          })
476        }
477      }
478      //...
479    }
480  }
481}
482```
483
484**服务端**:使用[onRemoteMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#onremotemessagerequest9)接口接收客户端发送的消息。
485
486```ts
487import { AppServiceExtensionAbility } from '@kit.AbilityKit';
488import { rpc } from '@kit.IPCKit';
489import { hilog } from '@kit.PerformanceAnalysisKit';
490
491const TAG: string = '[MyAppServiceExtAbility]';
492const DOMAIN_NUMBER: number = 0xFF00;
493
494// 开发者需要在这个类型里对接口进行实现
495class Stub extends rpc.RemoteObject {
496  onRemoteMessageRequest(code: number,
497    data: rpc.MessageSequence,
498    reply: rpc.MessageSequence,
499    options: rpc.MessageOption): boolean | Promise<boolean> {
500    hilog.info(DOMAIN_NUMBER, TAG, 'onRemoteMessageRequest');
501    let sum = data.readInt() + data.readInt();
502    reply.writeInt(sum);
503    return true;
504  }
505}
506
507// 服务端实现
508export default class MyAppServiceExtAbility extends AppServiceExtensionAbility {
509  onCreate(want: Want): void {
510    hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onCreate');
511  }
512
513  onDestroy(): void {
514    hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onDestroy');
515  }
516
517  onConnect(want: Want): rpc.RemoteObject {
518    hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onConnect');
519    return new Stub('test');
520  }
521
522  onDisconnect(): void {
523    hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onDisconnect');
524  }
525}
526```
527
528### 服务端对客户端身份校验
529
530部分开发者需要使用AppServiceExtensionAbility组件提供一些较为敏感的服务,可以通过如下方式对客户端身份进行校验。
531
532<!--Del-->
533**通过callerUid识别客户端应用**
534
535通过调用[getCallingUid()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallinguid)接口获取客户端的uid,再调用[getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid14)接口获取uid对应的bundleName,从而识别客户端身份。此处需要注意的是[getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid14)是一个异步接口,因此服务端无法将校验结果返回给客户端,这种校验方式适合客户端向服务端发起执行异步任务请求的场景,示例代码如下:
536
537```ts
538import { AppServiceExtensionAbility } from '@kit.AbilityKit';
539import { bundleManager } from '@kit.AbilityKit';
540import { rpc } from '@kit.IPCKit';
541import { osAccount, BusinessError } from '@kit.BasicServicesKit';
542
543class Stub extends rpc.RemoteObject {
544  private validAppIdentifier: string = "your_valid_app_identifier_here";
545
546  onRemoteMessageRequest(
547    code: number,
548    data: rpc.MessageSequence,
549    reply: rpc.MessageSequence,
550    options: rpc.MessageOption): boolean | Promise<boolean> {
551    this.verifyClientIdentity().then((isValid: boolean) => {
552      if (isValid) {
553        console.info('Client authentication PASSED');
554      } else {
555        console.error('Client authentication FAILED');
556      }
557    }).catch((err: BusinessError) => {
558      console.error(`Authentication error: ${err.code}, ${err.message}`);
559    });
560    return true;
561  }
562
563  private async verifyClientIdentity(): Promise<boolean> {
564    try {
565      const callerUid: number = rpc.IPCSkeleton.getCallingUid();
566      console.info(`Caller UID: ${callerUid}`);
567
568      const userId: number = await this.getUserIdByUid(callerUid);
569      console.info(`User ID: ${userId}`);
570
571      const bundleName: string = await bundleManager.getBundleNameByUid(callerUid);
572      console.info(`Bundle Name: ${bundleName}`);
573
574      const bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO;
575      const bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfo(bundleName, bundleFlags, userId);
576
577      if (bundleInfo.signatureInfo && bundleInfo.signatureInfo.appIdentifier) {
578        const appIdentifier: string = bundleInfo.signatureInfo.appIdentifier;
579        console.info(`App Identifier: ${appIdentifier}`);
580        return appIdentifier === this.validAppIdentifier;
581      }
582      return false;
583    } catch (err) {
584      if (err instanceof Error) {
585        console.error(`Verification failed: ${err.message}`);
586      } else {
587        console.error(`Verification failed: ${String(err)}`);
588      }
589      return false;
590    }
591  }
592
593  private async getUserIdByUid(uid: number): Promise<number> {
594    try {
595      const accountManager = osAccount.getAccountManager();
596      const userId: number = await accountManager.getOsAccountLocalIdForUid(uid);
597      return userId;
598    } catch (err) {
599      if (err instanceof Error) {
600        console.error(`Get userId failed: ${err.message}`);
601        throw err;
602      } else {
603        const error = new Error(String(err));
604        console.error(`Get userId failed: ${error.message}`);
605        throw error;
606      }
607    }
608  }
609}
610
611export default class MyAppServiceExtAbility extends AppServiceExtensionAbility {
612  onConnect(want: Want): rpc.RemoteObject {
613    return new Stub('test');
614  }
615  // 其他生命周期
616}
617```
618<!--DelEnd-->
619
620**通过callerTokenId对客户端进行鉴权**
621
622通过调用[getCallingTokenId()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingtokenid8)接口获取客户端的tokenID,再调用[verifyAccessTokenSync()](../reference/apis-ability-kit/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9)接口判断客户端是否有某个具体权限,由于当前不支持自定义权限,因此只能校验当前[系统所定义的权限](../security/AccessToken/app-permissions.md)。示例代码如下:
623
624```ts
625import { AppServiceExtensionAbility } from '@kit.AbilityKit';
626import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';
627import { rpc } from '@kit.IPCKit';
628import { hilog } from '@kit.PerformanceAnalysisKit';
629import { BusinessError } from '@kit.BasicServicesKit';
630
631const TAG: string = '[AppServiceExtImpl]';
632const DOMAIN_NUMBER: number = 0xFF00;
633
634// 开发者需要在这个类里进行实现
635
636class Stub extends rpc.RemoteObject {
637  onRemoteMessageRequest(
638    code: number,
639    data: rpc.MessageSequence,
640    reply: rpc.MessageSequence,
641    options: rpc.MessageOption): boolean | Promise<boolean> {
642    // 开发者自行实现业务逻辑
643    hilog.info(DOMAIN_NUMBER, TAG, `onRemoteMessageRequest: ${data}`);
644    let callerUid = rpc.IPCSkeleton.getCallingUid();
645    bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {
646      hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
647      // 对客户端包名进行识别
648      if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过
649        hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
650        return;
651      }
652    // 识别通过,执行正常业务逻辑
653    }).catch((err: BusinessError) => {
654      hilog.error(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
655    });
656
657    let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();
658    let accessManager = abilityAccessCtrl.createAtManager();
659    // 所校验的具体权限由开发者自行选择,此处ohos.permission.GET_BUNDLE_INFO_PRIVILEGED只作为示例
660    let grantStatus = accessManager.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED');
661    if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
662      hilog.error(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED');
663      return false;
664    }
665    hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.');
666    return true;
667  }
668}
669
670export default class MyAppServiceExtAbility extends AppServiceExtensionAbility {
671  onConnect(want: Want): rpc.RemoteObject {
672      return new Stub('test');
673  }
674  // 其他生命周期
675}
676```
677
678