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![ServiceExtensionAbility-lifecycle](figures/ServiceExtensionAbility-lifecycle.png)
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│ └
9495```
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
142143    ```
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