1# ServiceExtensionAbility 2 3## Overview 4 5[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) is an [ExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-extensionAbility.md) component of the SERVICE type that provides capabilities related to background services. It holds an internal [ServiceExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md), which provides a variety of APIs for external systems. 6 7In this document, the component that starts or connects to a ServiceExtensionAbility is called the client, and the ServiceExtensionAbility is called the server. 8 9A ServiceExtensionAbility can be started or connected by other components to process transactions in the background based on the request of the caller. System applications can call the [startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstartserviceextensionability) method to start background services or call the [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) method to connect to background services. Third-party applications can call only **connectServiceExtensionAbility()** to connect to background services. The differences between starting and connecting to a ServiceExtensionAbility are as follows: 10 11- **Starting**: In the case that AbilityA starts ServiceB, they are weakly associated. After AbilityA exits, ServiceB remains running. 12 13- **Connecting**: In the case that AbilityA connects to ServiceB, they are strongly associated. After AbilityA exits, ServiceB also exits. 14 15Note the following: 16 17- If a ServiceExtensionAbility is started only by means of connecting, its lifecycle is controlled by the client. A new connection is set up each time the client calls the **connectServiceExtensionAbility()** method. When the client exits or calls the [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) method, the connection is interrupted. After all connections are interrupted, the ServiceExtensionAbility automatically exits. 18 19- Once a ServiceExtensionAbility is started by means of starting, it will not exit automatically. System applications can call the [stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstopserviceextensionability) method to stop it. 20 21- The connection or disconnection operation can be performed only in the main thread, but not in the **Worker** and **TaskPool** threads. 22 23> **NOTE** 24> 25> Currently, third-party applications cannot implement a ServiceExtensionAbility. To implement transaction processing in the background, they can use [background tasks](../task-management/background-task-overview.md). 26> 27> A UIAbility of a third-party application can connect to a ServiceExtensionAbility provided by a system application through the context. 28> 29> Third-party applications can connect to a ServiceExtensionAbility provided by a system application only when they gain focus in the foreground. 30 31## Lifecycle 32 33The [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) class provides the lifecycle callbacks [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityoncreate), [onRequest()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonrequest), [onConnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonconnect), [onDisconnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityondisconnect), and [onDestroy()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityondestroy). Override them as required. The following figure shows the ServiceExtensionAbility lifecycle. 34 35**Figure 1** ServiceExtensionAbility lifecycle 36 37 38 39- **onCreate** 40 41 This callback is triggered when a ServiceExtensionAbility is created for the first time. You can perform initialization operations, for example, registering a common event listener. 42 43 > **NOTE** 44 > 45 > If a ServiceExtensionAbility has been created, starting it again does not trigger the **onCreate()** callback. 46 47- **onRequest** 48 49 This callback is triggered when another component calls the [startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstartserviceextensionability) method to start a ServiceExtensionAbility. After being started, the ServiceExtensionAbility runs in the background. This callback is triggered each time the **startServiceExtensionAbility()** method is called. 50 51- **onConnect** 52 53 This callback is triggered when another component calls the [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) method to connect to a ServiceExtensionAbility. In this method, a remote proxy object, namely, [IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject), is returned, through which the client communicates with the server by means of RPC. At the same time, the system stores the IRemoteObject. If another component calls the **connectServiceExtensionAbility()** method to connect to this ServiceExtensionAbility, the system returns the saved IRemoteObject, without triggering the callback. 54 55- **onDisconnect** 56 57 This callback is triggered when the last connection is interrupted. A connection is interrupted when the client exits or the [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) method is called. 58 59- **onDestroy** 60 61 This callback is triggered when a ServiceExtensionAbility is no longer used and the instance is ready for destruction. You can clear resources in this callback, for example, deregistering the listener. 62 63## Implementing a Background Service (for System Applications Only) 64 65### Preparations 66 67Only system applications can implement a [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md). You must make the following preparations before development: 68 69- **Switching to the full SDK**: All the APIs provided by the **ServiceExtensionAbility** class are marked as system APIs and hidden by default. Therefore, you must manually obtain the full SDK from the mirror and switch to it in DevEco Studio. For details, see [Switching to Full SDK](../faqs/full-sdk-switch-guide.md). 70 71- **Requesting the AllowAppUsePrivilegeExtension privilege**: Only applications with the **AllowAppUsePrivilegeExtension** privilege can implement a ServiceExtensionAbility. For details about how to request the privilege, see [Application Privilege Configuration](../../device-dev/subsystems/subsys-app-privilege-config-guide.md). 72 73### Defining IDL APIs 74 75As a background service, a ServiceExtensionAbility needs to provide APIs that can be called by external systems. You can define the APIs in IDL files and use the [IDL tool](../IDL/idl-guidelines.md) to generate proxy and stub files. The following demonstrates how to define a file named **IIdlServiceExt.idl**: 76 77```cpp 78interface OHOS.IIdlServiceExt { 79 int ProcessData([in] int data); 80 void InsertDataToMap([in] String key, [in] int val); 81} 82``` 83 84Create the **IdlServiceExt** directory in the **ets** directory of a module in a DevEco Studio project, and copy the files generated by the [IDL tool](../IDL/idl-guidelines.md) to this directory. Then create a file named **idl_service_ext_impl.ts** to implement the IDL APIs. 85 86``` 87├── ets 88│ ├── IdlServiceExt 89│ │ ├── i_idl_service_ext.ts # File generated by the IDL tool. 90│ │ ├── idl_service_ext_proxy.ts # File generated by the IDL tool. 91│ │ ├── idl_service_ext_stub.ts # File generated by the IDL tool. 92│ │ ├── idl_service_ext_impl.ts # Customize this file to implement IDL APIs. 93│ └ 94└ 95``` 96 97An example of **idl_service_ext_impl.ts** is as follows: 98 99```ts 100import IdlServiceExtStub from './idl_service_ext_stub'; 101import hilog from '@ohos.hilog'; 102import type { insertDataToMapCallback } from './i_idl_service_ext'; 103import type { processDataCallback } from './i_idl_service_ext'; 104 105const ERR_OK = 0; 106const TAG: string = "[IdlServiceExtImpl]"; 107const DOMAIN_NUMBER: number = 0xFF00; 108 109// You need to implement APIs in this type. 110export default class ServiceExtImpl extends IdlServiceExtStub { 111 processData(data: number, callback: processDataCallback): void { 112 // Implement service logic. 113 hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`); 114 callback(ERR_OK, data + 1); // The verification is successful, and service logic is executed normally. 115 } 116 117 insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void { 118 // Implement service logic. 119 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key} val: ${val}`); 120 callback(ERR_OK); 121 } 122} 123``` 124 125### Creating a ServiceExtensionAbility 126 127To manually create a ServiceExtensionAbility in the DevEco Studio project, perform the following steps: 128 1291. In the **ets** directory of a module in the project, right-click and choose **New > Directory** to create a directory named **ServiceExtAbility**. 130 1312. In the **ServiceExtAbility** directory, right-click and choose **New > ArkTS File** to create a file named **ServiceExtAbility.ets**. 132 133 ``` 134 ├── ets 135 │ ├── IdlServiceExt 136 │ │ ├── i_idl_service_ext.ets # File generated by the IDL tool. 137 │ │ ├── idl_service_ext_proxy.ets # File generated by the IDL tool. 138 │ │ ├── idl_service_ext_stub.ets # File generated by the IDL tool. 139 │ │ ├── idl_service_ext_impl.ets # Customize this file to implement IDL APIs. 140 │ ├── ServiceExtAbility 141 │ │ ├── ServiceExtAbility.ets 142 └ 143 ``` 144 1453. In the **ServiceExtAbility.ets** file, import the [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) module. Customize a class that inherits from ServiceExtensionAbility and implement the lifecycle callbacks. Return the previously defined **ServiceExtImpl** object in the [onConnect](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityoncreate) lifecycle callback. 146 147 ```ts 148 import { ServiceExtensionAbility, Want } from '@kit.AbilityKit'; 149 import { rpc } from '@kit.IPCKit'; 150 import { hilog } from '@kit.PerformanceAnalysisKit'; 151 import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl'; 152 153 const TAG: string = '[ServiceExtAbility]'; 154 const DOMAIN_NUMBER: number = 0xFF00; 155 156 export default class ServiceExtAbility extends ServiceExtensionAbility { 157 serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl'); 158 159 onCreate(want: Want): void { 160 let serviceExtensionContext = this.context; 161 hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`); 162 }; 163 164 onRequest(want: Want, startId: number): void { 165 hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`); 166 }; 167 168 onConnect(want: Want): rpc.RemoteObject { 169 hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`); 170 // Return the ServiceExtImpl object, through which the client can communicate with the ServiceExtensionAbility. 171 return this.serviceExtImpl as rpc.RemoteObject; 172 }; 173 174 onDisconnect(want: Want): void { 175 hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`); 176 }; 177 178 onDestroy(): void { 179 hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy'); 180 }; 181 }; 182 ``` 183 1844. Register the ServiceExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) of the module in the project. Set **type** to **"service"** and **srcEntry** to the code path of the ServiceExtensionAbility component. 185 186 ```json 187 { 188 "module": { 189 // ... 190 "extensionAbilities": [ 191 { 192 "name": "ServiceExtAbility", 193 "icon": "$media:icon", 194 "description": "service", 195 "type": "service", 196 "exported": true, 197 "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets" 198 } 199 ] 200 } 201 } 202 ``` 203 204## Starting a Background Service (for System Applications Only) 205 206A system application uses the [startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstartserviceextensionability) method to start a background service. The [onRequest()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonrequest) callback is invoked, through which the background service receives the **Want** object passed by the caller. After the background service is started, its lifecycle is independent of the client. In other words, even if the client is destroyed, the background service remains alive. Therefore, the background service must be stopped by calling [terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextterminateself) when its work is complete. Alternatively, another component can call [stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#abilitycontextstopserviceextensionability) to stop the background service. 207 208> **NOTE** 209> **startServiceExtensionAbility()**, **stopServiceExtensionAbility()**, and **terminateSelf()** provided by the **ServiceExtensionContext** class are system APIs and cannot be called by third-party applications. 210 2111. Start a new [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) in a system application. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). 212 213 ```ts 214 import { common, Want } from '@kit.AbilityKit'; 215 import { promptAction } from '@kit.ArkUI'; 216 import { hilog } from '@kit.PerformanceAnalysisKit'; 217 import { BusinessError } from '@kit.BasicServicesKit'; 218 219 const TAG: string = '[Page_ServiceExtensionAbility]'; 220 const DOMAIN_NUMBER: number = 0xFF00; 221 222 @Entry 223 @Component 224 struct Page_ServiceExtensionAbility { 225 build() { 226 Column() { 227 //... 228 List({ initialIndex: 0 }) { 229 ListItem() { 230 Row() { 231 //... 232 } 233 .onClick(() => { 234 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext 235 let want: Want = { 236 deviceId: '', 237 bundleName: 'com.samples.stagemodelabilitydevelop', 238 abilityName: 'ServiceExtAbility' 239 }; 240 context.startServiceExtensionAbility(want).then(() => { 241 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.'); 242 // The background service is started. 243 promptAction.showToast({ 244 message: 'SuccessfullyStartBackendService' 245 }); 246 }).catch((err: BusinessError) => { 247 hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); 248 }); 249 }) 250 } 251 //... 252 } 253 //... 254 } 255 //... 256 } 257 } 258 ``` 259 2602. Stop the [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) in the system application. 261 262 ```ts 263 import { common, Want } from '@kit.AbilityKit'; 264 import { promptAction } from '@kit.ArkUI'; 265 import { hilog } from '@kit.PerformanceAnalysisKit'; 266 import { BusinessError } from '@kit.BasicServicesKit'; 267 268 const TAG: string = '[Page_ServiceExtensionAbility]'; 269 const DOMAIN_NUMBER: number = 0xFF00; 270 271 @Entry 272 @Component 273 struct Page_ServiceExtensionAbility { 274 build() { 275 Column() { 276 //... 277 List({ initialIndex: 0 }) { 278 ListItem() { 279 Row() { 280 //... 281 } 282 .onClick(() => { 283 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext 284 let want: Want = { 285 deviceId: '', 286 bundleName: 'com.samples.stagemodelabilitydevelop', 287 abilityName: 'ServiceExtAbility' 288 }; 289 context.stopServiceExtensionAbility(want).then(() => { 290 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.'); 291 promptAction.showToast({ 292 message: 'SuccessfullyStoppedAStartedBackendService' 293 }); 294 }).catch((err: BusinessError) => { 295 hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); 296 }); 297 }) 298 } 299 //... 300 } 301 //... 302 } 303 //... 304 } 305 } 306 ``` 307 3083. Enable the [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) to stop itself. 309 310 ```ts 311 import { common } from '@kit.AbilityKit'; 312 import { promptAction } from '@kit.ArkUI'; 313 import { hilog } from '@kit.PerformanceAnalysisKit'; 314 import { BusinessError } from '@kit.BasicServicesKit'; 315 316 const TAG: string = '[Page_ServiceExtensionAbility]'; 317 const DOMAIN_NUMBER: number = 0xFF00; 318 319 @Entry 320 @Component 321 struct Page_ServiceExtensionAbility { 322 build() { 323 Column() { 324 //... 325 List({ initialIndex: 0 }) { 326 ListItem() { 327 Row() { 328 //... 329 } 330 .onClick(() => { 331 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext 332 context.terminateSelf().then(() => { 333 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in terminating self.'); 334 // The background service is stopped. 335 promptAction.showToast({ 336 message: 'SuccessfullyStopStartedBackendService' 337 }); 338 }).catch((err: BusinessError) => { 339 hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`); 340 }); 341 }) 342 } 343 //... 344 } 345 //... 346 } 347 //... 348 } 349 } 350 ``` 351 352> **NOTE** 353> 354> Background services remain alive in the background for a long time. To minimize resource usage, destroy a background service in time in either of the following ways when it finishes the requested task: 355> 356> - The background service calls the [terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextterminateself) method to automatically stop itself. 357> - Another component calls the [stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#abilitycontextstopserviceextensionability) method to stop the background service. 358> After either method is called, the system destroys the background service. 359 360## Connecting to a Background Service 361 362Either a system application or a third-party application can connect to a background service (specified in the **Want** object) through [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability). The [onConnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonconnect) callback is invoked, through which the background service receives the [Want](../reference/apis-ability-kit/js-apis-app-ability-want.md) object passed by the caller. In this way, a persistent connection is established. 363 364The ServiceExtensionAbility returns an [IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject) in the **onConnect()** callback. Through this IRemoteObject, you can define communication interfaces for RPC interaction between the client and server. Multiple clients can simultaneously connect to the same background service. After a client finishes the interaction, it must call [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) to disconnect from the service. If all clients connected to a background service are disconnected, the system destroys the service. 365 366- Call [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) to establish a connection to a background service. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). 367 368 ```ts 369 import { common, Want } from '@kit.AbilityKit'; 370 import { rpc } from '@kit.IPCKit'; 371 import { promptAction } from '@kit.ArkUI'; 372 import { hilog } from '@kit.PerformanceAnalysisKit'; 373 // The client needs to import idl_service_ext_proxy.ts provided by the server to the local project. 374 import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy'; 375 376 const TAG: string = '[Page_ServiceExtensionAbility]'; 377 const DOMAIN_NUMBER: number = 0xFF00; 378 379 let connectionId: number; 380 let want: Want = { 381 deviceId: '', 382 bundleName: 'com.samples.stagemodelabilitydevelop', 383 abilityName: 'ServiceExtAbility' 384 }; 385 386 let options: common.ConnectOptions = { 387 onConnect(elementName, remote: rpc.IRemoteObject): void { 388 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 389 if (remote === null) { 390 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 391 return; 392 } 393 let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote); 394 // Communication is carried out by API calling, without exposing RPC details. 395 serviceExtProxy.processData(1, (errorCode: number, retVal: number) => { 396 hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`); 397 }); 398 serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => { 399 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`); 400 }) 401 }, 402 onDisconnect(elementName): void { 403 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 404 }, 405 onFailed(code: number): void { 406 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code)); 407 } 408 }; 409 @Entry 410 @Component 411 struct Page_ServiceExtensionAbility { 412 build() { 413 Column() { 414 //... 415 List({ initialIndex: 0 }) { 416 ListItem() { 417 Row() { 418 //... 419 } 420 .onClick(() => { 421 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext 422 // The ID returned after the connection is set up must be saved. The ID will be used for disconnection. 423 connectionId = context.connectServiceExtensionAbility(want, options); 424 // The background service is connected. 425 promptAction.showToast({ 426 message: 'SuccessfullyConnectBackendService' 427 }); 428 // connectionId = context.connectAbility(want, options); 429 hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`); 430 }) 431 } 432 //... 433 } 434 //... 435 } 436 //... 437 } 438 } 439 ``` 440 441- Call [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) to disconnect from the background service. 442 443 ```ts 444 import { common } from '@kit.AbilityKit'; 445 import { promptAction } from '@kit.ArkUI'; 446 import { hilog } from '@kit.PerformanceAnalysisKit'; 447 import { BusinessError } from '@kit.BasicServicesKit'; 448 449 const TAG: string = '[Page_ServiceExtensionAbility]'; 450 const DOMAIN_NUMBER: number = 0xFF00; 451 452 let connectionId: number; 453 @Entry 454 @Component 455 struct Page_ServiceExtensionAbility { 456 build() { 457 Column() { 458 //... 459 List({ initialIndex: 0 }) { 460 ListItem() { 461 Row() { 462 //... 463 } 464 .onClick(() => { 465 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext 466 // connectionId is returned when connectServiceExtensionAbility is called and needs to be manually maintained. 467 context.disconnectServiceExtensionAbility(connectionId).then(() => { 468 hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success'); 469 // The background service is disconnected. 470 promptAction.showToast({ 471 message: 'SuccessfullyDisconnectBackendService' 472 }); 473 }).catch((error: BusinessError) => { 474 hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed'); 475 }); 476 }) 477 } 478 //... 479 } 480 //... 481 } 482 //... 483 } 484 } 485 486 ``` 487 488## Communication Between the Client and Server 489 490After obtaining the [rpc.IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject) from the [onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect) lifecycle callback, the client can communicate with the ServiceExtensionAbility in either of the following ways: 491 492- Using the IDL APIs provided by the server for communication (recommended) 493 494 ```ts 495 // The client needs to import idl_service_ext_proxy.ts provided by the server to the local project. 496 import { common } from '@kit.AbilityKit'; 497 import { rpc } from '@kit.IPCKit'; 498 import { hilog } from '@kit.PerformanceAnalysisKit'; 499 import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy'; 500 501 const TAG: string = '[Page_ServiceExtensionAbility]'; 502 const DOMAIN_NUMBER: number = 0xFF00; 503 504 let options: common.ConnectOptions = { 505 onConnect(elementName, remote: rpc.IRemoteObject): void { 506 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 507 if (remote === null) { 508 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 509 return; 510 } 511 let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote); 512 // Communication is carried out by API calling, without exposing RPC details. 513 serviceExtProxy.processData(1, (errorCode: number, retVal: number) => { 514 hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`); 515 }); 516 serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => { 517 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`); 518 }) 519 }, 520 onDisconnect(elementName): void { 521 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 522 }, 523 onFailed(code: number): void { 524 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code)); 525 } 526 }; 527 ``` 528 529- Calling [sendMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#sendmessagerequest9) to send messages to the server (not recommended) 530 531 ```ts 532 import { common } from '@kit.AbilityKit'; 533 import { promptAction } from '@kit.ArkUI'; 534 import { rpc } from '@kit.IPCKit'; 535 import { hilog } from '@kit.PerformanceAnalysisKit'; 536 import { BusinessError } from '@kit.BasicServicesKit'; 537 538 const TAG: string = '[Page_CollaborateAbility]'; 539 const DOMAIN_NUMBER: number = 0xFF00; 540 const REQUEST_CODE = 1; 541 let options: common.ConnectOptions = { 542 onConnect(elementName, remote): void { 543 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 544 if (remote === null) { 545 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 546 return; 547 } 548 let option = new rpc.MessageOption(); 549 let data = new rpc.MessageSequence(); 550 let reply = new rpc.MessageSequence(); 551 552 data.writeInt(99); 553 // You can send data to the target application for corresponding operations. 554 // @param code Indicates the service request code sent by the client. 555 // @param data Indicates the {@link MessageSequence} object sent by the client. 556 // @param reply Indicates the response message object sent by the remote service. 557 // @param options Specifies whether the operation is synchronous or asynchronous. 558 // @return Returns {@code true} if the operation is successful; returns {@code false} otherwise. 559 560 remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => { 561 let errCode = reply.readInt(); // Receive the information (100) returned by the target device if the connection is successful. 562 let msg: number = 0; 563 if (errCode === 0) { 564 msg = reply.readInt(); 565 } 566 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`); 567 // The background service is connected. 568 promptAction.showToast({ 569 message: `sendRequest msg:${msg}` 570 }); 571 }).catch((error: BusinessError) => { 572 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`); 573 }); 574 }, 575 onDisconnect(elementName): void { 576 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 577 }, 578 onFailed(code): void { 579 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback'); 580 } 581 }; 582 //... 583 ``` 584 585## Client Identity Verification by the Server 586 587When a ServiceExtensionAbility is used to provide sensitive services, the client identity must be verified. You can implement the verification on the IDL stub. For details about the IDL API implementation, see [Defining IDL APIs](#defining-idl-apis). Two verification modes are recommended: 588 589- **Verifying the client identity based on callerUid** 590 591 Call the [getCallingUid()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallinguid) method to obtain the UID of the client, and then call the [getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid) method to obtain the corresponding bundle name. In this way, the client identity is verified. Note that [getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid) is asynchronous, and therefore the server cannot return the verification result to the client. This verification mode applies when the client sends an asynchronous task request to the server. The sample code is as follows: 592 593 ```ts 594 import { bundleManager } from '@kit.AbilityKit'; 595 import { rpc } from '@kit.IPCKit'; 596 import { hilog } from '@kit.PerformanceAnalysisKit'; 597 import { BusinessError } from '@kit.BasicServicesKit'; 598 import IdlServiceExtStub from './idl_service_ext_stub'; 599 import type { InsertDataToMapCallback } from './i_idl_service_ext'; 600 import type { ProcessDataCallback } from './i_idl_service_ext'; 601 602 const ERR_OK = 0; 603 const ERR_DENY = -1; 604 const TAG: string = "[IdlServiceExtImpl]"; 605 const DOMAIN_NUMBER: number = 0xFF00; 606 607 // You need to implement APIs in this type. 608 export default class ServiceExtImpl extends IdlServiceExtStub { 609 processData(data: number, callback: ProcessDataCallback): void { 610 // Implement service logic. 611 hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`); 612 let callerUid = rpc.IPCSkeleton.getCallingUid(); 613 bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => { 614 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName); 615 // Identify the bundle name of the client. 616 if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // The verification fails. 617 hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject'); 618 return; 619 } 620 // The verification is successful, and service logic is executed normally. 621 }).catch((err: BusinessError) => { 622 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message); 623 }); 624 //... 625 }; 626 627 insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void { 628 // Implement service logic. 629 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key} val: ${val}`); 630 callback(ERR_OK); 631 }; 632 }; 633 ``` 634 635- **Verifying the client identity based on callerTokenId** 636 637 Call the [getCallingTokenId()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingtokenid) method to obtain the token ID of the client, and then call the [verifyAccessTokenSync()](../reference/apis-ability-kit/js-apis-abilityAccessCtrl.md#verifyaccesstokensync) method to check whether the client has the required permission. Currently, the system does not support permission customization. Therefore, only [system-defined permissions](../security/AccessToken/app-permissions.md) can be verified. The sample code is as follows: 638 639 ```ts 640 import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit'; 641 import { rpc } from '@kit.IPCKit'; 642 import { hilog } from '@kit.PerformanceAnalysisKit'; 643 import { BusinessError } from '@kit.BasicServicesKit'; 644 import IdlServiceExtStub from './idl_service_ext_stub'; 645 import type { InsertDataToMapCallback } from './i_idl_service_ext'; 646 import type { ProcessDataCallback } from './i_idl_service_ext'; 647 648 const ERR_OK = 0; 649 const ERR_DENY = -1; 650 const TAG: string = '[IdlServiceExtImpl]'; 651 const DOMAIN_NUMBER: number = 0xFF00; 652 653 // You need to implement APIs in this type. 654 export default class ServiceExtImpl extends IdlServiceExtStub { 655 processData(data: number, callback: ProcessDataCallback): void { 656 // Implement service logic. 657 hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`); 658 659 let callerUid = rpc.IPCSkeleton.getCallingUid(); 660 bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => { 661 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName); 662 // Identify the bundle name of the client. 663 if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // The verification fails. 664 hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject'); 665 return; 666 } 667 // The verification is successful, and service logic is executed normally. 668 }).catch((err: BusinessError) => { 669 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message); 670 }); 671 672 let callerTokenId = rpc.IPCSkeleton.getCallingTokenId(); 673 let accessManger = abilityAccessCtrl.createAtManager(); 674 // The permission to be verified varies depending on the service requirements. ohos.permission.GET_BUNDLE_INFO_PRIVILEGED is only an example. 675 let grantStatus = accessManger.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED'); 676 if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { 677 hilog.info(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED'); 678 callback(ERR_DENY, data); // The verification fails and an error is returned. 679 return; 680 } 681 hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.'); 682 callback(ERR_OK, data + 1); // The verification is successful, and service logic is executed normally. 683 }; 684 685 insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void { 686 // Implement service logic. 687 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key} val: ${val}`); 688 callback(ERR_OK); 689 }; 690 }; 691 ``` 692 693