1e41f4b71Sopenharmony_ci# Multi-device Collaboration 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci 4e41f4b71Sopenharmony_ci## When to Use 5e41f4b71Sopenharmony_ci 6e41f4b71Sopenharmony_ciMulti-device collaboration involves the following scenarios: 7e41f4b71Sopenharmony_ci 8e41f4b71Sopenharmony_ci- [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned) 9e41f4b71Sopenharmony_ci 10e41f4b71Sopenharmony_ci- [Starting UIAbility Across Devices (Data Returned)](#starting-uiability-across-devices-data-returned) 11e41f4b71Sopenharmony_ci 12e41f4b71Sopenharmony_ci- [Connecting to ServiceExtensionAbility Across Devices](#connecting-to-serviceextensionability-across-devices) 13e41f4b71Sopenharmony_ci 14e41f4b71Sopenharmony_ci- [Using Cross-Device Call](#using-cross-device-call) 15e41f4b71Sopenharmony_ci 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci## Multi-Device Collaboration Process 18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ciThe figure below shows the multi-device collaboration process. 20e41f4b71Sopenharmony_ci 21e41f4b71Sopenharmony_ci**Figure 1** Multi-device collaboration process 22e41f4b71Sopenharmony_ci 23e41f4b71Sopenharmony_ci 24e41f4b71Sopenharmony_ci 25e41f4b71Sopenharmony_ci 26e41f4b71Sopenharmony_ci## Constraints 27e41f4b71Sopenharmony_ci 28e41f4b71Sopenharmony_ci- Since multi-device collaboration mission management is not available, you can obtain the device list by developing system applications. Third-party applications cannot access the device list. 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci- Multi-device collaboration must comply with [Inter-Device Component Startup Rules](component-startup-rules.md#inter-device-component-startup-rules). 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci- For better user experience, you are advised to use the [want](../reference/apis-ability-kit/js-apis-app-ability-want.md) parameter to transmit data smaller than 100 KB. 33e41f4b71Sopenharmony_ci 34e41f4b71Sopenharmony_ci 35e41f4b71Sopenharmony_ci## Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned) 36e41f4b71Sopenharmony_ci 37e41f4b71Sopenharmony_ciOn device A, touch the **Start** button provided by the initiator application to start a specified [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) or [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) on device B. 38e41f4b71Sopenharmony_ci 39e41f4b71Sopenharmony_ci 40e41f4b71Sopenharmony_ci### Available APIs 41e41f4b71Sopenharmony_ci 42e41f4b71Sopenharmony_ci**Table 1** Cross-device startup APIs 43e41f4b71Sopenharmony_ci 44e41f4b71Sopenharmony_ci| **API**| **Description**| 45e41f4b71Sopenharmony_ci| -------- | -------- | 46e41f4b71Sopenharmony_ci| startAbility(want: Want, callback: AsyncCallback<void>): void; | Starts a UIAbility or ServiceExtensionAbility. This API uses an asynchronous callback to return the result.| 47e41f4b71Sopenharmony_ci| stopServiceExtensionAbility(want: Want, callback: AsyncCallback<void>): void; | Stops a ServiceExtensionAbility. This API uses an asynchronous callback to return the result.| 48e41f4b71Sopenharmony_ci| stopServiceExtensionAbility(want: Want): Promise<void>; | Stops a ServiceExtensionAbility. This API uses a promise to return the result.| 49e41f4b71Sopenharmony_ci 50e41f4b71Sopenharmony_ci 51e41f4b71Sopenharmony_ci### How to Develop 52e41f4b71Sopenharmony_ci 53e41f4b71Sopenharmony_ci1. Declare the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 54e41f4b71Sopenharmony_ci 55e41f4b71Sopenharmony_ci2. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/AccessToken/request-user-authorization.md). 56e41f4b71Sopenharmony_ci 57e41f4b71Sopenharmony_ci3. Obtain the device ID of the target device. 58e41f4b71Sopenharmony_ci 59e41f4b71Sopenharmony_ci ```ts 60e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 61e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 62e41f4b71Sopenharmony_ci 63e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 64e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 65e41f4b71Sopenharmony_ci 66e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 67e41f4b71Sopenharmony_ci 68e41f4b71Sopenharmony_ci function initDmClass(): void { 69e41f4b71Sopenharmony_ci // createDeviceManager is a system API. 70e41f4b71Sopenharmony_ci try { 71e41f4b71Sopenharmony_ci dmClass = distributedDeviceManager.createDeviceManager('com.samples.stagemodelabilitydevelop'); 72e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass) ?? ''); 73e41f4b71Sopenharmony_ci } catch (err) { 74e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, 'createDeviceManager err: ' + JSON.stringify(err)); 75e41f4b71Sopenharmony_ci } 76e41f4b71Sopenharmony_ci } 77e41f4b71Sopenharmony_ci 78e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 79e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 80e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 81e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 82e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 83e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 84e41f4b71Sopenharmony_ci return; 85e41f4b71Sopenharmony_ci } 86e41f4b71Sopenharmony_ci if (list.length === 0) { 87e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 88e41f4b71Sopenharmony_ci return; 89e41f4b71Sopenharmony_ci } 90e41f4b71Sopenharmony_ci return list[0].networkId; 91e41f4b71Sopenharmony_ci } else { 92e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 93e41f4b71Sopenharmony_ci return; 94e41f4b71Sopenharmony_ci } 95e41f4b71Sopenharmony_ci } 96e41f4b71Sopenharmony_ci ``` 97e41f4b71Sopenharmony_ci 98e41f4b71Sopenharmony_ci4. Set the target component parameters, and call [startAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) to start a [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) or [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md). 99e41f4b71Sopenharmony_ci 100e41f4b71Sopenharmony_ci ```ts 101e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 102e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 103e41f4b71Sopenharmony_ci import { Want, common } from '@kit.AbilityKit'; 104e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 105e41f4b71Sopenharmony_ci import { promptAction } from '@kit.ArkUI'; 106e41f4b71Sopenharmony_ci 107e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 108e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 109e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 110e41f4b71Sopenharmony_ci 111e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 112e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 113e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 114e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 115e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 116e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 117e41f4b71Sopenharmony_ci return; 118e41f4b71Sopenharmony_ci } 119e41f4b71Sopenharmony_ci if (list.length === 0) { 120e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 121e41f4b71Sopenharmony_ci return; 122e41f4b71Sopenharmony_ci } 123e41f4b71Sopenharmony_ci return list[0].networkId; 124e41f4b71Sopenharmony_ci } else { 125e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 126e41f4b71Sopenharmony_ci return; 127e41f4b71Sopenharmony_ci } 128e41f4b71Sopenharmony_ci }; 129e41f4b71Sopenharmony_ci 130e41f4b71Sopenharmony_ci @Entry 131e41f4b71Sopenharmony_ci @Component 132e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 133e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 134e41f4b71Sopenharmony_ci 135e41f4b71Sopenharmony_ci build() { 136e41f4b71Sopenharmony_ci Column() { 137e41f4b71Sopenharmony_ci //... 138e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 139e41f4b71Sopenharmony_ci //... 140e41f4b71Sopenharmony_ci ListItem() { 141e41f4b71Sopenharmony_ci Row() { 142e41f4b71Sopenharmony_ci //... 143e41f4b71Sopenharmony_ci } 144e41f4b71Sopenharmony_ci .onClick(() => { 145e41f4b71Sopenharmony_ci let want: Want = { 146e41f4b71Sopenharmony_ci deviceId: getRemoteDeviceId(), 147e41f4b71Sopenharmony_ci bundleName: 'com.samples.stagemodelabilityinteraction', 148e41f4b71Sopenharmony_ci abilityName: 'CollaborateAbility', 149e41f4b71Sopenharmony_ci moduleName: 'entry', // moduleName is optional. 150e41f4b71Sopenharmony_ci }; 151e41f4b71Sopenharmony_ci // context is the AbilityContext of the initiator UIAbility. 152e41f4b71Sopenharmony_ci this.context.startAbility(want).then(() => { 153e41f4b71Sopenharmony_ci promptAction.showToast({ 154e41f4b71Sopenharmony_ci message: 'SuccessfulCollaboration' 155e41f4b71Sopenharmony_ci }); 156e41f4b71Sopenharmony_ci }).catch((err: BusinessError) => { 157e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, `startAbility err: ` + JSON.stringify(err)); 158e41f4b71Sopenharmony_ci }); 159e41f4b71Sopenharmony_ci }) 160e41f4b71Sopenharmony_ci } 161e41f4b71Sopenharmony_ci //... 162e41f4b71Sopenharmony_ci } 163e41f4b71Sopenharmony_ci //... 164e41f4b71Sopenharmony_ci } 165e41f4b71Sopenharmony_ci //... 166e41f4b71Sopenharmony_ci } 167e41f4b71Sopenharmony_ci } 168e41f4b71Sopenharmony_ci ``` 169e41f4b71Sopenharmony_ci 170e41f4b71Sopenharmony_ci5. Call [stopServiceExtensionAbility](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstopserviceextensionability-1) to stop the [ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md) when it is no longer required on device B. (This API cannot be used to stop a [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md). Users must manually stop a UIAbility through mission management.) 171e41f4b71Sopenharmony_ci 172e41f4b71Sopenharmony_ci ```ts 173e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 174e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 175e41f4b71Sopenharmony_ci import { Want, common } from '@kit.AbilityKit'; 176e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 177e41f4b71Sopenharmony_ci 178e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 179e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 180e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 181e41f4b71Sopenharmony_ci 182e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 183e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 184e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 185e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 186e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 187e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 188e41f4b71Sopenharmony_ci return; 189e41f4b71Sopenharmony_ci } 190e41f4b71Sopenharmony_ci if (list.length === 0) { 191e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 192e41f4b71Sopenharmony_ci return; 193e41f4b71Sopenharmony_ci } 194e41f4b71Sopenharmony_ci return list[0].networkId; 195e41f4b71Sopenharmony_ci } else { 196e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 197e41f4b71Sopenharmony_ci return; 198e41f4b71Sopenharmony_ci } 199e41f4b71Sopenharmony_ci }; 200e41f4b71Sopenharmony_ci 201e41f4b71Sopenharmony_ci @Entry 202e41f4b71Sopenharmony_ci @Component 203e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 204e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 205e41f4b71Sopenharmony_ci 206e41f4b71Sopenharmony_ci build() { 207e41f4b71Sopenharmony_ci // ... 208e41f4b71Sopenharmony_ci Button('stopServiceExtensionAbility') 209e41f4b71Sopenharmony_ci .onClick(() => { 210e41f4b71Sopenharmony_ci let want: Want = { 211e41f4b71Sopenharmony_ci deviceId: getRemoteDeviceId(), 212e41f4b71Sopenharmony_ci bundleName: 'com.example.myapplication', 213e41f4b71Sopenharmony_ci abilityName: 'FuncAbility', 214e41f4b71Sopenharmony_ci moduleName: 'module1', // moduleName is optional. 215e41f4b71Sopenharmony_ci } 216e41f4b71Sopenharmony_ci // Stop the ServiceExtensionAbility started by calling startAbility(). 217e41f4b71Sopenharmony_ci this.context.stopServiceExtensionAbility(want).then(() => { 218e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, "stop service extension ability success") 219e41f4b71Sopenharmony_ci }).catch((err: BusinessError) => { 220e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, `stop service extension ability err is ` + JSON.stringify(err)); 221e41f4b71Sopenharmony_ci }) 222e41f4b71Sopenharmony_ci }) 223e41f4b71Sopenharmony_ci } 224e41f4b71Sopenharmony_ci } 225e41f4b71Sopenharmony_ci ``` 226e41f4b71Sopenharmony_ci 227e41f4b71Sopenharmony_ci## Starting UIAbility Across Devices (Data Returned) 228e41f4b71Sopenharmony_ci 229e41f4b71Sopenharmony_ciOn device A, touch the Start button provided by the initiator application to start a specified [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) on device B. When the UIAbility on device B exits, a value is returned to the initiator application. 230e41f4b71Sopenharmony_ci 231e41f4b71Sopenharmony_ci 232e41f4b71Sopenharmony_ci### Available APIs 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci**Table 2** APIs for starting a UIAbility across devices and returning the result data 235e41f4b71Sopenharmony_ci 236e41f4b71Sopenharmony_ci| API| Description| 237e41f4b71Sopenharmony_ci| -------- | -------- | 238e41f4b71Sopenharmony_ci| startAbilityForResult(want: Want, callback: AsyncCallback<AbilityResult>): void; | Starts a UIAbility. This API uses an asynchronous callback to return the result when the UIAbility is terminated.| 239e41f4b71Sopenharmony_ci| terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback<void>): void;| Terminates this UIAbility. This API uses an asynchronous callback to return the result. It is used together with **startAbilityForResult**.| 240e41f4b71Sopenharmony_ci| terminateSelfWithResult(parameter: AbilityResult): Promise<void>; | Terminates this UIAbility. This API uses a promise to return the result. It is used together with **startAbilityForResult**.| 241e41f4b71Sopenharmony_ci 242e41f4b71Sopenharmony_ci 243e41f4b71Sopenharmony_ci### How to Develop 244e41f4b71Sopenharmony_ci 245e41f4b71Sopenharmony_ci1. Declare the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 246e41f4b71Sopenharmony_ci 247e41f4b71Sopenharmony_ci2. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/AccessToken/request-user-authorization.md). 248e41f4b71Sopenharmony_ci 249e41f4b71Sopenharmony_ci3. Set the target component parameters on the initiator, and call [startAbilityForResult()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilityforresult) to start the target UIAbility. **data** in the asynchronous callback is used to receive the information returned by the target UIAbility to the initiator UIAbility after the target UIAbility terminates itself. For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned). 250e41f4b71Sopenharmony_ci 251e41f4b71Sopenharmony_ci ```ts 252e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 253e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 254e41f4b71Sopenharmony_ci import { Want, common } from '@kit.AbilityKit'; 255e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 256e41f4b71Sopenharmony_ci import { promptAction } from '@kit.ArkUI'; 257e41f4b71Sopenharmony_ci 258e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 259e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 260e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 261e41f4b71Sopenharmony_ci 262e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 263e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 264e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 265e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 266e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 267e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 268e41f4b71Sopenharmony_ci return; 269e41f4b71Sopenharmony_ci } 270e41f4b71Sopenharmony_ci if (list.length === 0) { 271e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 272e41f4b71Sopenharmony_ci return; 273e41f4b71Sopenharmony_ci } 274e41f4b71Sopenharmony_ci return list[0].networkId; 275e41f4b71Sopenharmony_ci } else { 276e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 277e41f4b71Sopenharmony_ci return; 278e41f4b71Sopenharmony_ci } 279e41f4b71Sopenharmony_ci }; 280e41f4b71Sopenharmony_ci 281e41f4b71Sopenharmony_ci @Entry 282e41f4b71Sopenharmony_ci @Component 283e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 284e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 285e41f4b71Sopenharmony_ci 286e41f4b71Sopenharmony_ci build() { 287e41f4b71Sopenharmony_ci Column() { 288e41f4b71Sopenharmony_ci //... 289e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 290e41f4b71Sopenharmony_ci //... 291e41f4b71Sopenharmony_ci ListItem() { 292e41f4b71Sopenharmony_ci Row() { 293e41f4b71Sopenharmony_ci //... 294e41f4b71Sopenharmony_ci } 295e41f4b71Sopenharmony_ci .onClick(() => { 296e41f4b71Sopenharmony_ci let want: Want = { 297e41f4b71Sopenharmony_ci deviceId: getRemoteDeviceId(), 298e41f4b71Sopenharmony_ci bundleName: 'com.samples.stagemodelabilityinteraction', 299e41f4b71Sopenharmony_ci abilityName: 'ServiceExtAbility', 300e41f4b71Sopenharmony_ci moduleName: 'entry', // moduleName is optional. 301e41f4b71Sopenharmony_ci }; 302e41f4b71Sopenharmony_ci // Stop the ServiceExtensionAbility started by calling startAbility(). 303e41f4b71Sopenharmony_ci this.context.stopServiceExtensionAbility(want).then(() => { 304e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'stop service extension ability success') 305e41f4b71Sopenharmony_ci promptAction.showToast({ 306e41f4b71Sopenharmony_ci message: 'SuccessfullyStop' 307e41f4b71Sopenharmony_ci }); 308e41f4b71Sopenharmony_ci }).catch((err: BusinessError) => { 309e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, `stop service extension ability err is ` + JSON.stringify(err)); 310e41f4b71Sopenharmony_ci }); 311e41f4b71Sopenharmony_ci }) 312e41f4b71Sopenharmony_ci } 313e41f4b71Sopenharmony_ci //... 314e41f4b71Sopenharmony_ci } 315e41f4b71Sopenharmony_ci //... 316e41f4b71Sopenharmony_ci } 317e41f4b71Sopenharmony_ci //... 318e41f4b71Sopenharmony_ci } 319e41f4b71Sopenharmony_ci } 320e41f4b71Sopenharmony_ci ``` 321e41f4b71Sopenharmony_ci 322e41f4b71Sopenharmony_ci4. After the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) mission on the target device is complete, call [terminateSelfWithResult()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) to return the data to the initiator UIAbility. 323e41f4b71Sopenharmony_ci 324e41f4b71Sopenharmony_ci ```ts 325e41f4b71Sopenharmony_ci import { common } from '@kit.AbilityKit'; 326e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 327e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 328e41f4b71Sopenharmony_ci 329e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 330e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 331e41f4b71Sopenharmony_ci 332e41f4b71Sopenharmony_ci @Entry 333e41f4b71Sopenharmony_ci @Component 334e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 335e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 336e41f4b71Sopenharmony_ci 337e41f4b71Sopenharmony_ci build() { 338e41f4b71Sopenharmony_ci Column() { 339e41f4b71Sopenharmony_ci //... 340e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 341e41f4b71Sopenharmony_ci //... 342e41f4b71Sopenharmony_ci ListItem() { 343e41f4b71Sopenharmony_ci Row() { 344e41f4b71Sopenharmony_ci //... 345e41f4b71Sopenharmony_ci } 346e41f4b71Sopenharmony_ci .onClick(() => { 347e41f4b71Sopenharmony_ci const RESULT_CODE: number = 1001; 348e41f4b71Sopenharmony_ci // context is the AbilityContext of the target UIAbility. 349e41f4b71Sopenharmony_ci this.context.terminateSelfWithResult( 350e41f4b71Sopenharmony_ci { 351e41f4b71Sopenharmony_ci resultCode: RESULT_CODE, 352e41f4b71Sopenharmony_ci want: { 353e41f4b71Sopenharmony_ci bundleName: 'ohos.samples.stagemodelabilitydevelop', 354e41f4b71Sopenharmony_ci abilityName: 'CollaborateAbility', 355e41f4b71Sopenharmony_ci moduleName: 'entry', 356e41f4b71Sopenharmony_ci parameters: { 357e41f4b71Sopenharmony_ci info: 'From Page_CollaborateAbility' 358e41f4b71Sopenharmony_ci } 359e41f4b71Sopenharmony_ci } 360e41f4b71Sopenharmony_ci }, 361e41f4b71Sopenharmony_ci (err: BusinessError) => { 362e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `terminateSelfWithResult err: ` + JSON.stringify(err)); 363e41f4b71Sopenharmony_ci }); 364e41f4b71Sopenharmony_ci }) 365e41f4b71Sopenharmony_ci } 366e41f4b71Sopenharmony_ci //... 367e41f4b71Sopenharmony_ci } 368e41f4b71Sopenharmony_ci //... 369e41f4b71Sopenharmony_ci } 370e41f4b71Sopenharmony_ci //... 371e41f4b71Sopenharmony_ci } 372e41f4b71Sopenharmony_ci } 373e41f4b71Sopenharmony_ci ``` 374e41f4b71Sopenharmony_ci 375e41f4b71Sopenharmony_ci5. The initiator [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) receives the information returned by the target UIAbility and processes the information. 376e41f4b71Sopenharmony_ci 377e41f4b71Sopenharmony_ci ```ts 378e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 379e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 380e41f4b71Sopenharmony_ci import { Want, common } from '@kit.AbilityKit'; 381e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 382e41f4b71Sopenharmony_ci import { promptAction } from '@kit.ArkUI'; 383e41f4b71Sopenharmony_ci 384e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 385e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 386e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 387e41f4b71Sopenharmony_ci 388e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 389e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 390e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 391e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 392e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 393e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 394e41f4b71Sopenharmony_ci return; 395e41f4b71Sopenharmony_ci } 396e41f4b71Sopenharmony_ci if (list.length === 0) { 397e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 398e41f4b71Sopenharmony_ci return; 399e41f4b71Sopenharmony_ci } 400e41f4b71Sopenharmony_ci return list[0].networkId; 401e41f4b71Sopenharmony_ci } else { 402e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 403e41f4b71Sopenharmony_ci return; 404e41f4b71Sopenharmony_ci } 405e41f4b71Sopenharmony_ci }; 406e41f4b71Sopenharmony_ci 407e41f4b71Sopenharmony_ci @Entry 408e41f4b71Sopenharmony_ci @Component 409e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 410e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 411e41f4b71Sopenharmony_ci 412e41f4b71Sopenharmony_ci build() { 413e41f4b71Sopenharmony_ci Column() { 414e41f4b71Sopenharmony_ci //... 415e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 416e41f4b71Sopenharmony_ci //... 417e41f4b71Sopenharmony_ci ListItem() { 418e41f4b71Sopenharmony_ci Row() { 419e41f4b71Sopenharmony_ci //... 420e41f4b71Sopenharmony_ci } 421e41f4b71Sopenharmony_ci .onClick(() => { 422e41f4b71Sopenharmony_ci let want: Want = { 423e41f4b71Sopenharmony_ci deviceId: getRemoteDeviceId(), 424e41f4b71Sopenharmony_ci bundleName: 'com.samples.stagemodelabilityinteraction', 425e41f4b71Sopenharmony_ci abilityName: 'CollaborateAbility', 426e41f4b71Sopenharmony_ci moduleName: 'entry', // moduleName is optional. 427e41f4b71Sopenharmony_ci }; 428e41f4b71Sopenharmony_ci const RESULT_CODE: number = 1001; 429e41f4b71Sopenharmony_ci // context is the UIAbilityContext of the initiator UIAbility. 430e41f4b71Sopenharmony_ci this.context.startAbilityForResult(want).then((data) => { 431e41f4b71Sopenharmony_ci if (data?.resultCode === RESULT_CODE) { 432e41f4b71Sopenharmony_ci // Parse the information returned by the target UIAbility. 433e41f4b71Sopenharmony_ci let info = data.want?.parameters?.info; 434e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? ''); 435e41f4b71Sopenharmony_ci if (info !== null) { 436e41f4b71Sopenharmony_ci promptAction.showToast({ 437e41f4b71Sopenharmony_ci message: JSON.stringify(info) 438e41f4b71Sopenharmony_ci }); 439e41f4b71Sopenharmony_ci } 440e41f4b71Sopenharmony_ci } 441e41f4b71Sopenharmony_ci }).catch((error: BusinessError) => { 442e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, `startAbilityForResult err: ` + JSON.stringify(error)); 443e41f4b71Sopenharmony_ci }); 444e41f4b71Sopenharmony_ci }) 445e41f4b71Sopenharmony_ci } 446e41f4b71Sopenharmony_ci //... 447e41f4b71Sopenharmony_ci } 448e41f4b71Sopenharmony_ci //... 449e41f4b71Sopenharmony_ci } 450e41f4b71Sopenharmony_ci //... 451e41f4b71Sopenharmony_ci } 452e41f4b71Sopenharmony_ci } 453e41f4b71Sopenharmony_ci ``` 454e41f4b71Sopenharmony_ci 455e41f4b71Sopenharmony_ci 456e41f4b71Sopenharmony_ci## Connecting to ServiceExtensionAbility Across Devices 457e41f4b71Sopenharmony_ci 458e41f4b71Sopenharmony_ciA system application can connect to a service on another device by calling [connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability). For example, in the distributed game scenario, a tablet is used as the remote control and a smart TV is used as the display. 459e41f4b71Sopenharmony_ci 460e41f4b71Sopenharmony_ci 461e41f4b71Sopenharmony_ci### Available APIs 462e41f4b71Sopenharmony_ci 463e41f4b71Sopenharmony_ci**Table 3** APIs for cross-device connection 464e41f4b71Sopenharmony_ci 465e41f4b71Sopenharmony_ci| API| Description| 466e41f4b71Sopenharmony_ci| -------- | -------- | 467e41f4b71Sopenharmony_ci| connectServiceExtensionAbility(want: Want, options: ConnectOptions): number; | Connects to a ServiceExtensionAbility.| 468e41f4b71Sopenharmony_ci| disconnectServiceExtensionAbility(connection: number, callback: AsyncCallback<void>): void; | Disconnects a connection. This API uses an asynchronous callback to return the result.| 469e41f4b71Sopenharmony_ci| disconnectServiceExtensionAbility(connection: number): Promise<void>; | Disconnects a connection. This API uses a promise to return the result.| 470e41f4b71Sopenharmony_ci 471e41f4b71Sopenharmony_ci 472e41f4b71Sopenharmony_ci### How to Develop 473e41f4b71Sopenharmony_ci 474e41f4b71Sopenharmony_ci1. Declare the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 475e41f4b71Sopenharmony_ci 476e41f4b71Sopenharmony_ci2. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/AccessToken/request-user-authorization.md). 477e41f4b71Sopenharmony_ci 478e41f4b71Sopenharmony_ci3. (Optional) [Implement a background service](serviceextensionability.md#implementing-a-background-service-for-system-applications-only). Perform this operation only if no background service is available. This operation is available only for system applications. 479e41f4b71Sopenharmony_ci 480e41f4b71Sopenharmony_ci4. Connect to the background service. 481e41f4b71Sopenharmony_ci - Implement the **IAbilityConnection** class. **IAbilityConnection** provides the following callbacks that you should implement: [onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect), [onDisconnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#ondisconnect), and [onFailed()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onfailed). The **onConnect()** callback is invoked when a service is connected, **onDisconnect()** is invoked when a service is unexpectedly disconnected, and **onFailed()** is invoked when the connection to a service fails. 482e41f4b71Sopenharmony_ci - Set the target component parameters, including the target device ID, bundle name, and ability name. 483e41f4b71Sopenharmony_ci - Call **connectServiceExtensionAbility()** to initiate a connection. 484e41f4b71Sopenharmony_ci - Receive the service handle returned by the target device when the connection is successful. 485e41f4b71Sopenharmony_ci - Perform cross-device call and obtain the result returned by the target service. 486e41f4b71Sopenharmony_ci 487e41f4b71Sopenharmony_ci ```ts 488e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 489e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 490e41f4b71Sopenharmony_ci import { Want, common } from '@kit.AbilityKit'; 491e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 492e41f4b71Sopenharmony_ci import { rpc } from '@kit.IPCKit'; 493e41f4b71Sopenharmony_ci 494e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 495e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 496e41f4b71Sopenharmony_ci const REQUEST_CODE = 1; 497e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 498e41f4b71Sopenharmony_ci let connectionId: number; 499e41f4b71Sopenharmony_ci let options: common.ConnectOptions = { 500e41f4b71Sopenharmony_ci onConnect(elementName, remote): void { 501e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 502e41f4b71Sopenharmony_ci if (remote === null) { 503e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 504e41f4b71Sopenharmony_ci return; 505e41f4b71Sopenharmony_ci } 506e41f4b71Sopenharmony_ci let option = new rpc.MessageOption(); 507e41f4b71Sopenharmony_ci let data = new rpc.MessageSequence(); 508e41f4b71Sopenharmony_ci let reply = new rpc.MessageSequence(); 509e41f4b71Sopenharmony_ci data.writeInt(99); // You can send data to the target application for corresponding operations. 510e41f4b71Sopenharmony_ci // @param code Indicates the service request code sent by the client. 511e41f4b71Sopenharmony_ci // @param data Indicates the {@link MessageSequence} object sent by the client. 512e41f4b71Sopenharmony_ci // @param reply Indicates the response message object sent by the remote service. 513e41f4b71Sopenharmony_ci // @param options Specifies whether the operation is synchronous or asynchronous. 514e41f4b71Sopenharmony_ci // 515e41f4b71Sopenharmony_ci // @return Returns {@code true} if the operation is successful; returns {@code false} otherwise. 516e41f4b71Sopenharmony_ci remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => { 517e41f4b71Sopenharmony_ci let errCode = reply.readInt(); // Receive the information (100) returned by the target device if the connection is successful. 518e41f4b71Sopenharmony_ci let msg: number = 0; 519e41f4b71Sopenharmony_ci if (errCode === 0) { 520e41f4b71Sopenharmony_ci msg = reply.readInt(); 521e41f4b71Sopenharmony_ci } 522e41f4b71Sopenharmony_ci // The background service is connected. 523e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`); 524e41f4b71Sopenharmony_ci }).catch((error: BusinessError) => { 525e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`); 526e41f4b71Sopenharmony_ci }); 527e41f4b71Sopenharmony_ci }, 528e41f4b71Sopenharmony_ci onDisconnect(elementName): void { 529e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 530e41f4b71Sopenharmony_ci }, 531e41f4b71Sopenharmony_ci onFailed(code): void { 532e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback'); 533e41f4b71Sopenharmony_ci } 534e41f4b71Sopenharmony_ci }; 535e41f4b71Sopenharmony_ci 536e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 537e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 538e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 539e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 540e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 541e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 542e41f4b71Sopenharmony_ci return; 543e41f4b71Sopenharmony_ci } 544e41f4b71Sopenharmony_ci if (list.length === 0) { 545e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 546e41f4b71Sopenharmony_ci return; 547e41f4b71Sopenharmony_ci } 548e41f4b71Sopenharmony_ci return list[0].networkId; 549e41f4b71Sopenharmony_ci } else { 550e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 551e41f4b71Sopenharmony_ci return; 552e41f4b71Sopenharmony_ci } 553e41f4b71Sopenharmony_ci } 554e41f4b71Sopenharmony_ci 555e41f4b71Sopenharmony_ci @Entry 556e41f4b71Sopenharmony_ci @Component 557e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 558e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 559e41f4b71Sopenharmony_ci 560e41f4b71Sopenharmony_ci build() { 561e41f4b71Sopenharmony_ci Column() { 562e41f4b71Sopenharmony_ci //... 563e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 564e41f4b71Sopenharmony_ci //... 565e41f4b71Sopenharmony_ci ListItem() { 566e41f4b71Sopenharmony_ci Row() { 567e41f4b71Sopenharmony_ci //... 568e41f4b71Sopenharmony_ci } 569e41f4b71Sopenharmony_ci .onClick(() => { 570e41f4b71Sopenharmony_ci let want: Want = { 571e41f4b71Sopenharmony_ci 'deviceId': getRemoteDeviceId(), 572e41f4b71Sopenharmony_ci 'bundleName': 'com.samples.stagemodelabilityinteraction', 573e41f4b71Sopenharmony_ci 'abilityName': 'ServiceExtAbility' 574e41f4b71Sopenharmony_ci }; 575e41f4b71Sopenharmony_ci // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection. 576e41f4b71Sopenharmony_ci connectionId = this.context.connectServiceExtensionAbility(want, options); 577e41f4b71Sopenharmony_ci }) 578e41f4b71Sopenharmony_ci } 579e41f4b71Sopenharmony_ci //... 580e41f4b71Sopenharmony_ci } 581e41f4b71Sopenharmony_ci //... 582e41f4b71Sopenharmony_ci } 583e41f4b71Sopenharmony_ci //... 584e41f4b71Sopenharmony_ci } 585e41f4b71Sopenharmony_ci } 586e41f4b71Sopenharmony_ci ``` 587e41f4b71Sopenharmony_ci 588e41f4b71Sopenharmony_ci For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned). 589e41f4b71Sopenharmony_ci 590e41f4b71Sopenharmony_ci5. Disconnect the connection. Use [disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) to disconnect from the background service. 591e41f4b71Sopenharmony_ci 592e41f4b71Sopenharmony_ci ```ts 593e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 594e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 595e41f4b71Sopenharmony_ci import { common } from '@kit.AbilityKit'; 596e41f4b71Sopenharmony_ci import { promptAction } from '@kit.ArkUI'; 597e41f4b71Sopenharmony_ci 598e41f4b71Sopenharmony_ci let connectionId: number; 599e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 600e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 601e41f4b71Sopenharmony_ci 602e41f4b71Sopenharmony_ci @Entry 603e41f4b71Sopenharmony_ci @Component 604e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 605e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 606e41f4b71Sopenharmony_ci 607e41f4b71Sopenharmony_ci build() { 608e41f4b71Sopenharmony_ci Column() { 609e41f4b71Sopenharmony_ci //... 610e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 611e41f4b71Sopenharmony_ci //... 612e41f4b71Sopenharmony_ci ListItem() { 613e41f4b71Sopenharmony_ci Row() { 614e41f4b71Sopenharmony_ci //... 615e41f4b71Sopenharmony_ci } 616e41f4b71Sopenharmony_ci .onClick(() => { 617e41f4b71Sopenharmony_ci this.context.disconnectServiceExtensionAbility(connectionId).then(() => { 618e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success'); 619e41f4b71Sopenharmony_ci // The background service is disconnected. 620e41f4b71Sopenharmony_ci promptAction.showToast({ 621e41f4b71Sopenharmony_ci message: 'SuccessfullyDisconnectBackendService' 622e41f4b71Sopenharmony_ci }) 623e41f4b71Sopenharmony_ci }).catch((error: BusinessError) => { 624e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed'); 625e41f4b71Sopenharmony_ci }); 626e41f4b71Sopenharmony_ci }) 627e41f4b71Sopenharmony_ci } 628e41f4b71Sopenharmony_ci //... 629e41f4b71Sopenharmony_ci } 630e41f4b71Sopenharmony_ci //... 631e41f4b71Sopenharmony_ci } 632e41f4b71Sopenharmony_ci //... 633e41f4b71Sopenharmony_ci } 634e41f4b71Sopenharmony_ci } 635e41f4b71Sopenharmony_ci ``` 636e41f4b71Sopenharmony_ci 637e41f4b71Sopenharmony_ci 638e41f4b71Sopenharmony_ci## Using Cross-Device Call 639e41f4b71Sopenharmony_ci 640e41f4b71Sopenharmony_ciThe basic principle of cross-device call is the same as that of intra-device call. For details, see [Using Call to Implement UIAbility Interaction (for System Applications Only)](uiability-intra-device-interaction.md#using-call-to-implement-uiability-interaction-for-system-applications-only). 641e41f4b71Sopenharmony_ci 642e41f4b71Sopenharmony_ciThe following describes how to implement multi-device collaboration through cross-device call. 643e41f4b71Sopenharmony_ci 644e41f4b71Sopenharmony_ci 645e41f4b71Sopenharmony_ci### Available APIs 646e41f4b71Sopenharmony_ci 647e41f4b71Sopenharmony_ci**Table 4** Call APIs 648e41f4b71Sopenharmony_ci 649e41f4b71Sopenharmony_ci| API| Description| 650e41f4b71Sopenharmony_ci| -------- | -------- | 651e41f4b71Sopenharmony_ci| startAbilityByCall(want: Want): Promise<Caller>; | Starts a UIAbility in the foreground or background and obtains the caller object for communicating with the UIAbility.| 652e41f4b71Sopenharmony_ci| on(method: string, callback: CalleeCallBack): void | Callback invoked when the CalleeAbility registers a method.| 653e41f4b71Sopenharmony_ci| off(method: string): void | Callback invoked when the CalleeAbility deregisters a method.| 654e41f4b71Sopenharmony_ci| call(method: string, data: rpc.Parcelable): Promise<void> | Sends agreed parcelable data to the CalleeAbility.| 655e41f4b71Sopenharmony_ci| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence>| Sends agreed parcelable data to the CalleeAbility and obtains the agreed parcelable data returned by the CalleeAbility.| 656e41f4b71Sopenharmony_ci| release(): void | Releases the caller object.| 657e41f4b71Sopenharmony_ci| on(type: "release", callback: OnReleaseCallback): void | Callback invoked when the caller object is released.| 658e41f4b71Sopenharmony_ci 659e41f4b71Sopenharmony_ci 660e41f4b71Sopenharmony_ci### How to Develop 661e41f4b71Sopenharmony_ci 662e41f4b71Sopenharmony_ci1. Declare the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 663e41f4b71Sopenharmony_ci 664e41f4b71Sopenharmony_ci2. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/AccessToken/request-user-authorization.md). 665e41f4b71Sopenharmony_ci 666e41f4b71Sopenharmony_ci3. Create the CalleeAbility. 667e41f4b71Sopenharmony_ci 668e41f4b71Sopenharmony_ci For the CalleeAbility, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use [on](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeon) to register a listener. When data does not need to be received, use [off](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeoff) to deregister the listener. 669e41f4b71Sopenharmony_ci 670e41f4b71Sopenharmony_ci 1. Configure the launch type of the UIAbility. 671e41f4b71Sopenharmony_ci 672e41f4b71Sopenharmony_ci Set **launchType** of the CalleeAbility to **singleton** in the [module.json5](../quick-start/module-configuration-file.md) file. 673e41f4b71Sopenharmony_ci 674e41f4b71Sopenharmony_ci | JSON Field| Description| 675e41f4b71Sopenharmony_ci | -------- | -------- | 676e41f4b71Sopenharmony_ci | "launchType"| UIAbility launch type. Set this parameter to **singleton**.| 677e41f4b71Sopenharmony_ci 678e41f4b71Sopenharmony_ci An example of the UIAbility configuration is as follows: 679e41f4b71Sopenharmony_ci 680e41f4b71Sopenharmony_ci ```json 681e41f4b71Sopenharmony_ci "abilities":[{ 682e41f4b71Sopenharmony_ci "name": ".CalleeAbility", 683e41f4b71Sopenharmony_ci "srcEntry": "./ets/CalleeAbility/CalleeAbility.ets", 684e41f4b71Sopenharmony_ci "launchType": "singleton", 685e41f4b71Sopenharmony_ci "description": "$string:CalleeAbility_desc", 686e41f4b71Sopenharmony_ci "icon": "$media:icon", 687e41f4b71Sopenharmony_ci "label": "$string:CalleeAbility_label", 688e41f4b71Sopenharmony_ci "exported": true 689e41f4b71Sopenharmony_ci }] 690e41f4b71Sopenharmony_ci ``` 691e41f4b71Sopenharmony_ci 2. Import the **UIAbility** module. 692e41f4b71Sopenharmony_ci 693e41f4b71Sopenharmony_ci ```ts 694e41f4b71Sopenharmony_ci import { UIAbility } from '@kit.AbilityKit'; 695e41f4b71Sopenharmony_ci ``` 696e41f4b71Sopenharmony_ci 3. Define the agreed parcelable data. 697e41f4b71Sopenharmony_ci 698e41f4b71Sopenharmony_ci The data formats sent and received by the CallerAbility and CalleeAbility must be consistent. In the following example, the data formats are number and string. 699e41f4b71Sopenharmony_ci 700e41f4b71Sopenharmony_ci 701e41f4b71Sopenharmony_ci ```ts 702e41f4b71Sopenharmony_ci import { rpc } from '@kit.IPCKit'; 703e41f4b71Sopenharmony_ci 704e41f4b71Sopenharmony_ci class MyParcelable { 705e41f4b71Sopenharmony_ci num: number = 0; 706e41f4b71Sopenharmony_ci str: string = ''; 707e41f4b71Sopenharmony_ci 708e41f4b71Sopenharmony_ci constructor(num: number, string: string) { 709e41f4b71Sopenharmony_ci this.num = num; 710e41f4b71Sopenharmony_ci this.str = string; 711e41f4b71Sopenharmony_ci } 712e41f4b71Sopenharmony_ci 713e41f4b71Sopenharmony_ci mySequenceable(num: number, string: string): void { 714e41f4b71Sopenharmony_ci this.num = num; 715e41f4b71Sopenharmony_ci this.str = string; 716e41f4b71Sopenharmony_ci } 717e41f4b71Sopenharmony_ci 718e41f4b71Sopenharmony_ci marshalling(messageSequence: rpc.MessageSequence): boolean { 719e41f4b71Sopenharmony_ci messageSequence.writeInt(this.num); 720e41f4b71Sopenharmony_ci messageSequence.writeString(this.str); 721e41f4b71Sopenharmony_ci return true; 722e41f4b71Sopenharmony_ci }; 723e41f4b71Sopenharmony_ci 724e41f4b71Sopenharmony_ci unmarshalling(messageSequence: rpc.MessageSequence): boolean { 725e41f4b71Sopenharmony_ci this.num = messageSequence.readInt(); 726e41f4b71Sopenharmony_ci this.str = messageSequence.readString(); 727e41f4b71Sopenharmony_ci return true; 728e41f4b71Sopenharmony_ci }; 729e41f4b71Sopenharmony_ci } 730e41f4b71Sopenharmony_ci ``` 731e41f4b71Sopenharmony_ci 4. Implement [Callee.on](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeon) and [Callee.off](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeoff). 732e41f4b71Sopenharmony_ci 733e41f4b71Sopenharmony_ci In the following example, the **MSG_SEND_METHOD** listener is registered in [onCreate](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) of the UIAbility and deregistered in [onDestroy](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityondestroy). After receiving parcelable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. 734e41f4b71Sopenharmony_ci 735e41f4b71Sopenharmony_ci ```ts 736e41f4b71Sopenharmony_ci import { AbilityConstant, UIAbility, Want, Caller } from '@kit.AbilityKit'; 737e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 738e41f4b71Sopenharmony_ci import { rpc } from '@kit.IPCKit'; 739e41f4b71Sopenharmony_ci 740e41f4b71Sopenharmony_ci 741e41f4b71Sopenharmony_ci const TAG: string = '[CalleeAbility]'; 742e41f4b71Sopenharmony_ci const MSG_SEND_METHOD: string = 'CallSendMsg'; 743e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 744e41f4b71Sopenharmony_ci 745e41f4b71Sopenharmony_ci class MyParcelable { 746e41f4b71Sopenharmony_ci num: number = 0; 747e41f4b71Sopenharmony_ci str: string = ''; 748e41f4b71Sopenharmony_ci 749e41f4b71Sopenharmony_ci constructor(num: number, string: string) { 750e41f4b71Sopenharmony_ci this.num = num; 751e41f4b71Sopenharmony_ci this.str = string; 752e41f4b71Sopenharmony_ci }; 753e41f4b71Sopenharmony_ci 754e41f4b71Sopenharmony_ci mySequenceable(num: number, string: string): void { 755e41f4b71Sopenharmony_ci this.num = num; 756e41f4b71Sopenharmony_ci this.str = string; 757e41f4b71Sopenharmony_ci }; 758e41f4b71Sopenharmony_ci 759e41f4b71Sopenharmony_ci marshalling(messageSequence: rpc.MessageSequence): boolean { 760e41f4b71Sopenharmony_ci messageSequence.writeInt(this.num); 761e41f4b71Sopenharmony_ci messageSequence.writeString(this.str); 762e41f4b71Sopenharmony_ci return true; 763e41f4b71Sopenharmony_ci }; 764e41f4b71Sopenharmony_ci 765e41f4b71Sopenharmony_ci unmarshalling(messageSequence: rpc.MessageSequence): boolean { 766e41f4b71Sopenharmony_ci this.num = messageSequence.readInt(); 767e41f4b71Sopenharmony_ci this.str = messageSequence.readString(); 768e41f4b71Sopenharmony_ci return true; 769e41f4b71Sopenharmony_ci }; 770e41f4b71Sopenharmony_ci } 771e41f4b71Sopenharmony_ci 772e41f4b71Sopenharmony_ci function sendMsgCallback(data: rpc.MessageSequence): rpc.Parcelable { 773e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'CalleeSortFunc called'); 774e41f4b71Sopenharmony_ci 775e41f4b71Sopenharmony_ci // Obtain the parcelable data sent by the CallerAbility. 776e41f4b71Sopenharmony_ci let receivedData: MyParcelable = new MyParcelable(0, ''); 777e41f4b71Sopenharmony_ci data.readParcelable(receivedData); 778e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `receiveData[${receivedData.num}, ${receivedData.str}]`); 779e41f4b71Sopenharmony_ci let num: number = receivedData.num; 780e41f4b71Sopenharmony_ci 781e41f4b71Sopenharmony_ci // Process the data. 782e41f4b71Sopenharmony_ci // Return the parcelable data result to the CallerAbility. 783e41f4b71Sopenharmony_ci return new MyParcelable(num + 1, `send ${receivedData.str} succeed`) as rpc.Parcelable; 784e41f4b71Sopenharmony_ci }; 785e41f4b71Sopenharmony_ci 786e41f4b71Sopenharmony_ci export default class CalleeAbility extends UIAbility { 787e41f4b71Sopenharmony_ci caller: Caller | undefined; 788e41f4b71Sopenharmony_ci 789e41f4b71Sopenharmony_ci onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 790e41f4b71Sopenharmony_ci try { 791e41f4b71Sopenharmony_ci this.callee.on(MSG_SEND_METHOD, sendMsgCallback); 792e41f4b71Sopenharmony_ci } catch (error) { 793e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`); 794e41f4b71Sopenharmony_ci } 795e41f4b71Sopenharmony_ci } 796e41f4b71Sopenharmony_ci 797e41f4b71Sopenharmony_ci //... 798e41f4b71Sopenharmony_ci releaseCall(): void { 799e41f4b71Sopenharmony_ci try { 800e41f4b71Sopenharmony_ci if (this.caller) { 801e41f4b71Sopenharmony_ci this.caller.release(); 802e41f4b71Sopenharmony_ci this.caller = undefined; 803e41f4b71Sopenharmony_ci } 804e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'caller release succeed'); 805e41f4b71Sopenharmony_ci } catch (error) { 806e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `caller release failed with ${error}`); 807e41f4b71Sopenharmony_ci } 808e41f4b71Sopenharmony_ci } 809e41f4b71Sopenharmony_ci 810e41f4b71Sopenharmony_ci //... 811e41f4b71Sopenharmony_ci onDestroy(): void { 812e41f4b71Sopenharmony_ci try { 813e41f4b71Sopenharmony_ci this.callee.off(MSG_SEND_METHOD); 814e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Callee OnDestroy'); 815e41f4b71Sopenharmony_ci this.releaseCall(); 816e41f4b71Sopenharmony_ci } catch (error) { 817e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`); 818e41f4b71Sopenharmony_ci } 819e41f4b71Sopenharmony_ci } 820e41f4b71Sopenharmony_ci } 821e41f4b71Sopenharmony_ci ``` 822e41f4b71Sopenharmony_ci 823e41f4b71Sopenharmony_ci4. Obtain the caller object and access the CalleeAbility. 824e41f4b71Sopenharmony_ci 1. Import the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) module. 825e41f4b71Sopenharmony_ci 826e41f4b71Sopenharmony_ci ```ts 827e41f4b71Sopenharmony_ci import { UIAbility } from '@kit.AbilityKit'; 828e41f4b71Sopenharmony_ci ``` 829e41f4b71Sopenharmony_ci 2. Obtain the caller object. 830e41f4b71Sopenharmony_ci 831e41f4b71Sopenharmony_ci The **context** attribute of the UIAbility implements [startAbilityByCall](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilitybycall) to obtain the [Caller](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#caller) object for communication. The following example uses **this.context** to obtain the **context** attribute of the UIAbility, uses **startAbilityByCall** to start [Callee](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callee), obtain the Caller object, and register the [onRelease](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleronrelease) and [onRemoteStateChange](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleronremotestatechange10) listeners of the CallerAbility. You need to implement processing based on service requirements. 832e41f4b71Sopenharmony_ci 833e41f4b71Sopenharmony_ci ```ts 834e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 835e41f4b71Sopenharmony_ci import { Caller, common } from '@kit.AbilityKit'; 836e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 837e41f4b71Sopenharmony_ci import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 838e41f4b71Sopenharmony_ci import { promptAction } from '@kit.ArkUI'; 839e41f4b71Sopenharmony_ci 840e41f4b71Sopenharmony_ci 841e41f4b71Sopenharmony_ci const TAG: string = '[Page_CollaborateAbility]'; 842e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 843e41f4b71Sopenharmony_ci let caller: Caller | undefined; 844e41f4b71Sopenharmony_ci let dmClass: distributedDeviceManager.DeviceManager; 845e41f4b71Sopenharmony_ci 846e41f4b71Sopenharmony_ci function getRemoteDeviceId(): string | undefined { 847e41f4b71Sopenharmony_ci if (typeof dmClass === 'object' && dmClass !== null) { 848e41f4b71Sopenharmony_ci let list = dmClass.getAvailableDeviceListSync(); 849e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); 850e41f4b71Sopenharmony_ci if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 851e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); 852e41f4b71Sopenharmony_ci return; 853e41f4b71Sopenharmony_ci } 854e41f4b71Sopenharmony_ci if (list.length === 0) { 855e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); 856e41f4b71Sopenharmony_ci return; 857e41f4b71Sopenharmony_ci } 858e41f4b71Sopenharmony_ci return list[0].networkId; 859e41f4b71Sopenharmony_ci } else { 860e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); 861e41f4b71Sopenharmony_ci return; 862e41f4b71Sopenharmony_ci } 863e41f4b71Sopenharmony_ci }; 864e41f4b71Sopenharmony_ci 865e41f4b71Sopenharmony_ci @Entry 866e41f4b71Sopenharmony_ci @Component 867e41f4b71Sopenharmony_ci struct Page_CollaborateAbility { 868e41f4b71Sopenharmony_ci private context = getContext(this) as common.UIAbilityContext; 869e41f4b71Sopenharmony_ci 870e41f4b71Sopenharmony_ci build() { 871e41f4b71Sopenharmony_ci Column() { 872e41f4b71Sopenharmony_ci //... 873e41f4b71Sopenharmony_ci List({ initialIndex: 0 }) { 874e41f4b71Sopenharmony_ci //... 875e41f4b71Sopenharmony_ci ListItem() { 876e41f4b71Sopenharmony_ci Row() { 877e41f4b71Sopenharmony_ci //... 878e41f4b71Sopenharmony_ci } 879e41f4b71Sopenharmony_ci .onClick(() => { 880e41f4b71Sopenharmony_ci let caller: Caller | undefined; 881e41f4b71Sopenharmony_ci let context = this.context; 882e41f4b71Sopenharmony_ci 883e41f4b71Sopenharmony_ci context.startAbilityByCall({ 884e41f4b71Sopenharmony_ci deviceId: getRemoteDeviceId(), 885e41f4b71Sopenharmony_ci bundleName: 'com.samples.stagemodelabilityinteraction', 886e41f4b71Sopenharmony_ci abilityName: 'CalleeAbility' 887e41f4b71Sopenharmony_ci }).then((data) => { 888e41f4b71Sopenharmony_ci if (data !== null) { 889e41f4b71Sopenharmony_ci caller = data; 890e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'get remote caller success'); 891e41f4b71Sopenharmony_ci // Register the onRelease listener of the CallerAbility. 892e41f4b71Sopenharmony_ci caller.onRelease((msg) => { 893e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `remote caller onRelease is called ${msg}`); 894e41f4b71Sopenharmony_ci }); 895e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'remote caller register OnRelease succeed'); 896e41f4b71Sopenharmony_ci promptAction.showToast({ 897e41f4b71Sopenharmony_ci message: 'CallerSuccess' 898e41f4b71Sopenharmony_ci }); 899e41f4b71Sopenharmony_ci // Register the onRemoteStateChange listener of the CallerAbility. 900e41f4b71Sopenharmony_ci try { 901e41f4b71Sopenharmony_ci caller.onRemoteStateChange((str) => { 902e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'Remote state changed ' + str); 903e41f4b71Sopenharmony_ci }); 904e41f4b71Sopenharmony_ci } catch (error) { 905e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `Caller.onRemoteStateChange catch error, error.code: ${JSON.stringify(error.code)}, error.message: ${JSON.stringify(error.message)}`); 906e41f4b71Sopenharmony_ci } 907e41f4b71Sopenharmony_ci } 908e41f4b71Sopenharmony_ci }).catch((error: BusinessError) => { 909e41f4b71Sopenharmony_ci hilog.error(DOMAIN_NUMBER, TAG, `get remote caller failed with ${error}`); 910e41f4b71Sopenharmony_ci }); 911e41f4b71Sopenharmony_ci }) 912e41f4b71Sopenharmony_ci } 913e41f4b71Sopenharmony_ci //... 914e41f4b71Sopenharmony_ci } 915e41f4b71Sopenharmony_ci //... 916e41f4b71Sopenharmony_ci } 917e41f4b71Sopenharmony_ci //... 918e41f4b71Sopenharmony_ci } 919e41f4b71Sopenharmony_ci } 920e41f4b71Sopenharmony_ci ``` 921e41f4b71Sopenharmony_ci 922e41f4b71Sopenharmony_ci For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned). 923e41f4b71Sopenharmony_ci 924e41f4b71Sopenharmony_ci5. Sends agreed parcelable data to the CalleeAbility. 925e41f4b71Sopenharmony_ci 1. The parcelable data can be sent to the CalleeAbility with or without a return value. The method and parcelable data must be consistent with those of the CalleeAbility. The following example describes how to use [Call](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callercall) to send data to [Callee](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callee). 926e41f4b71Sopenharmony_ci 927e41f4b71Sopenharmony_ci ```ts 928e41f4b71Sopenharmony_ci import { UIAbility, Caller } from '@kit.AbilityKit'; 929e41f4b71Sopenharmony_ci import { rpc } from '@kit.IPCKit'; 930e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 931e41f4b71Sopenharmony_ci 932e41f4b71Sopenharmony_ci const TAG: string = '[CalleeAbility]'; 933e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 934e41f4b71Sopenharmony_ci const MSG_SEND_METHOD: string = 'CallSendMsg'; 935e41f4b71Sopenharmony_ci 936e41f4b71Sopenharmony_ci class MyParcelable { 937e41f4b71Sopenharmony_ci num: number = 0; 938e41f4b71Sopenharmony_ci str: string = ''; 939e41f4b71Sopenharmony_ci 940e41f4b71Sopenharmony_ci constructor(num: number, string: string) { 941e41f4b71Sopenharmony_ci this.num = num; 942e41f4b71Sopenharmony_ci this.str = string; 943e41f4b71Sopenharmony_ci }; 944e41f4b71Sopenharmony_ci 945e41f4b71Sopenharmony_ci mySequenceable(num: number, string: string): void { 946e41f4b71Sopenharmony_ci this.num = num; 947e41f4b71Sopenharmony_ci this.str = string; 948e41f4b71Sopenharmony_ci }; 949e41f4b71Sopenharmony_ci 950e41f4b71Sopenharmony_ci marshalling(messageSequence: rpc.MessageSequence): boolean { 951e41f4b71Sopenharmony_ci messageSequence.writeInt(this.num); 952e41f4b71Sopenharmony_ci messageSequence.writeString(this.str); 953e41f4b71Sopenharmony_ci return true; 954e41f4b71Sopenharmony_ci }; 955e41f4b71Sopenharmony_ci 956e41f4b71Sopenharmony_ci unmarshalling(messageSequence: rpc.MessageSequence): boolean { 957e41f4b71Sopenharmony_ci this.num = messageSequence.readInt(); 958e41f4b71Sopenharmony_ci this.str = messageSequence.readString(); 959e41f4b71Sopenharmony_ci return true; 960e41f4b71Sopenharmony_ci }; 961e41f4b71Sopenharmony_ci } 962e41f4b71Sopenharmony_ci 963e41f4b71Sopenharmony_ci export default class EntryAbility extends UIAbility { 964e41f4b71Sopenharmony_ci // ... 965e41f4b71Sopenharmony_ci caller: Caller | undefined; 966e41f4b71Sopenharmony_ci 967e41f4b71Sopenharmony_ci async onButtonCall(): Promise<void> { 968e41f4b71Sopenharmony_ci try { 969e41f4b71Sopenharmony_ci let msg: MyParcelable = new MyParcelable(1, 'origin_Msg'); 970e41f4b71Sopenharmony_ci if (this.caller) { 971e41f4b71Sopenharmony_ci await this.caller.call(MSG_SEND_METHOD, msg); 972e41f4b71Sopenharmony_ci } 973e41f4b71Sopenharmony_ci } catch (error) { 974e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `caller call failed with ${error}`); 975e41f4b71Sopenharmony_ci } 976e41f4b71Sopenharmony_ci } 977e41f4b71Sopenharmony_ci // ... 978e41f4b71Sopenharmony_ci } 979e41f4b71Sopenharmony_ci ``` 980e41f4b71Sopenharmony_ci 2. In the following, [CallWithResult](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callercallwithresult) is used to send data **originMsg** to the CalleeAbility and assign the data processed by the **CallSendMsg** method to **backMsg**. 981e41f4b71Sopenharmony_ci 982e41f4b71Sopenharmony_ci ```ts 983e41f4b71Sopenharmony_ci import { UIAbility, Caller } from '@kit.AbilityKit'; 984e41f4b71Sopenharmony_ci import { rpc } from '@kit.IPCKit'; 985e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 986e41f4b71Sopenharmony_ci 987e41f4b71Sopenharmony_ci const TAG: string = '[CalleeAbility]'; 988e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 989e41f4b71Sopenharmony_ci 990e41f4b71Sopenharmony_ci const MSG_SEND_METHOD: string = 'CallSendMsg'; 991e41f4b71Sopenharmony_ci let originMsg: string = ''; 992e41f4b71Sopenharmony_ci let backMsg: string = ''; 993e41f4b71Sopenharmony_ci 994e41f4b71Sopenharmony_ci class MyParcelable { 995e41f4b71Sopenharmony_ci num: number = 0; 996e41f4b71Sopenharmony_ci str: string = ''; 997e41f4b71Sopenharmony_ci 998e41f4b71Sopenharmony_ci constructor(num: number, string: string) { 999e41f4b71Sopenharmony_ci this.num = num; 1000e41f4b71Sopenharmony_ci this.str = string; 1001e41f4b71Sopenharmony_ci }; 1002e41f4b71Sopenharmony_ci 1003e41f4b71Sopenharmony_ci mySequenceable(num: number, string: string): void { 1004e41f4b71Sopenharmony_ci this.num = num; 1005e41f4b71Sopenharmony_ci this.str = string; 1006e41f4b71Sopenharmony_ci }; 1007e41f4b71Sopenharmony_ci 1008e41f4b71Sopenharmony_ci marshalling(messageSequence: rpc.MessageSequence): boolean { 1009e41f4b71Sopenharmony_ci messageSequence.writeInt(this.num); 1010e41f4b71Sopenharmony_ci messageSequence.writeString(this.str); 1011e41f4b71Sopenharmony_ci return true; 1012e41f4b71Sopenharmony_ci }; 1013e41f4b71Sopenharmony_ci 1014e41f4b71Sopenharmony_ci unmarshalling(messageSequence: rpc.MessageSequence): boolean { 1015e41f4b71Sopenharmony_ci this.num = messageSequence.readInt(); 1016e41f4b71Sopenharmony_ci this.str = messageSequence.readString(); 1017e41f4b71Sopenharmony_ci return true; 1018e41f4b71Sopenharmony_ci }; 1019e41f4b71Sopenharmony_ci } 1020e41f4b71Sopenharmony_ci 1021e41f4b71Sopenharmony_ci export default class EntryAbility extends UIAbility { 1022e41f4b71Sopenharmony_ci // ... 1023e41f4b71Sopenharmony_ci caller: Caller | undefined; 1024e41f4b71Sopenharmony_ci 1025e41f4b71Sopenharmony_ci async onButtonCallWithResult(originMsg: string, backMsg: string): Promise<void> { 1026e41f4b71Sopenharmony_ci try { 1027e41f4b71Sopenharmony_ci let msg: MyParcelable = new MyParcelable(1, originMsg); 1028e41f4b71Sopenharmony_ci if (this.caller) { 1029e41f4b71Sopenharmony_ci const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg); 1030e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'caller callWithResult succeed'); 1031e41f4b71Sopenharmony_ci let result: MyParcelable = new MyParcelable(0, ''); 1032e41f4b71Sopenharmony_ci data.readParcelable(result); 1033e41f4b71Sopenharmony_ci backMsg = result.str; 1034e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `caller result is [${result.num}, ${result.str}]`); 1035e41f4b71Sopenharmony_ci } 1036e41f4b71Sopenharmony_ci } catch (error) { 1037e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `caller callWithResult failed with ${error}`); 1038e41f4b71Sopenharmony_ci } 1039e41f4b71Sopenharmony_ci } 1040e41f4b71Sopenharmony_ci // ... 1041e41f4b71Sopenharmony_ci } 1042e41f4b71Sopenharmony_ci ``` 1043e41f4b71Sopenharmony_ci 1044e41f4b71Sopenharmony_ci6. Release the caller object. 1045e41f4b71Sopenharmony_ci 1046e41f4b71Sopenharmony_ci When the [Caller](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#caller) object is no longer required, use [release](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callerrelease) to release it. 1047e41f4b71Sopenharmony_ci 1048e41f4b71Sopenharmony_ci ```ts 1049e41f4b71Sopenharmony_ci import { UIAbility, Caller } from '@kit.AbilityKit'; 1050e41f4b71Sopenharmony_ci import { hilog } from '@kit.PerformanceAnalysisKit'; 1051e41f4b71Sopenharmony_ci 1052e41f4b71Sopenharmony_ci const TAG: string = '[CalleeAbility]'; 1053e41f4b71Sopenharmony_ci const DOMAIN_NUMBER: number = 0xFF00; 1054e41f4b71Sopenharmony_ci 1055e41f4b71Sopenharmony_ci export default class EntryAbility extends UIAbility { 1056e41f4b71Sopenharmony_ci caller: Caller | undefined; 1057e41f4b71Sopenharmony_ci 1058e41f4b71Sopenharmony_ci releaseCall(): void { 1059e41f4b71Sopenharmony_ci try { 1060e41f4b71Sopenharmony_ci if (this.caller) { 1061e41f4b71Sopenharmony_ci this.caller.release(); 1062e41f4b71Sopenharmony_ci this.caller = undefined; 1063e41f4b71Sopenharmony_ci } 1064e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, 'caller release succeed'); 1065e41f4b71Sopenharmony_ci } catch (error) { 1066e41f4b71Sopenharmony_ci hilog.info(DOMAIN_NUMBER, TAG, `caller release failed with ${error}`); 1067e41f4b71Sopenharmony_ci } 1068e41f4b71Sopenharmony_ci } 1069e41f4b71Sopenharmony_ci } 1070e41f4b71Sopenharmony_ci ``` 1071e41f4b71Sopenharmony_ci<!--no_check--> 1072