1# Service Ability Development 2 3## When to Use 4A Service ability is used to run tasks in the background, such as playing music or downloading files. It does not provide a UI for user interaction. Service abilities can be started by other applications or abilities and can remain running in the background even after the user switches to another application. 5 6## Available APIs 7 8**Table 1** Service ability lifecycle APIs 9|API|Description| 10|:------|:------| 11|onStart?(): void|Called to initialize a Service ability being created. This callback is invoked only once in the entire lifecycle of a Service ability. The **Want** object passed to this callback must be null.| 12|onCommand?(want: Want, startId: number): void|Called every time a Service ability is created on a client. You can collect calling statistics and perform initialization operations in this callback.| 13|onConnect?(want: Want): rpc.RemoteObject|Called when another ability is connected to the Service ability.| 14|onDisconnect?(want: Want): void|Called when another ability is disconnected from the Service ability.| 15|onStop?(): void|Called when the Service ability is being destroyed. You should override this callback for your Service ability to clear its resources, such as threads and registered listeners.| 16 17## How to Develop 18 19### Creating a Service Ability 20 211. Create a child class of the **Ability** class and override the following Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests: 22 23 ```javascript 24 export default { 25 onStart() { 26 console.log('ServiceAbility onStart'); 27 }, 28 onCommand(want, startId) { 29 console.log('ServiceAbility onCommand'); 30 }, 31 onConnect(want) { 32 console.log('ServiceAbility OnConnect'); 33 return null; 34 }, 35 onDisconnect(want) { 36 console.log('ServiceAbility OnDisConnect'); 37 }, 38 onStop() { 39 console.log('ServiceAbility onStop'); 40 }, 41 } 42 ``` 43 442. Register a Service ability. 45 46 Declare the Service ability in the **config.json** file by setting its **type** attribute to **service**. 47 48 ```javascript 49 { 50 "module": { 51 "abilities": [ 52 { 53 "name": ".ServiceAbility", 54 "type": "service", 55 "visible": true 56 ... 57 } 58 ] 59 ... 60 } 61 ... 62 } 63 ``` 64 65 66 67### Starting a Service Ability 68 69The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object. 70 71To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified. The meanings of the parameters are as follows: 72 73- **bundleName** indicates the name of the bundle to which the target ability belongs. 74- **abilityName** indicates the target ability name. 75 76The following code snippet shows how to start a Service ability running on the local device: 77 78```javascript 79import featureAbility from '@ohos.ability.featureAbility'; 80let promise = featureAbility.startAbility( 81 { 82 want: 83 { 84 bundleName: "com.jstest.service", 85 abilityName: "com.jstest.service.ServiceAbility", 86 }, 87 } 88); 89``` 90 91After the preceding code is executed, the **startAbility()** API is called to start the Service ability. 92- If the Service ability is not running, the system calls **onStart()** to initialize the Service ability, and then calls **onCommand()** on the Service ability. 93- If the Service ability is running, the system directly calls **onCommand()** on the Service ability. 94 95 96 97### Stopping a Service Ability 98 99Once created, the Service ability keeps running in the background. The system does not stop or destroy it unless memory resources must be reclaimed. You can call **terminateSelf()** on a Service ability to stop it. 100 101 102 103### Connecting to a Local Service Ability 104 105If you need to connect a Service ability to a Page ability or to a Service ability in another application, you must first implement the **IAbilityConnection** API for the connection. A Service ability allows other abilities to connect to it through **connectAbility()**. 106 107When calling **connectAbility()**, you should pass a **Want** object containing information about the target Service ability and an **IAbilityConnection** object to the API. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a Service ability is connected, **onDisconnect()** is invoked when a Service ability is unexpectedly disconnected, and **onFailed()** is invoked when a connection to a Service ability fails. 108 109The following code snippet shows how to implement the callbacks: 110 111```javascript 112import prompt from '@system.prompt' 113 114let mRemote; 115function onConnectCallback(element, remote){ 116 console.log('onConnectLocalService onConnectDone element: ' + element); 117 console.log('onConnectLocalService onConnectDone remote: ' + remote); 118 mRemote = remote; 119 if (mRemote == null) { 120 prompt.showToast({ 121 message: "onConnectLocalService not connected yet" 122 }); 123 return; 124 } 125 let option = new rpc.MessageOption(); 126 let data = new rpc.MessageParcel(); 127 let reply = new rpc.MessageParcel(); 128 data.writeInt(1); 129 data.writeInt(99); 130 mRemote.sendRequest(1, data, reply, option).then((result) => { 131 console.log('sendRequest success'); 132 let msg = reply.readInt(); 133 prompt.showToast({ 134 message: "onConnectLocalService connect result: " + msg, 135 duration: 3000 136 }); 137 }).catch((e) => { 138 console.log('sendRequest error:' + e); 139 }); 140 141} 142 143function onDisconnectCallback(element){ 144 console.log('ConnectAbility onDisconnect Callback') 145} 146 147function onFailedCallback(code){ 148 console.log('ConnectAbility onFailed Callback') 149} 150``` 151 152The following code snippet shows how to connect to a local Service ability: 153 154```javascript 155import featureAbility from '@ohos.ability.featureAbility'; 156let connId = featureAbility.connectAbility( 157 { 158 bundleName: "com.jstest.service", 159 abilityName: "com.jstest.service.ServiceAbility", 160 }, 161 { 162 onConnect: onConnectCallback, 163 onDisconnect: onDisconnectCallback, 164 onFailed: onFailedCallback, 165 }, 166); 167``` 168 169When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides a default implementation of **IRemoteObject**. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**. 170 171The following code snippet shows how the Service ability instance returns itself to the calling ability: 172 173```javascript 174import rpc from "@ohos.rpc"; 175 176let mMyStub; 177export default { 178 onStart() { 179 class MyStub extends rpc.RemoteObject{ 180 constructor(des) { 181 if (typeof des === 'string') { 182 super(des); 183 } 184 return null; 185 } 186 onRemoteRequest(code, data, reply, option) { 187 console.log("ServiceAbility onRemoteRequest called"); 188 if (code === 1) { 189 let op1 = data.readInt(); 190 let op2 = data.readInt(); 191 console.log("op1 = " + op1 + ", op2 = " + op2); 192 reply.writeInt(op1 + op2); 193 } else { 194 console.log("ServiceAbility unknown request code"); 195 } 196 return true; 197 } 198 } 199 mMyStub = new MyStub("ServiceAbility-test"); 200 }, 201 onCommand(want, startId) { 202 console.log('ServiceAbility onCommand'); 203 }, 204 onConnect(want) { 205 console.log('ServiceAbility OnConnect'); 206 return mMyStub; 207 }, 208 onDisconnect(want) { 209 console.log('ServiceAbility OnDisConnect'); 210 }, 211 onStop() { 212 console.log('ServiceAbility onStop'); 213 }, 214} 215``` 216 217### Connecting to a Remote Service Ability 218>**NOTE** 219> 220>This feature applies only to system applications, since the **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications. 221 222If you need to connect a Service ability to a Page ability or another Service ability on a remote device, you must first implement the **IAbilityConnection** interface for the connection. A Service ability allows abilities on another device to connect to it through **connectAbility()**. 223 224When calling **connectAbility()**, you should pass a **Want** object containing information about the target Service ability and an **IAbilityConnection** object to the API. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a Service ability is connected, **onDisconnect()** is invoked when a Service ability is unexpectedly disconnected, and **onFailed()** is invoked when a connection to a Service ability fails. 225 226The following code snippet shows how to implement the callbacks: 227 228```ts 229import prompt from '@system.prompt' 230 231let mRemote; 232function onConnectCallback(element, remote){ 233 console.log('onConnectRemoteService onConnectDone element: ' + element); 234 console.log('onConnectRemoteService onConnectDone remote: ' + remote); 235 mRemote = remote; 236 if (mRemote == null) { 237 prompt.showToast({ 238 message: "onConnectRemoteService not connected yet" 239 }); 240 return; 241 } 242 let option = new rpc.MessageOption(); 243 let data = new rpc.MessageParcel(); 244 let reply = new rpc.MessageParcel(); 245 data.writeInt(1); 246 data.writeInt(99); 247 mRemote.sendRequest(1, data, reply, option).then((result) => { 248 console.log('sendRequest success'); 249 let msg = reply.readInt(); 250 prompt.showToast({ 251 message: "onConnectRemoteService connect result: " + msg, 252 duration: 3000 253 }); 254 }).catch((e) => { 255 console.log('sendRequest error:' + e); 256 }); 257} 258 259function onDisconnectCallback(element){ 260 console.log('ConnectRemoteAbility onDisconnect Callback') 261} 262 263function onFailedCallback(code){ 264 console.log('ConnectRemoteAbility onFailed Callback') 265} 266``` 267 268The **Want** of the target Service ability must contain the remote **deviceId**, which can be obtained from **DeviceManager**. The sample code is as follows: 269 270```ts 271import deviceManager from '@ohos.distributedHardware.deviceManager'; 272 273// For details about the implementation of dmClass, see the implementation in Distributed Demo in Samples. 274let dmClass; 275 276function getRemoteDeviceId() { 277 if (typeof dmClass === 'object' && dmClass != null) { 278 let list = dmClass.getTrustedDeviceListSync(); 279 if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { 280 console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null"); 281 return; 282 } 283 console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); 284 return list[0].deviceId; 285 } else { 286 console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null"); 287 } 288} 289``` 290 291The following code snippet shows how to connect to a remote Service ability: 292 293```ts 294import featureAbility from '@ohos.ability.featureAbility'; 295let connId = featureAbility.connectAbility( 296 { 297 deviceId: getRemoteDeviceId(), 298 bundleName: "ohos.samples.etsDemo", 299 abilityName: "ohos.samples.etsDemo.ServiceAbility", 300 }, 301 { 302 onConnect: onConnectCallback, 303 onDisconnect: onDisconnectCallback, 304 onFailed: onFailedCallback, 305 }, 306); 307``` 308In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows: 309 310```ts 311import abilityAccessCtrl from "@ohos.abilityAccessCtrl"; 312import bundle from '@ohos.bundle'; 313async function RequestPermission() { 314 console.info('RequestPermission begin'); 315 let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"]; 316 let bundleFlag = 0; 317 let tokenID = undefined; 318 let userID = 100; 319 let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID); 320 tokenID = appInfo.accessTokenId; 321 let atManager = abilityAccessCtrl.createAtManager(); 322 let requestPermissions: Array<string> = []; 323 for (let i = 0;i < array.length; i++) { 324 let result = await atManager.verifyAccessToken(tokenID, array[i]); 325 console.info("verifyAccessToken result:" + JSON.stringify(result)); 326 if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 327 } else { 328 requestPermissions.push(array[i]); 329 } 330 } 331 console.info("requestPermissions:" + JSON.stringify(requestPermissions)); 332 if (requestPermissions.length == 0 || requestPermissions == []) { 333 return; 334 } 335 let context = featureAbility.getContext(); 336 context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{ 337 console.info("data:" + JSON.stringify(data)); 338 }); 339 console.info('RequestPermission end'); 340} 341``` 342 343When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides a default implementation of the **IRemoteObject** interface. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**. 344 345The following code snippet shows how the Service ability instance returns itself to the calling ability: 346 347```ts 348import rpc from "@ohos.rpc"; 349 350class FirstServiceAbilityStub extends rpc.RemoteObject{ 351 constructor(des) { 352 if (typeof des === 'string') { 353 super(des); 354 } else { 355 return null; 356 } 357 } 358 onRemoteRequest(code, data, reply, option) { 359 console.log("ServiceAbility onRemoteRequest called"); 360 if (code === 1) { 361 let op1 = data.readInt(); 362 let op2 = data.readInt(); 363 console.log("op1 = " + op1 + ", op2 = " + op2); 364 reply.writeInt(op1 + op2); 365 } else { 366 console.log("ServiceAbility unknown request code"); 367 } 368 return true; 369 } 370} 371 372export default { 373 onStart() { 374 console.info('ServiceAbility onStart'); 375 }, 376 onStop() { 377 console.info('ServiceAbility onStop'); 378 }, 379 onConnect(want) { 380 console.log("ServiceAbility onConnect"); 381 try { 382 let value = JSON.stringify(want); 383 console.log("ServiceAbility want:" + value); 384 } catch(error) { 385 console.log("ServiceAbility error:" + error); 386 } 387 return new FirstServiceAbilityStub("first ts service stub"); 388 }, 389 onDisconnect(want) { 390 console.log("ServiceAbility onDisconnect"); 391 let value = JSON.stringify(want); 392 console.log("ServiceAbility want:" + value); 393 }, 394 onCommand(want, startId) { 395 console.info('ServiceAbility onCommand'); 396 let value = JSON.stringify(want); 397 console.log("ServiceAbility want:" + value); 398 console.log("ServiceAbility startId:" + startId); 399 } 400}; 401``` 402