1# ServiceExtensionAbility 2<!--Kit: Ability Kit--> 3<!--Subsystem: Ability--> 4<!--Owner: @yewei0794--> 5<!--Designer: @jsjzju--> 6<!--Tester: @lixueqing513--> 7<!--Adviser: @huipeizi--> 8 9## Overview 10 11[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. 12 13In this document, the component that starts or connects to a ServiceExtensionAbility is called the client, and the ServiceExtensionAbility is called the server. 14 15A 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#startserviceextensionability) method to start background services or call the [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectserviceextensionability) 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: 16 17- **Starting**: In the case that AbilityA starts ServiceB, they are weakly associated. After AbilityA exits, ServiceB remains running. 18 19- **Connecting**: In the case that AbilityA connects to ServiceB, they are strongly associated. After AbilityA exits, ServiceB also exits. 20 21Note the following: 22 23- 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#disconnectserviceextensionability) method, the connection is interrupted. After all connections are interrupted, the ServiceExtensionAbility automatically exits. 24 25- 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#stopserviceextensionability) method to stop it. 26 27- The connection or disconnection operation can be performed only in the main thread, but not in the **Worker** and **TaskPool** threads. 28 29> **NOTE** 30> 31> 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). 32> 33> A UIAbility of a third-party application can connect to a ServiceExtensionAbility provided by a system application through the context. 34> 35> Third-party applications can connect to a ServiceExtensionAbility provided by a system application only when they gain focus in the foreground. 36 37## Lifecycle 38 39The [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#oncreate), [onRequest()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#onrequest), [onConnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#onconnect), [onDisconnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#ondisconnect), and [onDestroy()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#ondestroy). Override them as required. The following figure shows the ServiceExtensionAbility lifecycle. 40 41**Figure 1** ServiceExtensionAbility lifecycle 42 43 44 45- **onCreate** 46 47 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. 48 49 > **NOTE** 50 > 51 > If a ServiceExtensionAbility has been created, starting it again does not trigger the **onCreate()** callback. 52 53- **onRequest** 54 55 This callback is triggered when another component calls the [startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#startserviceextensionability) 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. 56 57- **onConnect** 58 59 This callback is triggered when another component calls the [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectserviceextensionability) 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. 60 61- **onDisconnect** 62 63 This callback is triggered when the last connection is interrupted. A connection is interrupted when the client exits or [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#disconnectserviceextensionability) is called. 64 65- **onDestroy** 66 67 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. 68 69## Implementing a Background Service (for System Applications Only) 70 71### Preparations 72 73Only 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: 74 75- **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). 76 77- **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). 78 79### Defining IDL APIs 80 81As 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**: 82 83```cpp 84interface OHOS.IIdlServiceExt { 85 int ProcessData([in] int data); 86 void InsertDataToMap([in] String key, [in] int val); 87} 88``` 89 90Create 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. 91 92``` 93├── ets 94│ ├── IdlServiceExt 95│ │ ├── i_idl_service_ext.ts # File generated by the IDL tool. 96│ │ ├── idl_service_ext_proxy.ts # File generated by the IDL tool. 97│ │ ├── idl_service_ext_stub.ts # File generated by the IDL tool. 98│ │ ├── idl_service_ext_impl.ts # Customize this file to implement IDL APIs. 99│ └ 100└ 101``` 102 103An example of **idl_service_ext_impl.ts** is as follows: 104 105```ts 106import IdlServiceExtStub from './idl_service_ext_stub'; 107import hilog from '@ohos.hilog'; 108import type { insertDataToMapCallback } from './i_idl_service_ext'; 109import type { processDataCallback } from './i_idl_service_ext'; 110 111const ERR_OK = 0; 112const TAG: string = "[IdlServiceExtImpl]"; 113const DOMAIN_NUMBER: number = 0xFF00; 114 115// You need to implement APIs in this type. 116export default class ServiceExtImpl extends IdlServiceExtStub { 117 processData(data: number, callback: processDataCallback): void { 118 // Implement service logic. 119 hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`); 120 callback(ERR_OK, data + 1); // The verification is successful, and service logic is executed normally. 121 } 122 123 insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void { 124 // Implement service logic. 125 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key} val: ${val}`); 126 callback(ERR_OK); 127 } 128} 129``` 130 131### Creating a ServiceExtensionAbility 132 133To manually create a ServiceExtensionAbility in the DevEco Studio project, perform the following steps: 134 1351. In the **ets** directory of a module in the project, right-click and choose **New > Directory** to create a directory named **ServiceExtAbility**. 136 1372. In the **ServiceExtAbility** directory, right-click and choose **New > ArkTS File** to create a file named **ServiceExtAbility.ets**. 138 139 ``` 140 ├── ets 141 │ ├── IdlServiceExt 142 │ │ ├── i_idl_service_ext.ets # File generated by the IDL tool. 143 │ │ ├── idl_service_ext_proxy.ets # File generated by the IDL tool. 144 │ │ ├── idl_service_ext_stub.ets # File generated by the IDL tool. 145 │ │ ├── idl_service_ext_impl.ets # Customize this file to implement IDL APIs. 146 │ ├── ServiceExtAbility 147 │ │ ├── ServiceExtAbility.ets 148 └ 149 ``` 150 1513. 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#oncreate) lifecycle callback. 152 153 ```ts 154 import { ServiceExtensionAbility, Want } from '@kit.AbilityKit'; 155 import { rpc } from '@kit.IPCKit'; 156 import { hilog } from '@kit.PerformanceAnalysisKit'; 157 import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl'; 158 159 const TAG: string = '[ServiceExtAbility]'; 160 const DOMAIN_NUMBER: number = 0xFF00; 161 162 export default class ServiceExtAbility extends ServiceExtensionAbility { 163 serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl'); 164 165 onCreate(want: Want): void { 166 let serviceExtensionContext = this.context; 167 hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`); 168 }; 169 170 onRequest(want: Want, startId: number): void { 171 hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`); 172 }; 173 174 onConnect(want: Want): rpc.RemoteObject { 175 hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`); 176 // Return the ServiceExtImpl object, through which the client can communicate with the ServiceExtensionAbility. 177 return this.serviceExtImpl as rpc.RemoteObject; 178 }; 179 180 onDisconnect(want: Want): void { 181 hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`); 182 }; 183 184 onDestroy(): void { 185 hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy'); 186 }; 187 }; 188 ``` 189 1904. 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. 191 192 ```json 193 { 194 "module": { 195 // ... 196 "extensionAbilities": [ 197 { 198 "name": "ServiceExtAbility", 199 "icon": "$media:icon", 200 "description": "service", 201 "type": "service", 202 "exported": true, 203 "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets" 204 } 205 ] 206 } 207 } 208 ``` 209 210## Starting a Background Service (for System Applications Only) 211 212A system application uses the [startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#startserviceextensionability) method to start a background service. The [onRequest()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#onrequest) 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. 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#stopserviceextensionability) to stop the background service. 213 214> **NOTE** 215> 216> **startServiceExtensionAbility()**, **stopServiceExtensionAbility()**, and **terminateSelf()** provided by the **ServiceExtensionContext** class are system APIs and cannot be called by third-party applications. 217 2181. 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). 219 220 ```ts 221 import { common, Want } from '@kit.AbilityKit'; 222 import { hilog } from '@kit.PerformanceAnalysisKit'; 223 import { BusinessError } from '@kit.BasicServicesKit'; 224 225 const TAG: string = '[Page_ServiceExtensionAbility]'; 226 const DOMAIN_NUMBER: number = 0xFF00; 227 228 @Entry 229 @Component 230 struct Page_ServiceExtensionAbility { 231 build() { 232 Column() { 233 //... 234 List({ initialIndex: 0 }) { 235 ListItem() { 236 Row() { 237 //... 238 } 239 .onClick(() => { 240 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 241 let want: Want = { 242 deviceId: '', 243 bundleName: 'com.samples.stagemodelabilitydevelop', 244 abilityName: 'ServiceExtAbility' 245 }; 246 context.startServiceExtensionAbility(want).then(() => { 247 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.'); 248 // The background service is started. 249 this.getUIContext().getPromptAction().showToast({ 250 message: 'SuccessfullyStartBackendService' 251 }); 252 }).catch((err: BusinessError) => { 253 hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); 254 }); 255 }) 256 } 257 //... 258 } 259 //... 260 } 261 //... 262 } 263 } 264 ``` 265 2662. Stop the [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) in the system application. 267 268 ```ts 269 import { common, Want } from '@kit.AbilityKit'; 270 import { hilog } from '@kit.PerformanceAnalysisKit'; 271 import { BusinessError } from '@kit.BasicServicesKit'; 272 273 const TAG: string = '[Page_ServiceExtensionAbility]'; 274 const DOMAIN_NUMBER: number = 0xFF00; 275 276 @Entry 277 @Component 278 struct Page_ServiceExtensionAbility { 279 build() { 280 Column() { 281 //... 282 List({ initialIndex: 0 }) { 283 ListItem() { 284 Row() { 285 //... 286 } 287 .onClick(() => { 288 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 289 let want: Want = { 290 deviceId: '', 291 bundleName: 'com.samples.stagemodelabilitydevelop', 292 abilityName: 'ServiceExtAbility' 293 }; 294 context.stopServiceExtensionAbility(want).then(() => { 295 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.'); 296 this.getUIContext().getPromptAction().showToast({ 297 message: 'SuccessfullyStoppedAStartedBackendService' 298 }); 299 }).catch((err: BusinessError) => { 300 hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); 301 }); 302 }) 303 } 304 //... 305 } 306 //... 307 } 308 //... 309 } 310 } 311 ``` 312 3133. Enable the [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) to stop itself. 314 315 ```ts 316 import { common } from '@kit.AbilityKit'; 317 import { hilog } from '@kit.PerformanceAnalysisKit'; 318 import { BusinessError } from '@kit.BasicServicesKit'; 319 320 const TAG: string = '[Page_ServiceExtensionAbility]'; 321 const DOMAIN_NUMBER: number = 0xFF00; 322 323 @Entry 324 @Component 325 struct Page_ServiceExtensionAbility { 326 build() { 327 Column() { 328 //... 329 List({ initialIndex: 0 }) { 330 ListItem() { 331 Row() { 332 //... 333 } 334 .onClick(() => { 335 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 336 context.terminateSelf().then(() => { 337 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in terminating self.'); 338 // The background service is stopped. 339 this.getUIContext().getPromptAction().showToast({ 340 message: 'SuccessfullyStopStartedBackendService' 341 }); 342 }).catch((err: BusinessError) => { 343 hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`); 344 }); 345 }) 346 } 347 //... 348 } 349 //... 350 } 351 //... 352 } 353 } 354 ``` 355 356> **NOTE** 357> 358> 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: 359> 360> - The background service calls the [terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextterminateself) method to automatically stop itself. 361> - Another component calls the [stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#stopserviceextensionability) method to stop the background service. 362> After either method is called, the system destroys the background service. 363 364## Connecting to a Background Service 365 366Either 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#connectserviceextensionability). The [onConnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#onconnect) 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. 367 368The 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#disconnectserviceextensionability) to disconnect from the service. If all clients connected to a background service are disconnected, the system destroys the service. 369 370- Call [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectserviceextensionability) 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). 371 372 ```ts 373 import { common, Want } from '@kit.AbilityKit'; 374 import { rpc } from '@kit.IPCKit'; 375 import { hilog } from '@kit.PerformanceAnalysisKit'; 376 // The client needs to import idl_service_ext_proxy.ts provided by the server to the local project. 377 import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy'; 378 379 const TAG: string = '[Page_ServiceExtensionAbility]'; 380 const DOMAIN_NUMBER: number = 0xFF00; 381 382 let connectionId: number; 383 let want: Want = { 384 deviceId: '', 385 bundleName: 'com.samples.stagemodelabilitydevelop', 386 abilityName: 'ServiceExtAbility' 387 }; 388 389 let options: common.ConnectOptions = { 390 onConnect(elementName, remote: rpc.IRemoteObject): void { 391 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 392 if (remote === null) { 393 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 394 return; 395 } 396 let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote); 397 // Communication is carried out by API calling, without exposing RPC details. 398 serviceExtProxy.processData(1, (errorCode: number, retVal: number) => { 399 hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`); 400 }); 401 serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => { 402 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`); 403 }) 404 }, 405 onDisconnect(elementName): void { 406 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 407 }, 408 onFailed(code: number): void { 409 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code)); 410 } 411 }; 412 @Entry 413 @Component 414 struct Page_ServiceExtensionAbility { 415 build() { 416 Column() { 417 //... 418 List({ initialIndex: 0 }) { 419 ListItem() { 420 Row() { 421 //... 422 } 423 .onClick(() => { 424 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 425 // The ID returned after the connection is set up must be saved. The ID will be used for disconnection. 426 connectionId = context.connectServiceExtensionAbility(want, options); 427 // The background service is connected. 428 this.getUIContext().getPromptAction().showToast({ 429 message: 'SuccessfullyConnectBackendService' 430 }); 431 // connectionId = context.connectAbility(want, options); 432 hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`); 433 }) 434 } 435 //... 436 } 437 //... 438 } 439 //... 440 } 441 } 442 ``` 443 444- Call [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#disconnectserviceextensionability) to disconnect from the background service. 445 446 ```ts 447 import { common } from '@kit.AbilityKit'; 448 import { hilog } from '@kit.PerformanceAnalysisKit'; 449 import { BusinessError } from '@kit.BasicServicesKit'; 450 451 const TAG: string = '[Page_ServiceExtensionAbility]'; 452 const DOMAIN_NUMBER: number = 0xFF00; 453 454 let connectionId: number; 455 @Entry 456 @Component 457 struct Page_ServiceExtensionAbility { 458 build() { 459 Column() { 460 //... 461 List({ initialIndex: 0 }) { 462 ListItem() { 463 Row() { 464 //... 465 } 466 .onClick(() => { 467 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 468 // connectionId is returned when connectServiceExtensionAbility is called and needs to be manually maintained. 469 context.disconnectServiceExtensionAbility(connectionId).then(() => { 470 hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success'); 471 // The background service is disconnected. 472 this.getUIContext().getPromptAction().showToast({ 473 message: 'SuccessfullyDisconnectBackendService' 474 }); 475 }).catch((error: BusinessError) => { 476 hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed'); 477 }); 478 }) 479 } 480 //... 481 } 482 //... 483 } 484 //... 485 } 486 } 487 ``` 488 489## Communication Between the Client and Server 490 491After 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: 492 493- Using the IDL APIs provided by the server for communication (recommended) 494 495 ```ts 496 // The client needs to import idl_service_ext_proxy.ts provided by the server to the local project. 497 import { common } from '@kit.AbilityKit'; 498 import { rpc } from '@kit.IPCKit'; 499 import { hilog } from '@kit.PerformanceAnalysisKit'; 500 import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy'; 501 502 const TAG: string = '[Page_ServiceExtensionAbility]'; 503 const DOMAIN_NUMBER: number = 0xFF00; 504 505 let options: common.ConnectOptions = { 506 onConnect(elementName, remote: rpc.IRemoteObject): void { 507 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 508 if (remote === null) { 509 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 510 return; 511 } 512 let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote); 513 // Communication is carried out by API calling, without exposing RPC details. 514 serviceExtProxy.processData(1, (errorCode: number, retVal: number) => { 515 hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`); 516 }); 517 serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => { 518 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`); 519 }) 520 }, 521 onDisconnect(elementName): void { 522 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 523 }, 524 onFailed(code: number): void { 525 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code)); 526 } 527 }; 528 ``` 529 530- Calling [sendMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#sendmessagerequest9) to send messages to the server (not recommended) 531 532 ```ts 533 import { common } from '@kit.AbilityKit'; 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 // The background service is connected. 567 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest success, msg: ${msg}`); 568 }).catch((error: BusinessError) => { 569 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`); 570 }); 571 }, 572 onDisconnect(elementName): void { 573 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 574 }, 575 onFailed(code): void { 576 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback'); 577 } 578 }; 579 //... 580 ``` 581 582## Client Identity Verification by the Server 583 584When 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: 585 586- **Verifying the client identity based on callerUid** 587 588 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#bundlemanagergetbundlenamebyuid14) 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#bundlemanagergetbundlenamebyuid14) 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: 589 590 ```ts 591 import { bundleManager } from '@kit.AbilityKit'; 592 import { rpc } from '@kit.IPCKit'; 593 import { hilog } from '@kit.PerformanceAnalysisKit'; 594 import { BusinessError } from '@kit.BasicServicesKit'; 595 import IdlServiceExtStub from './idl_service_ext_stub'; 596 import type { InsertDataToMapCallback } from './i_idl_service_ext'; 597 import type { ProcessDataCallback } from './i_idl_service_ext'; 598 599 const ERR_OK = 0; 600 const ERR_DENY = -1; 601 const TAG: string = "[IdlServiceExtImpl]"; 602 const DOMAIN_NUMBER: number = 0xFF00; 603 604 // You need to implement APIs in this type. 605 export default class ServiceExtImpl extends IdlServiceExtStub { 606 processData(data: number, callback: ProcessDataCallback): void { 607 // Implement service logic. 608 hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`); 609 let callerUid = rpc.IPCSkeleton.getCallingUid(); 610 bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => { 611 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName); 612 // Identify the bundle name of the client. 613 if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // The verification fails. 614 hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject'); 615 return; 616 } 617 // The verification is successful, and service logic is executed normally. 618 }).catch((err: BusinessError) => { 619 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message); 620 }); 621 //... 622 }; 623 624 insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void { 625 // Implement service logic. 626 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key} val: ${val}`); 627 callback(ERR_OK); 628 }; 629 }; 630 ``` 631 632- **Verifying the client identity based on callerTokenId** 633 634 Call the [getCallingTokenId()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingtokenid8) method to obtain the token ID of the client, and then call the [verifyAccessTokenSync()](../reference/apis-ability-kit/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9) 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: 635 636 ```ts 637 import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit'; 638 import { rpc } from '@kit.IPCKit'; 639 import { hilog } from '@kit.PerformanceAnalysisKit'; 640 import { BusinessError } from '@kit.BasicServicesKit'; 641 import IdlServiceExtStub from './idl_service_ext_stub'; 642 import type { InsertDataToMapCallback } from './i_idl_service_ext'; 643 import type { ProcessDataCallback } from './i_idl_service_ext'; 644 645 const ERR_OK = 0; 646 const ERR_DENY = -1; 647 const TAG: string = '[IdlServiceExtImpl]'; 648 const DOMAIN_NUMBER: number = 0xFF00; 649 650 // You need to implement APIs in this type. 651 export default class ServiceExtImpl extends IdlServiceExtStub { 652 processData(data: number, callback: ProcessDataCallback): void { 653 // Implement service logic. 654 hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`); 655 656 let callerUid = rpc.IPCSkeleton.getCallingUid(); 657 bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => { 658 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName); 659 // Identify the bundle name of the client. 660 if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // The verification fails. 661 hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject'); 662 return; 663 } 664 // The verification is successful, and service logic is executed normally. 665 }).catch((err: BusinessError) => { 666 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message); 667 }); 668 669 let callerTokenId = rpc.IPCSkeleton.getCallingTokenId(); 670 let accessManger = abilityAccessCtrl.createAtManager(); 671 // The permission to be verified varies depending on the service requirements. ohos.permission.GET_BUNDLE_INFO_PRIVILEGED is only an example. 672 let grantStatus = accessManger.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED'); 673 if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { 674 hilog.info(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED'); 675 callback(ERR_DENY, data); // The verification fails and an error is returned. 676 return; 677 } 678 hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.'); 679 callback(ERR_OK, data + 1); // The verification is successful, and service logic is executed normally. 680 }; 681 682 insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void { 683 // Implement service logic. 684 hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key} val: ${val}`); 685 callback(ERR_OK); 686 }; 687 }; 688 ``` 689 690