• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ServiceExtensionAbility
2
3## 概述
4
5[ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md)是SERVICE类型的ExtensionAbility组件,提供后台服务能力,其内部持有了一个[ServiceExtensionContext](../reference/apis/js-apis-inner-application-serviceExtensionContext.md),通过[ServiceExtensionContext](../reference/apis/js-apis-inner-application-serviceExtensionContext.md)提供了丰富的接口供外部使用。
6
7本文描述中称被启动的ServiceExtensionAbility为服务端,称启动ServiceExtensionAbility的组件为客户端。
8
9[ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md)可以被其他组件启动或连接,并根据调用者的请求信息在后台处理相关事务。[ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md)支持以启动和连接两种形式运行,系统应用可以调用[startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartserviceextensionability)方法启动后台服务,也可以调用[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法连接后台服务,而三方应用只能调用[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法连接后台服务。启动和连接后台服务的差别:
10
11- **启动**:AbilityA启动ServiceB,启动后AbilityA和ServiceB为弱关联,AbilityA退出后,ServiceB可以继续存在。
12
13- **连接**:AbilityA连接ServiceB,连接后AbilityA和ServiceB为强关联,AbilityA退出后,ServiceB也一起退出。
14
15此处有如下细节需要注意:
16
17- 若Service只通过connect的方式被拉起,那么该Service的生命周期将受客户端控制,当客户端调用一次[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法,将建立一个连接,当客户端退出或者调用[disconnectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability)方法,该连接将断开。当所有连接都断开后,Service将自动退出。
18
19- Service一旦通过start的方式被拉起,将不会自动退出,系统应用可以调用[stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstopserviceextensionability)方法将Service退出。
20
21> **说明:**
22>
23> 1. OpenHarmony当前不支持三方应用实现ServiceExtensionAbility。如果三方开发者想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见[后台任务](../task-management/background-task-overview.md)。
24> 2. 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
25> 3. 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。
26
27## 生命周期
28
29[ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md)提供了onCreate()、onRequest()、onConnect()、onDisconnect()和onDestroy()生命周期回调,根据需要重写对应的回调方法。下图展示了ServiceExtensionAbility的生命周期。
30
31  **图1** ServiceExtensionAbility生命周期
32![ServiceExtensionAbility-lifecycle](figures/ServiceExtensionAbility-lifecycle.png)
33
34- **onCreate**
35  服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。
36
37  > **说明:**
38  > 如果服务已创建,再次启动该ServiceExtensionAbility不会触发onCreate()回调。
39
40- **onRequest**
41  当另一个组件调用[startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartserviceextensionability)方法启动该服务组件时,触发该回调。执行此方法后,服务会启动并在后台运行。每调用一次[startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartserviceextensionability)方法均会触发该回调。
42
43- **onConnect**
44  当另一个组件调用[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法与该服务连接时,触发该回调。开发者在此方法中,返回一个远端代理对象(IRemoteObject),客户端拿到这个对象后可以通过这个对象与服务端进行RPC通信,同时系统侧也会将该远端代理对象(IRemoteObject)储存。后续若有组件再调用[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法,系统侧会直接将所保存的远端代理对象(IRemoteObject)返回,而不再触发该回调。
45
46- **onDisconnect**
47  当最后一个连接断开时,将触发该回调。客户端死亡或者调用[disconnectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability)方法可以使连接断开。
48
49- **onDestroy**
50  当不再使用服务且准备将其销毁该实例时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。
51
52## 实现一个后台服务(仅对系统应用开放)
53
54### 开发准备
55
56只有系统应用才允许实现ServiceExtensionAbility,因此开发者在开发之前需做如下准备:
57
58- **替换Full SDK**:ServiceExtensionAbility相关接口都被标记为System-API,默认对开发者隐藏,因此需要手动从镜像站点获取Full SDK,并在DevEco Studio中替换,具体操作可参考[替换指南](../faqs/full-sdk-switch-guide.md)。
59
60- **申请AllowAppUsePrivilegeExtension特权**:只有具有AllowAppUsePrivilegeExtension特权的应用才允许开发ServiceExtensionAbility,具体申请方式可参考[应用特权配置指南](../../device-dev/subsystems/subsys-app-privilege-config-guide.md)。
61
62### 定义IDL接口
63
64ServiceExtensionAbility作为后台服务,需要向外部提供可调用的接口,开发者可将接口定义在idl文件中,并使用[IDL工具](../IDL/idl-guidelines.md)生成对应的proxy、stub文件。此处定义一个名为IIdlServiceExt.idl的文件作为示例:
65
66```cpp
67interface OHOS.IIdlServiceExt {
68  int ProcessData([in] int data);
69  void InsertDataToMap([in] String key, [in] int val);
70}
71```
72
73在DevEco Studio工程Module对应的ets目录下手动新建名为IdlServiceExt的目录,将[IDL工具](../IDL/idl-guidelines.md)生成的文件复制到该目录下,并创建一个名为idl_service_ext_impl.ts的文件,作为idl接口的实现:
74
75```
76├── ets
77│ ├── IdlServiceExt
78│ │   ├── i_idl_service_ext.ts      # 生成文件
79│ │   ├── idl_service_ext_proxy.ts  # 生成文件
80│ │   ├── idl_service_ext_stub.ts   # 生成文件
81│ │   ├── idl_service_ext_impl.ts   # 开发者自定义文件,对idl接口的具体实现
82│ └
8384```
85
86idl_service_ext_impl.ts实现如下:
87
88```ts
89import {processDataCallback} from './i_idl_service_ext';
90import {insertDataToMapCallback} from './i_idl_service_ext';
91import IdlServiceExtStub from './idl_service_ext_stub';
92
93const ERR_OK = 0;
94const TAG: string = "[IdlServiceExtImpl]";
95
96// 开发者需要在这个类型里对接口进行实现
97export default class ServiceExtImpl extends IdlServiceExtStub {
98  processData(data: number, callback: processDataCallback): void {
99    // 开发者自行实现业务逻辑
100    console.info(TAG, `processData: ${data}`);
101    callback(ERR_OK, data + 1);
102  }
103
104  insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {
105    // 开发者自行实现业务逻辑
106    console.info(TAG, `insertDataToMap, key: ${key}  val: ${val}`);
107    callback(ERR_OK);
108  }
109}
110```
111
112### 创建ServiceExtensionAbility
113
114在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下:
115
1161. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为ServiceExtAbility。
117
1182. 在ServiceExtAbility目录,右键选择“New > TypeScript File”,新建一个TypeScript文件并命名为ServiceExtAbility.ts119
120    ```
121    ├── ets
122    │ ├── IdlServiceExt
123    │ │   ├── i_idl_service_ext.ts      # 生成文件
124    │ │   ├── idl_service_ext_proxy.ts  # 生成文件
125    │ │   ├── idl_service_ext_stub.ts   # 生成文件
126    │ │   ├── idl_service_ext_impl.ts   # 开发者自定义文件,对idl接口的具体实现
127    │ ├── ServiceExtAbility
128    │ │   ├── ServiceExtAbility.ts
129130    ```
131
1323. 在ServiceExtAbility.ts文件中,增加导入ServiceExtensionAbility的依赖包,自定义类继承ServiceExtensionAbility并实现生命周期回调,在onConnect生命周期回调里,需要将之前定义的ServiceExtImpl对象返回。
133
134   ```ts
135   import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
136   import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl';
137   import Want from '@ohos.app.ability.Want';
138   import rpc from '@ohos.rpc';
139
140   const TAG: string = "[ServiceExtAbility]";
141
142   export default class ServiceExtAbility extends ServiceExtensionAbility {
143     serviceExtImpl: ServiceExtImpl = new ServiceExtImpl("ExtImpl");
144
145     onCreate(want: Want) {
146       console.info(TAG, `onCreate, want: ${want.abilityName}`);
147     }
148
149     onRequest(want: Want, startId: number) {
150       console.info(TAG, `onRequest, want: ${want.abilityName}`);
151     }
152
153     onConnect(want: Want) {
154       console.info(TAG, `onConnect, want: ${want.abilityName}`);
155       // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信
156       return this.serviceExtImpl as rpc.RemoteObject;
157     }
158
159     onDisconnect(want: Want) {
160       console.info(TAG, `onDisconnect, want: ${want.abilityName}`);
161     }
162
163     onDestroy() {
164       console.info(TAG, `onDestroy`);
165     }
166   }
167   ```
168
1694. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册ServiceExtensionAbility,type标签需要设置为“service”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。
170
171   ```json
172   {
173     "module": {
174       ...
175       "extensionAbilities": [
176         {
177           "name": "ServiceExtAbility",
178           "icon": "$media:icon",
179           "description": "service",
180           "type": "service",
181           "exported": true,
182           "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ts"
183         }
184       ]
185     }
186   }
187   ```
188
189## 启动一个后台服务(仅对系统应用开放)
190
191系统应用通过[startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartserviceextensionability)方法启动一个后台服务,服务的[onRequest()](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonrequest)回调就会被调用,并在该回调方法中接收到调用者传递过来的want对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用ServiceExtensionContext的[terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself)来自行停止,或者由另一个组件调用[stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability)来将其停止。
192
193> **说明:**
194> ServiceExtensionContext的[startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartserviceextensionability)、[stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability)和[terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself)为系统接口,三方应用不支持调用。
195
1961. 在系统应用中启动一个新的ServiceExtensionAbility。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
197
198   ```ts
199   import common from '@ohos.app.ability.common';
200   import Want from '@ohos.app.ability.Want';
201   import { BusinessError } from '@ohos.base';
202
203   let context: common.UIAbilityContext = ...; // UIAbilityContext
204   let want: Want = {
205     deviceId: "",
206     bundleName: "com.example.myapplication",
207     abilityName: "ServiceExtAbility"
208   };
209   context.startServiceExtensionAbility(want).then(() => {
210     console.info('Succeeded in starting ServiceExtensionAbility.');
211   }).catch((err: BusinessError) => {
212     console.error(`Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
213   })
214   ```
215
2162. 在系统应用中停止一个已启动的ServiceExtensionAbility。
217
218   ```ts
219   import common from '@ohos.app.ability.common';
220   import Want from '@ohos.app.ability.Want';
221   import { BusinessError } from '@ohos.base';
222
223   let context: common.UIAbilityContext = ...; // UIAbilityContext
224   let want: Want = {
225     deviceId: "",
226     bundleName: "com.example.myapplication",
227     abilityName: "ServiceExtAbility"
228   };
229   context.stopServiceExtensionAbility(want).then(() => {
230     console.info('Succeeded in stopping ServiceExtensionAbility.');
231   }).catch((err: BusinessError) => {
232     console.error(`Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
233   })
234   ```
235
2363. 已启动的ServiceExtensionAbility停止自身。
237
238   ```ts
239   import common from '@ohos.app.ability.common';
240   import { BusinessError } from '@ohos.base';
241
242   let context: common.ServiceExtensionContext = ...; // ServiceExtensionContext
243   context.terminateSelf().then(() => {
244     console.info('Succeeded in terminating self.');
245   }).catch((err: BusinessError) => {
246     console.error(`Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
247   })
248   ```
249
250> **说明:**
251> 后台服务可以在后台长期运行,为了避免资源浪费,需要对后台服务的生命周期进行管理。即一个后台服务完成了请求方的任务,需要及时销毁。销毁已启动的后台服务有两种方式:
252>
253> - 后台服务自身调用[terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself)方法来自行停止。
254> - 由其他组件调用[stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability)方法来停止。
255> 调用[terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself)或[stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability)方法之后,系统将销毁后台服务。
256
257## 连接一个后台服务
258
259系统应用或者三方应用可以通过[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability)连接一个服务(在Want对象中指定启动的目标服务),服务的[onConnect()](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonconnect)就会被调用,并在该回调方法中接收到调用者传递过来的Want对象,从而建立长连接。
260
261ServiceExtensionAbility服务组件在[onConnect()](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonconnect)中返回IRemoteObject对象,开发者通过该IRemoteObject定义通信接口,用于客户端与服务端进行RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务的交互后,客户端需要通过调用[disconnectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextdisconnectserviceextensionability)来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。
262
263- 使用connectServiceExtensionAbility()建立与后台服务的连接。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
264
265  ```ts
266  import common from '@ohos.app.ability.common';
267  import Want from '@ohos.app.ability.Want';
268
269  let want: Want = {
270    deviceId: "",
271    bundleName: "com.example.myapplication",
272    abilityName: "ServiceExtAbility"
273  };
274  let options: common.ConnectOptions = {
275    onConnect(elementName, remote) {
276      /* 此处的入参remote为ServiceExtensionAbility在onConnect生命周期回调中返回的对象,
277       * 开发者通过这个对象便可以与ServiceExtensionAbility进行通信,具体通信方式见下文
278       */
279      console.info('onConnect callback');
280      if (remote === null) {
281        console.info(`onConnect remote is null`);
282        return;
283      }
284    },
285    onDisconnect(elementName) {
286      console.info('onDisconnect callback')
287    },
288    onFailed(code) {
289      console.info('onFailed callback')
290    }
291  }
292  // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
293  let connectionId: number = this.context.connectServiceExtensionAbility(want, options);
294  ```
295
296- 使用disconnectServiceExtensionAbility()断开与后台服务的连接。
297
298  ```ts
299  import { BusinessError } from '@ohos.base';
300  // connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护
301  this.context.disconnectServiceExtensionAbility(connectionId).then(() => {
302    console.info('disconnectServiceExtensionAbility success');
303  }).catch((error: BusinessError) => {
304    console.error('disconnectServiceExtensionAbility failed');
305  })
306  ```
307
308## 客户端与服务端通信
309
310客户端在onConnect()中获取到[rpc.RemoteObject](../reference/apis/js-apis-rpc.md#iremoteobject)对象后便可与Service进行通信,有如下两种方式:
311
312- 使用服务端提供的IDL接口进行通信(推荐)
313
314  ```ts
315  // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
316  import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
317  import common from '@ohos.app.ability.common';
318
319  let options: common.ConnectOptions = {
320    onConnect(elementName, remote) {
321      console.info('onConnect callback');
322      if (remote === null) {
323        console.info(`onConnect remote is null`);
324        return;
325      }
326      let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
327      // 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了
328      serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {
329        console.info(`processData, errorCode: ${errorCode}, retVal: ${retVal}`);
330      });
331      serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => {
332        console.info(`insertDataToMap, errorCode: ${errorCode}`);
333      })
334    },
335    onDisconnect(elementName) {
336      console.info('onDisconnect callback')
337    },
338    onFailed(code) {
339      console.info('onFailed callback')
340    }
341  }
342  ```
343
344- 直接使用[sendMessageRequest](../reference/apis/js-apis-rpc.md#sendmessagerequest9)接口向服务端发送消息(不推荐)
345
346  ```ts
347  import rpc from '@ohos.rpc';
348  import common from '@ohos.app.ability.common';
349  import { BusinessError } from '@ohos.base';
350
351  const REQUEST_CODE = 1;
352  let options: common.ConnectOptions = {
353    onConnect(elementName, remote) {
354      console.info('onConnect callback');
355      if (remote === null) {
356        console.info(`onConnect remote is null`);
357        return;
358      }
359      // 直接调用rpc的接口向服务端发送消息,客户端需自行对入参进行序列化,对返回值进行反序列化,操作繁琐
360      let option = new rpc.MessageOption();
361      let data = new rpc.MessageSequence();
362      let reply = new rpc.MessageSequence();
363      data.writeInt(100);
364
365      // @param code 表示客户端发送的服务请求代码。
366      // @param data 表示客户端发送的{@link MessageSequence}对象。
367      // @param reply 表示远程服务发送的响应消息对象。
368      // @param options 指示操作是同步的还是异步的。
369      //
370      // @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
371      remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret) => {
372        let msg = reply.readInt();
373        console.info(`sendMessageRequest ret:${ret} msg:${msg}`);
374      }).catch((error: BusinessError) => {
375        console.info('sendMessageRequest failed');
376      });
377    },
378    onDisconnect(elementName) {
379      console.info('onDisconnect callback')
380    },
381    onFailed(code) {
382      console.info('onFailed callback')
383    }
384  }
385  ```
386
387## 服务端对客户端身份校验
388
389部分开发者需要使用ServiceExtension提供一些较为敏感的服务,因此需要对客户端身份进行校验,开发者可在IDL接口的stub端进行校验,IDL接口实现详见上文[定义IDL接口](#定义idl接口),此处推荐两种校验方式:
390
391- **通过callerUid识别客户端应用**
392
393  通过调用[getCallingUid()](../reference/apis/js-apis-rpc.md#getcallinguid)接口获取客户端的uid,再调用[getBundleNameByUid()](../reference/apis/js-apis-bundleManager.md#bundlemanagergetbundlenamebyuid)接口获取uid对应的bundleName,从而识别客户端身份。此处需要注意的是[getBundleNameByUid()](../reference/apis/js-apis-bundleManager.md#bundlemanagergetbundlenamebyuid)是一个异步接口,因此服务端无法将校验结果返回给客户端,这种校验方式适合客户端向服务端发起执行异步任务请求的场景,示例代码如下:
394
395  ```ts
396  import rpc from '@ohos.rpc';
397  import { BusinessError } from '@ohos.base';
398  import bundleManager from '@ohos.bundle.bundleManager';
399  import { processDataCallback } from './i_idl_service_ext';
400  import { insertDataToMapCallback } from './i_idl_service_ext';
401  import IdlServiceExtStub from './idl_service_ext_stub';
402
403  const ERR_OK = 0;
404  const ERR_DENY = -1;
405  const TAG: string = "[IdlServiceExtImpl]";
406
407  export default class ServiceExtImpl extends IdlServiceExtStub {
408    processData(data: number, callback: processDataCallback): void {
409      console.info(TAG, `processData: ${data}`);
410
411      let callerUid = rpc.IPCSkeleton.getCallingUid();
412      bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {
413        console.info(TAG, 'getBundleNameByUid: ' + callerBundleName);
414        // 对客户端包名进行识别
415        if (callerBundleName != 'com.example.connectextapp') { // 识别不通过
416          console.info(TAG, 'The caller bundle is not in trustlist, reject');
417          return;
418        }
419        // 识别通过,执行正常业务逻辑
420      }).catch((err: BusinessError) => {
421        console.info(TAG, 'getBundleNameByUid failed: ' + err.message);
422      });
423    }
424
425    insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {
426      // 开发者自行实现业务逻辑
427      console.info(TAG, `insertDataToMap, key: ${key}  val: ${val}`);
428      callback(ERR_OK);
429    }
430  }
431  ```
432
433- **通过callerTokenId对客户端进行鉴权**
434
435  通过调用[getCallingTokenId()](../reference/apis/js-apis-rpc.md#getcallingtokenid)接口获取客户端的tokenID,再调用[verifyAccessTokenSync()](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstokensync)接口判断客户端是否有某个具体权限,由于OpenHarmony当前不支持自定义权限,因此只能校验当前[系统所定义的权限](../security/permission-list.md)。示例代码如下:
436
437  ```ts
438  import rpc from '@ohos.rpc';
439  import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
440  import {processDataCallback} from './i_idl_service_ext';
441  import {insertDataToMapCallback} from './i_idl_service_ext';
442  import IdlServiceExtStub from './idl_service_ext_stub';
443
444  const ERR_OK = 0;
445  const ERR_DENY = -1;
446  const TAG: string = "[IdlServiceExtImpl]";
447
448  export default class ServiceExtImpl extends IdlServiceExtStub {
449    processData(data: number, callback: processDataCallback): void {
450      console.info(TAG, `processData: ${data}`);
451
452      let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();
453      let accessManger = abilityAccessCtrl.createAtManager();
454      // 所校验的具体权限由开发者自行选择,此处ohos.permission.SET_WALLPAPER只作为示例
455      let grantStatus =
456          accessManger.verifyAccessTokenSync(callerTokenId, "ohos.permission.SET_WALLPAPER");
457      if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
458          console.info(TAG, `PERMISSION_DENIED`);
459          callback(ERR_DENY, data);   // 鉴权失败,返回错误
460          return;
461      }
462      callback(ERR_OK, data + 1);     // 鉴权通过,执行正常业务逻辑
463    }
464
465    insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {
466      // 开发者自行实现业务逻辑
467      console.info(TAG, `insertDataToMap, key: ${key}  val: ${val}`);
468      callback(ERR_OK);
469    }
470  }
471  ```
472
473## 相关实例
474
475针对ServiceExtensionAbility开发,有以下相关实例可供参考:
476
477- [跨任务链返回(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-4.0-Release/code/SystemFeature/ApplicationModels/TestRely/LauncherTest/CrossChainBack)
478
479- [Ability与ServiceExtensionAbility通信(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-4.0-Release/code/BasicFeature/IDL/AbilityConnectServiceExtension)
480
481- [Stage模型(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-4.0-Release/code/BasicFeature/ApplicationModels/StageModel)
482