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 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. 50 512. Access the callee ability. 52 53### Creating a Callee Ability 54For 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. 55**1. 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 63An example of the ability configuration is as follows: 64```json 65"abilities":[{ 66 "name": ".CalleeAbility", 67 "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts", 68 "launchType": "singleton", 69 "description": "$string:CalleeAbility_desc", 70 "icon": "$media:icon", 71 "label": "$string:CalleeAbility_label", 72 "visible": true 73}] 74``` 75**2. Import the Ability module.** 76```ts 77import Ability from '@ohos.app.ability.UIAbility' 78``` 79**3. Define the agreed sequenceable data.** 80 81 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: 82```ts 83export default class MySequenceable { 84 num: number = 0 85 str: string = "" 86 87 constructor(num, string) { 88 this.num = num 89 this.str = string 90 } 91 92 marshalling(messageParcel) { 93 messageParcel.writeInt(this.num) 94 messageParcel.writeString(this.str) 95 return true 96 } 97 98 unmarshalling(messageParcel) { 99 this.num = messageParcel.readInt() 100 this.str = messageParcel.readString() 101 return true 102 } 103} 104``` 105**4. Implement Callee.on and Callee.off.** 106 107 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: 108```ts 109const TAG: string = '[CalleeAbility]' 110const MSG_SEND_METHOD: string = 'CallSendMsg' 111 112function sendMsgCallback(data) { 113 console.log('CalleeSortFunc called') 114 115 // Obtain the sequenceable data sent by the caller ability. 116 let receivedData = new MySequenceable(0, '') 117 data.readSequenceable(receivedData) 118 console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`) 119 120 // Process the data. 121 // Return the sequenceable data result to the caller ability. 122 return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`) 123} 124 125export default class CalleeAbility extends Ability { 126 onCreate(want, launchParam) { 127 try { 128 this.callee.on(MSG_SEND_METHOD, sendMsgCallback) 129 } catch (error) { 130 console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) 131 } 132 } 133 134 onDestroy() { 135 try { 136 this.callee.off(MSG_SEND_METHOD) 137 } catch (error) { 138 console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) 139 } 140 } 141} 142``` 143 144### Accessing the Callee Ability 145**1. Import the Ability module.** 146```ts 147import Ability from '@ohos.app.ability.UIAbility' 148``` 149**2. Obtain the Caller object.** 150 151 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: 152```ts 153// Register the onRelease listener of the caller ability. 154private regOnRelease(caller) { 155 try { 156 caller.on("release", (msg) => { 157 console.log(`caller onRelease is called ${msg}`) 158 }) 159 console.log('caller register OnRelease succeed') 160 } catch (error) { 161 console.log(`caller register OnRelease failed with ${error}`) 162 } 163} 164 165async onButtonGetCaller() { 166 try { 167 this.caller = await context.startAbilityByCall({ 168 bundleName: 'com.samples.CallApplication', 169 abilityName: 'CalleeAbility' 170 }) 171 if (this.caller === undefined) { 172 console.log('get caller failed') 173 return 174 } 175 console.log('get caller success') 176 this.regOnRelease(this.caller) 177 } catch (error) { 178 console.log(`get caller failed with ${error}`) 179 } 180} 181``` 182 In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows: 183```ts 184async onButtonGetRemoteCaller() { 185 var caller = undefined 186 var context = this.context 187 188 context.startAbilityByCall({ 189 deviceId: getRemoteDeviceId(), 190 bundleName: 'com.samples.CallApplication', 191 abilityName: 'CalleeAbility' 192 }).then((data) => { 193 if (data != null) { 194 caller = data 195 console.log('get remote caller success') 196 // Register the onRelease listener of the caller ability. 197 caller.on("release", (msg) => { 198 console.log(`remote caller onRelease is called ${msg}`) 199 }) 200 console.log('remote caller register OnRelease succeed') 201 } 202 }).catch((error) => { 203 console.error(`get remote caller failed with ${error}`) 204 }) 205} 206``` 207 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: 208```ts 209import deviceManager from '@ohos.distributedHardware.deviceManager'; 210var dmClass; 211function getRemoteDeviceId() { 212 if (typeof dmClass === 'object' && dmClass != null) { 213 var list = dmClass.getTrustedDeviceListSync() 214 if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { 215 console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null") 216 return 217 } 218 console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId) 219 return list[0].deviceId 220 } else { 221 console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null") 222 } 223} 224``` 225 In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows: 226```ts 227import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts'; 228 229requestPermission() { 230 let context = this.context 231 let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC'] 232 let atManager = abilityAccessCtrl.createAtManager(); 233 atManager.requestPermissionsFromUser(context, permissions).then((data) => { 234 console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) 235 }).catch((error) => { 236 console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) 237 }) 238} 239``` 240**3. Send agreed sequenceable data.** 241 242 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: 243```ts 244const MSG_SEND_METHOD: string = 'CallSendMsg' 245async onButtonCall() { 246 try { 247 let msg = new MySequenceable(1, 'origin_Msg') 248 await this.caller.call(MSG_SEND_METHOD, msg) 249 } catch (error) { 250 console.log(`caller call failed with ${error}`) 251 } 252} 253``` 254 255 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: 256```ts 257const MSG_SEND_METHOD: string = 'CallSendMsg' 258originMsg: string = '' 259backMsg: string = '' 260async onButtonCallWithResult(originMsg, backMsg) { 261 try { 262 let msg = new MySequenceable(1, originMsg) 263 const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg) 264 console.log('caller callWithResult succeed') 265 266 let result = new MySequenceable(0, '') 267 data.readSequenceable(result) 268 backMsg(result.str) 269 console.log(`caller result is [${result.num}, ${result.str}]`) 270 } catch (error) { 271 console.log(`caller callWithResult failed with ${error}`) 272 } 273} 274``` 275**4. Release the Caller object.** 276 277 When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows: 278```ts 279releaseCall() { 280 try { 281 this.caller.release() 282 this.caller = undefined 283 console.log('caller release succeed') 284 } catch (error) { 285 console.log(`caller release failed with ${error}`) 286 } 287} 288``` 289