1# UIAbility Connection Development 2 3 4## Introduction 5 6Cross-device connection management allows for mutual capability assistance between devices that form a Super Device through a distributed OS, providing users with a more efficient, immersive experience compared to that of a single device. <!--Del-->For example, a camera application of the watch can start a camera function of the mobile phone to implement real-time image preview and remote photographing.<!--DelEnd--> 7 8 9### Available Capabilities 10 11- Cross-device application startup: uses the application on the local device to start the same application on another device and perform collaborative operations. 12- Data interaction: implements cross-device transmission of data.<!--Del-->Such data includes text messages, byte streams, images, and transport streams (text interaction supported only for third-party applications).<!--DelEnd--> 13 14 15### Typical Use Cases 16 17The transport stream feature allows users to start the peer camera from the local camera to access capabilities such as text-based interaction<!--Del-->, camera preview, photo reception, and remote camera shutter<!--DelEnd-->. 18 19 20### Basic Concepts 21 22Before you get started, familiarize yourself with the following concepts: 23 24- **Distributed Management Service (DMS)** 25 26 A framework that provides distributed component management capabilities. 27 28- **UIAbility** 29 30 A component that implements tasks specific to application UIs, such as lifecycle management, user interaction, and UI rendering. 31 32- **Extension** 33 34 A component that extends application functions or implements cross-device collaboration. It allows applications to run some tasks in the background or migrates some functions to other devices for execution, implementing distributed capabilities. 35 36<!--Del--> 37- **Byte stream** 38 39 Data of the [ArrayBuffer](../arkts-utils/arraybuffer-object.md) type, which can be used to store binary data, for example, image or audio data. 40 41- **Transport stream** 42 43 Media streams that can be used to transmit images and video streams. 44<!--DelEnd--> 45### Implementation Principles 46 47Cross-device connection management is built on a distributed component management framework. It implements JS object encapsulation on the distributed component management framework and establishes sessions between applications through this framework to perform cross-device collaboration. The data-based interaction capabilities are provided by the system. 48 49**Figure 1** Cross-device connection mechanism 50 51 52 53 54### Constraints 55 56- You need to log in with the same HUAWEI ID on different devices. 57 58- Cross-device collaboration is supported only for UIAbility applications with the same bundle name on different devices. 59<!--Del--> 60- The byte stream, image, and transport stream capabilities are supported only for system applications. 61<!--DelEnd--> 62- After the service collaboration is complete, the collaboration status must be ended in a timely manner. If an application does not apply for a continuous task, the collaboration lifecycle will be ended when the screen is locked or the application is switched to the background for more than 5 seconds. 63 64- The distributed component management framework does not censor the transmitted content during the collaboration process. If privacy data is involved, it is recommended that the application employs measures such as data encryption and pop-up notification to enhance information security. 65 66 67## Environment Setup 68 69### Environment Requirements 70 71You have logged in to devices A and B with the same HUAWEI ID and the two devices are successfully networked through Bluetooth. 72 73 74### Setting Up the Environment 75 761. Download and install DevEco Studio on the PC. For details, see [Downloading Software](https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V5/ide-software-download-V5) and [Installing DevEco Studio](https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V5/ide-software-install-V5). The DevEco Studio version must be 4.1 or later. 772. Update the public-SDK to API 18 or later. For details about how to update the SDK, see [OpenHarmony SDK Upgrade Assistant]( ../tools/openharmony_sdk_upgrade_assistant.md). 783. Connect device A and device B to the PC using USB cables. 794. Enable Bluetooth on device A and device B to implement networking. 80 81 82### Verifying the Environment 83 84Run the following shell command on the PC: 85 86```shell 87hdc shell 88hidumper -s 4700 -a "buscenter -l remote_device_info" 89``` 90 91If the networking is successful, the number of networking devices is displayed, for example, **remote device num = 1**. 92 93 94## How to Develop 95 96Cross-device connection management allows for mutual capability assistance between devices that form a Super Device through a distributed OS. 97 98 99### Available APIs 100 101The following table describes the APIs for cross-device connection management. For details, see [abilityConnectionManager](../reference/apis-distributedservice-kit/js-apis-distributed-abilityConnectionManager.md). 102 103**Table 1** Available APIs 104 105| API| Description| 106| -------- | -------- | 107| createAbilityConnectionSession(serverId: string, context: Context, peerInfo: peerInfo, connectOpt: ConnectOption): number; | Creates a session between applications.| 108| destroyAbilityConnectionSession(sessionId: number): void; | Destroys a session between applications.| 109| connect(sessionId: number): Promise<ConnectResult>; | Connects to the ability on the source side.| 110| acceptConnect(sessionId: number, token: string): Promise<void>; | Connects to the ability on the sink side.| 111| disconnect(sessionId: number): void; | Disconnects the ability connection.| 112| on(type: 'connect' \| 'disconnect' \| 'receiveMessage' \| 'receiveData' \| 'receiveImage', sessionId: number, callback: Callback<EventCallbackInfo>): void | Enable listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, **receiveData**, and **receiveImage **<!--DelEnd-->events.| 113| off(type: 'connect' \| 'disconnect' \| 'receiveMessage' \| 'receiveData' \| 'receiveImage', 'connect', sessionId: number, callback?: Callback<EventCallbackInfo>): void | Cancels listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, **receiveData**, and **receiveImage **<!--DelEnd-->events.| 114| sendMessage(sessionId: number, msg: string): Promise<void>; | Sends a text message.| 115|<!--DelRow--> sendData(sessionId: number, data: ArrayBuffer): Promise<void>; | Sends byte streams (supported only for system applications).| 116|<!--DelRow--> sendImage(sessionId: number, image: image.PixelMap): Promise<void>; | Sends an image (supported only for system applications).| 117|<!--DelRow--> createStream(sessionId: number, param: StreamParam): Promise<number>; | Creates transport streams (supported only for system applications).| 118|<!--DelRow--> destroyStream(sessionId: number): void; | Destroys transport streams (supported only for system applications).| 119 120 121### Development Procedure 122 123The application on device A starts and connects to the application on device B through the cross-device application management module. After the connection is successful, the applications on device A and device B register a callback listener for corresponding events through the **on** interface. The application on device A or device B calls **sendMessage**<!--Del-->, **sendData**, **sendImage**, or **createStream**<!--DelEnd--> to send text messages<!--Del-->, byte streams, or transport streams<!--DelEnd-->. The peer end performs subsequent service coordination based on the received callback. 124 125#### Importing the AbilityConnectionManager Module File 126 127 ```ts 128 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 129 ``` 130 131 132#### Discovering a Device 133 134The application on device A needs to discover device B and use its **netWorkId** as the input parameter of the collaboration API. You can call APIs of the distributed device management module to discover and select the peer device. For details, see [Distributed Device Management Development](devicemanager-guidelines.md). 135 136 137#### Initiating a Session Between Applications 138 139During session establishment, the applications on device A and device B perform different operations. In the subsequent development procedure, the application on device A serves as the connection initiator, while the application on device B serves as the connection receiver. 140 141##### Device A 142 143The application calls **createAbilityConnectionSession()** to create a session and obtain the session ID. Then, it calls **connect()** to start the ability session connection. Now, the application on device B is started. 144 145 ```ts 146 import { abilityConnectionManager, distributedDeviceManager } from '@kit.DistributedServiceKit'; 147 import { common } from '@kit.AbilityKit'; 148 import { hilog } from '@kit.PerformanceAnalysisKit'; 149 150 let dmClass: distributedDeviceManager.DeviceManager; 151 152 function initDmClass(): void { 153 try { 154 dmClass = distributedDeviceManager.createDeviceManager('com.example.remotephotodemo'); 155 } catch (err) { 156 hilog.error(0x0000, 'testTag', 'createDeviceManager err: ' + JSON.stringify(err)); 157 } 158 } 159 // Obtain the ID of device B. 160 function getRemoteDeviceId(): string | undefined { 161 initDmClass(); 162 if (typeof dmClass === 'object' && dmClass !== null) { 163 hilog.info(0x0000, 'testTag', 'getRemoteDeviceId begin'); 164 let list = dmClass.getAvailableDeviceListSync(); 165 if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 166 hilog.info(0x0000, 'testTag', 'getRemoteDeviceId err: list is null'); 167 return; 168 } 169 if (list.length === 0) { 170 hilog.info(0x0000, 'testTag', 'getRemoteDeviceId err: list is empty'); 171 return; 172 } 173 return list[0].networkId; 174 } else { 175 hilog.info(0x0000, 'testTag', 'getRemoteDeviceId err: dmClass is null'); 176 return; 177 } 178 } 179 // Define the collaboration information of device B. 180 const peerInfo: abilityConnectionManager.PeerInfo = { 181 deviceId: getRemoteDeviceId(), 182 bundleName: 'com.example.remotephotodemo', 183 moduleName: 'entry', 184 abilityName: 'EntryAbility', 185 serverId: 'collabTest' 186 }; 187 const myRecord: Record<string, string> = { 188 "newKey1": "value1", 189 }; 190 191 const options: Record<string, string> = { 192 'ohos.collabrate.key.start.option': 'ohos.collabrate.value.foreground', 193 }; 194 // Define connection options. 195 const connectOption: abilityConnectionManager.ConnectOption = { 196 needSendBigData: true, 197 needSendStream: false, 198 needReceiveStream: true, 199 options: options, 200 parameters: myRecord 201 }; 202 let context = getContext(this) as common.UIAbilityContext; 203 try { 204 this.sessionId = abilityConnectionManager.createAbilityConnectionSession("collabTest", context, peerInfo, connectOption); 205 hilog.info(0x0000, 'testTag', 'createSession sessionId is', this.sessionId); 206 207 abilityConnectionManager.connect(this.sessionId).then((ConnectResult) => { 208 if (!ConnectResult.isConnected) { 209 hilog.info(0x0000, 'testTag', 'connect failed'); 210 return; 211 } 212 }).catch(() => { 213 hilog.error(0x0000, 'testTag', "connect failed"); 214 }) 215 216 } catch (error) { 217 hilog.error(0x0000, 'testTag', error); 218 } 219 ``` 220 221##### Device B 222 223After the application on device A calls **connect()**, the application on device B is started in collaboration mode, and the collaboration lifecycle function **onCollaborate()** is triggered. You can configure the **createAbilityConnectionSession()** and **acceptConnect()** calls in this API. 224 225 ```ts 226 import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 227 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 228 import { hilog } from '@kit.PerformanceAnalysisKit'; 229 230 export default class EntryAbility extends UIAbility { 231 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 232 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 233 } 234 235 onCollaborate(wantParam: Record<string, Object>): AbilityConstant.OnCollaborateResult { 236 hilog.info(0x0000, 'testTag', '%{public}s', 'on collaborate'); 237 let param = wantParam["ohos.extra.param.key.supportCollaborateIndex"] as Record<string, Object> 238 this.onCollab(param); 239 return 0; 240 } 241 242 onCollab(collabParam: Record<string, Object>) { 243 const sessionId = this.createSessionFromWant(collabParam); 244 if (sessionId == -1) { 245 hilog.info(0x0000, 'testTag', 'Invalid session ID.'); 246 return; 247 } 248 const collabToken = collabParam["ohos.dms.collabToken"] as string; 249 abilityConnectionManager.acceptConnect(sessionId, collabToken).then(() => { 250 hilog.info(0x0000, 'testTag', 'acceptConnect success'); 251 }).catch(() => { 252 hilog.error("failed"); 253 }) 254 } 255 256 createSessionFromWant(collabParam: Record<string, Object>): number { 257 let sessionId = -1; 258 const peerInfo = collabParam["PeerInfo"] as abilityConnectionManager.PeerInfo; 259 if (peerInfo == undefined) { 260 return sessionId; 261 } 262 263 const options = collabParam["ConnectOption"] as abilityConnectionManager.ConnectOption; 264 options.needSendBigData = true; 265 options.needSendStream = true; 266 options.needReceiveStream = false; 267 try { 268 sessionId = abilityConnectionManager.createAbilityConnectionSession("collabTest", this.context, peerInfo, options); 269 AppStorage.setOrCreate('sessionId', sessionId); 270 hilog.info(0x0000, 'testTag', 'createSession sessionId is' + sessionId); 271 } catch (error) { 272 hilog.error(0x0000, 'testTag', error); 273 } 274 return sessionId; 275 } 276 } 277 ``` 278 279#### Enabling Event Listening 280 281After the application creates a session and obtains the session ID, you can call **on()** to listen for the corresponding events and notify the listener through a callback. 282<!--RP1--> 283 ```ts 284 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 285 import { hilog } from '@kit.PerformanceAnalysisKit'; 286 287 abilityConnectionManager.on("connect", this.sessionId,(callbackInfo) => { 288 hilog.info(0x0000, 'testTag', 'session connect, sessionId is', callbackInfo.sessionId); 289 }); 290 abilityConnectionManager.on("disconnect", this.sessionId,(callbackInfo) => { 291 hilog.info(0x0000, 'testTag', 'session disconnect, sessionId is', callbackInfo.sessionId); 292 }); 293 abilityConnectionManager.on("receiveMessage", this.sessionId,(callbackInfo) => { 294 hilog.info(0x0000, 'testTag', 'session receiveMessage, sessionId is', callbackInfo.sessionId); 295 }); 296 abilityConnectionManager.on("receiveData", this.sessionId,(callbackInfo) => { 297 hilog.info(0x0000, 'testTag', 'session receiveData, sessionId is', callbackInfo.sessionId); 298 }); 299 abilityConnectionManager.on("receiveImage", this.sessionId,(callbackInfo) => { 300 hilog.info(0x0000, 'testTag', 'session receiveImage, sessionId is', callbackInfo.sessionId); 301 }); 302<!--RP1End--> ``` 303 304#### Sending Data 305 306##### Sending Messages 307After the applications are successfully connected, you can call **sendMessage()** on device A or device B to send text messages to the peer application. 308 309 ```ts 310 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 311 import { hilog } from '@kit.PerformanceAnalysisKit'; 312 313 abilityConnectionManager.sendMessage(this.sessionId, "message send success").then(() => { 314 hilog.info(0x0000, 'testTag', "sendMessage success"); 315 }).catch(() => { 316 hilog.error(0x0000, 'testTag', "connect failed"); 317 }) 318 ``` 319<!--Del--> 320##### Sending Byte Streams 321 322After the applications are successfully connected, you can call **sendData()** on device A or device B to send byte streams to the peer application. (This function is supported only for system applications.) 323 324 ```ts 325 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 326 import { hilog } from '@kit.PerformanceAnalysisKit'; 327 328 let textEncoder = util.TextEncoder.create("utf-8"); 329 const arrayBuffer = textEncoder.encodeInto("data send success"); 330 331 abilityConnectionManager.sendData(this.sessionId, arrayBuffer.buffer).then(() => { 332 hilog.info(0x0000, 'testTag', "sendMessage success"); 333 }).catch(() => { 334 hilog.info(0x0000, 'testTag', "sendMessage failed"); 335 }) 336 ``` 337 338##### Sending Images 339 340After the applications are successfully connected, you can call **sendImage()** on device A or device B to send images to the peer application. (This function is supported only for system applications.) 341 342 ```ts 343 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 344 import { hilog } from '@kit.PerformanceAnalysisKit'; 345 import CameraService from '../model/CameraService'; 346 import { photoAccessHelper } from '@kit.MediaLibraryKit'; 347 import { image } from '@kit.ImageKit'; 348 import { fileIo as fs } from '@kit.CoreFileKit'; 349 350 try { 351 let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); 352 photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; 353 photoSelectOptions.maxSelectNumber = 5; 354 let photoPicker = new photoAccessHelper.PhotoViewPicker(); 355 photoPicker.select(photoSelectOptions).then((photoSelectResult) => { 356 if (!photoSelectResult) { 357 hilog.error(0x0000, 'testTag', 'photoSelectResult = null'); 358 return; 359 } 360 361 let file = fs.openSync(photoSelectResult.photoUris[0], fs.OpenMode.READ_ONLY); 362 hilog.info(0x0000, 'testTag', 'file.fd:' + file.fd); 363 364 let imageSourceApi: image.ImageSource = image.createImageSource(file.fd); 365 if (imageSourceApi) { 366 imageSourceApi.createPixelMap().then((pixelMap) => { 367 abilityConnectionManager.sendImage(this.sessionId, pixelMap) 368 }); 369 } else { 370 hilog.info(0x0000, 'testTag', 'imageSourceApi is undefined'); 371 } 372 }) 373 } catch (error) { 374 hilog.error(0x0000, 'testTag', 'photoPicker failed with error: ' + JSON.stringify(error)); 375 } 376 ``` 377 378##### Sending Transport Streams 379 380After the applications are successfully connected, you can call **createStream()** on device A or device B to create transport streams and call **startStream()** to send the transport streams to the peer application. (This function is supported only for system applications.) 381 382 ```ts 383 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 384 import { hilog } from '@kit.PerformanceAnalysisKit'; 385 386 hilog.info(0x0000, 'testTag', 'startStream'); 387 abilityConnectionManager.createStream(sessionId ,{name: 'receive', role: 0}).then(async (streamId) => { 388 let surfaceParam: abilityConnectionManager.SurfaceParam = { 389 width: 640, 390 height: 480, 391 format: 1 392 } 393 let surfaceId = abilityConnectionManager.getSurfaceId(streamId, surfaceParam); 394 hilog.info(0x0000, 'testTag', 'surfaceId is'+surfaceId); 395 AppStorage.setOrCreate<string>('surfaceId', surfaceId); 396 await CameraService.initCamera(surfaceId, 0); 397 abilityConnectionManager.startStream(streamId); 398 }) 399 ``` 400<!--DelEnd--> 401#### Ending Collaboration 402 403After the service collaboration is complete, the collaboration status must be ended in a timely manner. If service collaboration is required in a near future, you can call **disconnect()** to disconnect the connection between applications while retaining the session ID. This allows you to reuse the same session ID for establishing a connection next time. If service coordination is not required, you can directly call **destroyAbilityConnectionSession()** to destroy the session. In this case, the connection is automatically disconnected. 404 405 ```ts 406 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 407 import { hilog } from '@kit.PerformanceAnalysisKit'; 408 409 hilog.info(0x0000, 'testTag', 'disconnectRemoteAbility begin'); 410 if (this.sessionId == -1) { 411 hilog.info(0x0000, 'testTag', 'Invalid session ID.'); 412 return; 413 } 414 abilityConnectionManager.disconnect(this.sessionId); 415 416 hilog.info(0x0000, 'testTag', 'destroyAbilityConnectionSession called'); 417 abilityConnectionManager.destroyAbilityConnectionSession(this.sessionId); 418 ``` 419 420 421### Debugging and Verification 422 423After application development is complete, you can install the application on device A and device B. The test procedure is as follows: 424 4251. Tap the **Connect** button of the application on device A. The application on device B is started. 4262. Tap the **sendMessage** button of the application on device A. The application on device B triggers the callback of the **on()** API to receive the text strings. 427<!--Del--> 4283. Tap the **sendData** button of the application on device A. The application on device B triggers the callback of the **on()** API to receive the byte streams. 4294. Tap the **sendImage** button of the application on device A. The application on device B triggers the callback of the **on()** API to receive the images. 4305. Tap the **createStream** button of the application on device A. The application on device B triggers the callback of the **on()** API to receive the transport streams. 431<!--DelEnd--> 4326. Tap the **Disconnect** button of the application on device A or device B. The connection between the two devices is disconnected. The callback of the **connect()** API is triggered to report a disconnection event to the applications on both devices. 433 434## FAQs 435 436### What should I do if the application on device A fails to start the application on device B? 437 438**Possible Cause** 439 440- Devices are not networked with each other. When device A initiates a connection request, the **peerInfo.deviceId** attribute in the **createAbilityConnectionSession()** API is not correctly set. 441 442- Multiple devices are connected to each other. When device A initiates a connection request, the **peerInfo.deviceId** attribute in the **createAbilityConnectionSession()** API is set to **deviceId** of another device, but not device B. 443 444**Solution** 445 446- For cause 1, enable the USB debugging function on device A and device B, and use a USB cable to connect the devices to the PC. Run the following shell command on the PC: 447 448 ```shell 449 hdc shell 450 hidumper -s 4700 -a "buscenter -l remote_device_info" 451 ``` 452 If **remote device num = 0** is displayed in the command output, the networking has failed. Ensure that you log in to devices using the same HUAWEI ID and connect them through Bluetooth. If the networking is successful, the number of networking devices is displayed, for example, **remote device num = 1**. 453 454- For cause 2, add the desired to the device list to ensure that it is selected during device query and selection. 455 456### What should I do if the ongoing service collaboration is interrupted after the application screen is locked or the application is running in the background for a period of time? 457 458**Possible Cause** 459 460During service collaboration, DMS keeps listening for the collaboration lifecycle. If the application screen is locked or the application is running in the background for 5 seconds, the collaboration will be ended if the application does not apply for a continuous task. 461 462**Solution** 463 464[Apply for a continuous task](../task-management/continuous-task.md). 465