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