1# Ability Call Development 2## When to Use 3Ability call is an extension of the ability capability. It enables an ability to be invoked by and communicate with external systems. The ability invoked can be either started in the foreground or created and run in the background. You can use the ability call to implement data sharing between two abilities (caller ability and callee ability) through inter-process communication (IPC). 4 5The core API used for the ability call is **startAbilityByCall**, which differs from **startAbility** in the following ways: 6 - **startAbilityByCall** supports ability startup in the foreground and background, whereas **startAbility** supports ability startup in the foreground only. 7 - The caller ability can use the **Caller** object returned by **startAbilityByCall** to communicate with the callee ability, but **startAbility** does not provide the communication capability. 8 9Ability call is usually used in the following scenarios: 10- Communicating with the callee ability 11- Starting the callee ability in the background 12 13**Table 1** Terms used in the ability call 14|Term|Description| 15|:------|:------| 16|Caller ability|Ability that triggers the ability call.| 17|Callee ability|Ability invoked by the ability call.| 18|Caller |Object returned by **startAbilityByCall** and used by the caller ability to communicate with the callee ability.| 19|Callee |Object held by the callee ability to communicate with the caller ability.| 20|IPC |Inter-process communication.| 21 22The ability call process is as follows: 23 - The caller ability uses **startAbilityByCall** to obtain a **Caller** object and uses **call()** of the **Caller** object to send data to the callee ability. 24 - The callee ability, which holds a **Callee** object, uses **on()** of the **Callee** object to register a callback. This callback is invoked when the callee ability receives data from the caller ability. 25![stage-call](figures/stage-call.png) 26 27> **NOTE** 28> 29> The launch type of the callee ability must be **singleton**. 30> 31> Currently, only system applications can use the ability call. 32 33## Available APIs 34The table below describes the ability call APIs. For details, see [Ability](../reference/apis/js-apis-application-ability.md#caller). 35 36**Table 2** Ability call APIs 37|API|Description| 38|:------|:------| 39|startAbilityByCall(want: Want): Promise\<Caller>|Starts an ability in the foreground (through the **want** configuration) or background (default) and obtains the **Caller** object for communication with the ability. For details, see [AbilityContext](../reference/apis/js-apis-ability-context.md#abilitycontextstartabilitybycall) or **ServiceExtensionContext**.| 40|on(method: string, callback: CalleeCallBack): void|Callback invoked when the callee ability registers a method.| 41|off(method: string): void|Callback invoked when the callee ability deregisters a method.| 42|call(method: string, data: rpc.Sequenceable): Promise\<void>|Sends agreed sequenceable data to the callee ability.| 43|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|Sends agreed sequenceable data to the callee ability and obtains the agreed sequenceable data returned by the callee ability.| 44|release(): void|Releases the **Caller** object.| 45|on(type: "release", callback: OnReleaseCallback): void|Callback invoked when the **Caller** object is released.| 46 47## How to Develop 48The procedure for developing the ability call is as follows: 491. Create a callee ability. 502. Access the callee ability. 51 52### Creating a Callee Ability 53For the callee ability, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. 54 551. **Configure the ability launch type.** 56 57 Set **launchType** of the callee ability to **singleton** in the **module.json5** file. 58 59 |JSON Field|Description| 60 |:------|:------| 61 |"launchType"|Ability launch type. Set this parameter to **singleton**.| 62 63 An example of the ability configuration is as follows: 64 65 ```json 66 "abilities":[{ 67 "name": ".CalleeAbility", 68 "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts", 69 "launchType": "singleton", 70 "description": "$string:CalleeAbility_desc", 71 "icon": "$media:icon", 72 "label": "$string:CalleeAbility_label", 73 "exported": true 74 }] 75 ``` 76 772. **Import the ability module.** 78 79 ```ts 80 import Ability from '@ohos.app.ability.UIAbility'; 81 ``` 82 833. **Define the agreed sequenceable data.** 84 85 The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. The code snippet is as follows: 86 87 ```ts 88 export default class MySequenceable { 89 num: number = 0 90 str: string = "" 91 92 constructor(num, string) { 93 this.num = num 94 this.str = string 95 } 96 97 marshalling(messageParcel) { 98 messageParcel.writeInt(this.num) 99 messageParcel.writeString(this.str) 100 return true 101 } 102 103 unmarshalling(messageParcel) { 104 this.num = messageParcel.readInt() 105 this.str = messageParcel.readString() 106 return true 107 } 108 } 109 ``` 110 1114. **Implement Callee.on and Callee.off.** 112 113 The time to register a listener for the callee ability 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 code snippet is as follows: 114 115 ```ts 116 const TAG: string = '[CalleeAbility]' 117 const MSG_SEND_METHOD: string = 'CallSendMsg' 118 119 function sendMsgCallback(data) { 120 console.log('CalleeSortFunc called') 121 122 // Obtain the sequenceable data sent by the caller ability. 123 let receivedData = new MySequenceable(0, '') 124 data.readSequenceable(receivedData) 125 console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`) 126 127 // Process the data. 128 // Return the sequenceable data result to the caller ability. 129 return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`) 130 } 131 132 export default class CalleeAbility extends Ability { 133 onCreate(want, launchParam) { 134 try { 135 this.callee.on(MSG_SEND_METHOD, sendMsgCallback) 136 } catch (error) { 137 console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) 138 } 139 } 140 141 onDestroy() { 142 try { 143 this.callee.off(MSG_SEND_METHOD) 144 } catch (error) { 145 console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) 146 } 147 } 148 } 149 ``` 150 151### Accessing the Callee Ability 1521. **Import the Ability module.** 153 154 ```ts 155 import Ability from '@ohos.app.ability.UIAbility'; 156 ``` 157 1582. **Obtain the Caller object.** 159 160 The **context** attribute of the ability implements **startAbilityByCall** to obtain the **Caller** object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the **Caller** object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. The code snippet is as follows: 161 162 ```ts 163 // Register the onRelease listener of the caller ability. 164 private regOnRelease(caller) { 165 try { 166 caller.on("release", (msg) => { 167 console.log(`caller onRelease is called ${msg}`) 168 }) 169 console.log('caller register OnRelease succeed') 170 } catch (error) { 171 console.log(`caller register OnRelease failed with ${error}`) 172 } 173 } 174 175 async onButtonGetCaller() { 176 try { 177 this.caller = await context.startAbilityByCall({ 178 bundleName: 'com.samples.CallApplication', 179 abilityName: 'CalleeAbility' 180 }) 181 if (this.caller === undefined) { 182 console.log('get caller failed') 183 return 184 } 185 console.log('get caller success') 186 this.regOnRelease(this.caller) 187 } catch (error) { 188 console.log(`get caller failed with ${error}`) 189 } 190 } 191 ``` 192 193 In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows: 194 195 ```ts 196 async onButtonGetRemoteCaller() { 197 var caller = undefined 198 var context = this.context 199 200 context.startAbilityByCall({ 201 deviceId: getRemoteDeviceId(), 202 bundleName: 'com.samples.CallApplication', 203 abilityName: 'CalleeAbility' 204 }).then((data) => { 205 if (data != null) { 206 caller = data 207 console.log('get remote caller success') 208 // Register the onRelease listener of the caller ability. 209 caller.on("release", (msg) => { 210 console.log(`remote caller onRelease is called ${msg}`) 211 }) 212 console.log('remote caller register OnRelease succeed') 213 } 214 }).catch((error) => { 215 console.error(`get remote caller failed with ${error}`) 216 }) 217 } 218 ``` 219 220 Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The code snippet is as follows: 221 222 ```ts 223 import deviceManager from '@ohos.distributedHardware.deviceManager'; 224 var dmClass; 225 function getRemoteDeviceId() { 226 if (typeof dmClass === 'object' && dmClass != null) { 227 var list = dmClass.getTrustedDeviceListSync() 228 if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { 229 console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null") 230 return 231 } 232 console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId) 233 return list[0].deviceId 234 } else { 235 console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null") 236 } 237 } 238 ``` 239 240 In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows: 241 242 ```ts 243 import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts'; 244 245 requestPermission() { 246 let context = this.context 247 let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC'] 248 let atManager = abilityAccessCtrl.createAtManager(); 249 atManager.requestPermissionsFromUser(context, permissions).then((data) => { 250 console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) 251 }).catch((error) => { 252 console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) 253 }) 254 } 255 ``` 256 2573. **Send agreed sequenceable data.** 258 259 The sequenceable data can be sent to the callee ability with or without a return value. The method and sequenceable data must be consistent with those of the callee ability. The following example describes how to send data to the callee ability. The code snippet is as follows: 260 261 ```ts 262 const MSG_SEND_METHOD: string = 'CallSendMsg' 263 async onButtonCall() { 264 try { 265 let msg = new MySequenceable(1, 'origin_Msg') 266 await this.caller.call(MSG_SEND_METHOD, msg) 267 } catch (error) { 268 console.log(`caller call failed with ${error}`) 269 } 270 } 271 ``` 272 273 In the following, **CallWithResult** is used to send data **originMsg** to the callee ability and assign the data processed by the **CallSendMsg** method to **backMsg**. The code snippet is as follows: 274 275 ```ts 276 const MSG_SEND_METHOD: string = 'CallSendMsg' 277 originMsg: string = '' 278 backMsg: string = '' 279 async onButtonCallWithResult(originMsg, backMsg) { 280 try { 281 let msg = new MySequenceable(1, originMsg) 282 const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg) 283 console.log('caller callWithResult succeed') 284 285 let result = new MySequenceable(0, '') 286 data.readSequenceable(result) 287 backMsg(result.str) 288 console.log(`caller result is [${result.num}, ${result.str}]`) 289 } catch (error) { 290 console.log(`caller callWithResult failed with ${error}`) 291 } 292 } 293 ``` 294 2954. **Release the Caller object.** 296 297 When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows: 298 299 ```ts 300 releaseCall() { 301 try { 302 this.caller.release() 303 this.caller = undefined 304 console.log('caller release succeed') 305 } catch (error) { 306 console.log(`caller release failed with ${error}`) 307 } 308 } 309 ```