1# Ability Call Development 2## When to Use 3Ability call is an extension of the ability capabilities. It enables an ability to be invoked by external systems. In this way, the ability can be displayed as a UI page on the foreground and created and run on the background. You can use the **Call** APIs to implement data sharing between different abilities through inter-process communication (IPC). There are two roles in the ability call: caller and callee. The following scenarios are involved in the ability call development: 4- Creating a callee 5- Accessing the callee 6 7The following figure shows the ability call process. 8 9 10 11>  **NOTE**<br/> 12> The startup mode of the callee must be **singleton**. 13> Currently, only system applications and Service Extension abilities can use the **Call** APIs to access the callee. 14 15## Available APIs 16The table below describes the ability call APIs. For details, see [Ability](../reference/apis/js-apis-application-ability.md#caller). 17 18**Table 1** Ability call APIs 19|API|Description| 20|:------|:------| 21|startAbilityByCall(want: Want): Promise\<Caller>|Obtains the caller interface of the specified ability and, if the specified ability is not running, starts the ability in the background.| 22|on(method: string, callback: CaleeCallBack): void|Callback invoked when the callee registers a method.| 23|off(method: string): void|Callback invoked when the callee deregisters a method.| 24|call(method: string, data: rpc.Sequenceable): Promise\<void>|Sends agreed sequenceable data to the callee.| 25|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|Sends agreed sequenceable data to the callee and returns the agreed sequenceable data.| 26|release(): void|Releases the caller interface.| 27|onRelease(callback: OnReleaseCallBack): void|Registers a callback that is invoked when the caller is disconnected.| 28 29## How to Develop 30>  **NOTE**<br/> 31> The sample code snippets provided in the **How to Develop** section are used to show specific development steps. They may not be able to run independently. For details about the complete project code, see [Samples](#samples). 32### Creating a Callee 33For the callee, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use the **on** API to register a listener. When data does not need to be received, use the **off** API to deregister the listener. 341. Configure the ability startup mode. 35 36 Set the ability of the callee to **singleton** in the **module.json5** file. 37 38|JSON Field|Description| 39|:------|:------| 40|"launchType"|Ability startup mode. Set this parameter to **singleton**.| 41 42An example of the ability configuration is as follows: 43```json 44"abilities":[{ 45 "name": ".CalleeAbility", 46 "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts", 47 "launchType": "singleton", 48 "description": "$string:CalleeAbility_desc", 49 "icon": "$media:icon", 50 "label": "$string:CalleeAbility_label", 51 "visible": true 52}] 53``` 542. Import the **Ability** module. 55``` 56import Ability from '@ohos.application.Ability' 57``` 583. Define the agreed sequenceable data. 59 60 The data formats sent and received by the caller and callee must be consistent. In the following example, the data consists of numbers and strings. The sample code snippet is as follows: 61```ts 62export default class MySequenceable { 63 num: number = 0 64 str: String = "" 65 66 constructor(num, string) { 67 this.num = num 68 this.str = string 69 } 70 71 marshalling(messageParcel) { 72 messageParcel.writeInt(this.num) 73 messageParcel.writeString(this.str) 74 return true 75 } 76 77 unmarshalling(messageParcel) { 78 this.num = messageParcel.readInt() 79 this.str = messageParcel.readString() 80 return true 81 } 82} 83``` 844. Implement **Callee.on** and **Callee.off**. 85 86 The time to register a listener for the callee depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving sequenceable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The sample code snippet is as follows: 87```ts 88const TAG: string = '[CalleeAbility]' 89const MSG_SEND_METHOD: string = 'CallSendMsg' 90 91function sendMsgCallback(data) { 92 Logger.log(TAG, 'CalleeSortFunc called') 93 94 // Obtain the sequenceable data sent by the caller. 95 let receivedData = new MySequenceable(0, '') 96 data.readSequenceable(receivedData) 97 Logger.log(TAG, `receiveData[${receivedData.num}, ${receivedData.str}]`) 98 99 // Process the data. 100 // Return the sequenceable data result to the caller. 101 return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`) 102} 103 104export default class CalleeAbility extends Ability { 105 onCreate(want, launchParam) { 106 try { 107 this.callee.on(MSG_SEND_METHOD, sendMsgCallback) 108 } catch (error) { 109 Logger.error(TAG, `${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) 110 } 111 } 112 113 onDestroy() { 114 try { 115 this.callee.off(MSG_SEND_METHOD) 116 } catch (error) { 117 console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) 118 } 119 } 120} 121``` 122 123### Accessing the Callee 1241. Import the **Ability** module. 125``` 126import Ability from '@ohos.application.Ability' 127``` 1282. Obtain the caller interface. 129 130 The **context** attribute of the ability implements **startAbilityByCall** to obtain the caller interface of the ability. The following example uses **this.context** to obtain the **context** attribute of the **Ability** instance, uses **startAbilityByCall** to start the callee, obtain the caller interface, and register the **onRelease** listener of the caller. You need to implement processing based on service requirements. The sample code snippet is as follows: 131```ts 132async onButtonGetCaller() { 133 try { 134 this.caller = await context.startAbilityByCall({ 135 bundleName: 'com.samples.CallApplication', 136 abilityName: 'CalleeAbility' 137 }) 138 if (this.caller === undefined) { 139 Logger.error(TAG, 'get caller failed') 140 return 141 } 142 Logger.log(TAG, 'get caller success') 143 this.regOnRelease(this.caller) 144 } catch (error) { 145 Logger.error(TAG, `get caller failed with ${error}`) 146 } 147}.catch((error) => { 148 console.error(TAG + 'get caller failed with ' + error) 149}) 150``` 151 In the cross-device scenario, you need to specify the ID of the peer device. The sample code snippet is as follows: 152```ts 153let TAG = '[MainAbility] ' 154var caller = undefined 155let context = this.context 156 157context.startAbilityByCall({ 158 deviceId: getRemoteDeviceId(), 159 bundleName: 'com.samples.CallApplication', 160 abilityName: 'CalleeAbility' 161}).then((data) => { 162 if (data != null) { 163 caller = data 164 console.log(TAG + 'get remote caller success') 165 // Register the onRelease listener of the caller. 166 caller.onRelease((msg) => { 167 console.log(TAG + 'remote caller onRelease is called ' + msg) 168 }) 169 console.log(TAG + 'remote caller register OnRelease succeed') 170 } 171}).catch((error) => { 172 console.error(TAG + 'get remote caller failed with ' + error) 173}) 174``` 175 Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The sample code snippet is as follows: 176```ts 177import deviceManager from '@ohos.distributedHardware.deviceManager'; 178var dmClass; 179function getRemoteDeviceId() { 180 if (typeof dmClass === 'object' && dmClass != null) { 181 var list = dmClass.getTrustedDeviceListSync(); 182 if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { 183 console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null"); 184 return; 185 } 186 console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); 187 return list[0].deviceId; 188 } else { 189 console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null"); 190 } 191} 192``` 193 In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code snippet is as follows: 194```ts 195let context = this.context 196let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC'] 197context.requestPermissionsFromUser(permissions).then((data) => { 198 console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) 199}).catch((error) => { 200 console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) 201}) 202``` 2033. Send agreed sequenceable data. 204 205 The sequenceable data can be sent to the callee with or without a return value. The method and sequenceable data must be consistent with those of the callee. The following example describes how to invoke the **Call** API to send data to the callee. The sample code snippet is as follows: 206```ts 207const MSG_SEND_METHOD: string = 'CallSendMsg' 208async onButtonCall() { 209 try { 210 let msg = new MySequenceable(1, 'origin_Msg') 211 await this.caller.call(MSG_SEND_METHOD, msg) 212 } catch (error) { 213 Logger.error(TAG, `caller call failed with ${error}`) 214 } 215} 216``` 217 218 In the following, **CallWithResult** is used to send data **originMsg** to the callee and assign the data processed by the **CallSendMsg** method to **backMsg**. The sample code snippet is as follows: 219```ts 220const MSG_SEND_METHOD: string = 'CallSendMsg' 221originMsg: string = '' 222backMsg: string = '' 223async onButtonCallWithResult(originMsg, backMsg) { 224 try { 225 let msg = new MySequenceable(1, originMsg) 226 const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg) 227 Logger.log(TAG, 'caller callWithResult succeed') 228 229 let result = new MySequenceable(0, '') 230 data.readSequenceable(result) 231 backMsg(result.str) 232 Logger.log(TAG, `caller result is [${result.num}, ${result.str}]`) 233 } catch (error) { 234 Logger.error(TAG, `caller callWithResult failed with ${error}`) 235 } 236} 237``` 2384. Release the caller interface. 239 240 When the caller interface is no longer required, call the **release** API to release it. The sample code snippet is as follows: 241```ts 242try { 243 this.caller.release() 244 this.caller = undefined 245 Logger.log(TAG, 'caller release succeed') 246} catch (error) { 247 Logger.error(TAG, `caller release failed with ${error}`) 248} 249``` 250