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(serviceName: string, context: Context, peerInfo: PeerInfo, connectOptions: ConnectOptions): 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', sessionId: number, callback: Callback<EventCallbackInfo>): void | Enable listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, and **receiveData**<!--DelEnd-->events.| 113| off(type: 'connect' \| 'disconnect' \| 'receiveMessage' \| 'receiveData', sessionId: number, callback?: Callback<EventCallbackInfo>): void | Cancels listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, and **receiveData**<!--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 serviceName: '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 connectOptions: abilityConnectionManager.ConnectOptions = { 196 needSendData: true, 197 startOptions: abilityConnectionManager.StartOptionParams.START_IN_FOREGROUND, 198 parameters: myRecord 199 }; 200 let context = this.getUIContext().getHostContext(); 201 try { 202 this.sessionId = abilityConnectionManager.createAbilityConnectionSession("collabTest", context, peerInfo, connectOptions); 203 hilog.info(0x0000, 'testTag', 'createSession sessionId is', this.sessionId); 204 205 abilityConnectionManager.connect(this.sessionId).then((ConnectResult) => { 206 if (!ConnectResult.isConnected) { 207 hilog.info(0x0000, 'testTag', 'connect failed'); 208 return; 209 } 210 }).catch(() => { 211 hilog.error(0x0000, 'testTag', "connect failed"); 212 }) 213 214 } catch (error) { 215 hilog.error(0x0000, 'testTag', error); 216 } 217 ``` 218 219##### Device B 220 221After 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. 222 223 ```ts 224 import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 225 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 226 import { hilog } from '@kit.PerformanceAnalysisKit'; 227 228 export default class EntryAbility extends UIAbility { 229 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 230 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 231 } 232 233 onCollaborate(wantParam: Record<string, Object>): AbilityConstant.CollaborateResult { 234 hilog.info(0x0000, 'testTag', '%{public}s', 'on collaborate'); 235 let param = wantParam["ohos.extra.param.key.supportCollaborateIndex"] as Record<string, Object> 236 this.onCollab(param); 237 return 0; 238 } 239 240 onCollab(collabParam: Record<string, Object>) { 241 const sessionId = this.createSessionFromWant(collabParam); 242 if (sessionId == -1) { 243 hilog.info(0x0000, 'testTag', 'Invalid session ID.'); 244 return; 245 } 246 const collabToken = collabParam["ohos.dms.collabToken"] as string; 247 abilityConnectionManager.acceptConnect(sessionId, collabToken).then(() => { 248 hilog.info(0x0000, 'testTag', 'acceptConnect success'); 249 }).catch(() => { 250 hilog.error("failed"); 251 }) 252 } 253 254 createSessionFromWant(collabParam: Record<string, Object>): number { 255 let sessionId = -1; 256 const peerInfo = collabParam["PeerInfo"] as abilityConnectionManager.PeerInfo; 257 if (peerInfo == undefined) { 258 return sessionId; 259 } 260 261 const options = collabParam["ConnectOptions"] as abilityConnectionManager.ConnectOptions; 262 options.needSendBigData = true; 263 options.needSendStream = true; 264 options.needReceiveStream = false; 265 try { 266 sessionId = abilityConnectionManager.createAbilityConnectionSession("collabTest", this.context, peerInfo, options); 267 AppStorage.setOrCreate('sessionId', sessionId); 268 hilog.info(0x0000, 'testTag', 'createSession sessionId is' + sessionId); 269 } catch (error) { 270 hilog.error(0x0000, 'testTag', error); 271 } 272 return sessionId; 273 } 274 } 275 ``` 276 277#### Enabling Event Listening 278 279After 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. 280<!--RP1--> 281 ```ts 282 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 283 import { hilog } from '@kit.PerformanceAnalysisKit'; 284 285 abilityConnectionManager.on("connect", this.sessionId,(callbackInfo) => { 286 hilog.info(0x0000, 'testTag', 'session connect, sessionId is', callbackInfo.sessionId); 287 }); 288 abilityConnectionManager.on("disconnect", this.sessionId,(callbackInfo) => { 289 hilog.info(0x0000, 'testTag', 'session disconnect, sessionId is', callbackInfo.sessionId); 290 }); 291 abilityConnectionManager.on("receiveMessage", this.sessionId,(callbackInfo) => { 292 hilog.info(0x0000, 'testTag', 'session receiveMessage, sessionId is', callbackInfo.sessionId); 293 }); 294 abilityConnectionManager.on("receiveData", this.sessionId,(callbackInfo) => { 295 hilog.info(0x0000, 'testTag', 'session receiveData, sessionId is', callbackInfo.sessionId); 296 }); 297 abilityConnectionManager.on("receiveImage", this.sessionId,(callbackInfo) => { 298 hilog.info(0x0000, 'testTag', 'session receiveImage, sessionId is', callbackInfo.sessionId); 299 }); 300<!--RP1End--> ``` 301 302#### Sending Data 303 304##### Sending Messages 305After the applications are successfully connected, you can call **sendMessage()** on device A or device B to send text messages to the peer application. 306 307 ```ts 308 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 309 import { hilog } from '@kit.PerformanceAnalysisKit'; 310 311 abilityConnectionManager.sendMessage(this.sessionId, "message send success").then(() => { 312 hilog.info(0x0000, 'testTag', "sendMessage success"); 313 }).catch(() => { 314 hilog.error(0x0000, 'testTag', "connect failed"); 315 }) 316 ``` 317<!--Del--> 318##### Sending Byte Streams 319 320After 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.) 321 322 ```ts 323 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 324 import { hilog } from '@kit.PerformanceAnalysisKit'; 325 326 let textEncoder = util.TextEncoder.create("utf-8"); 327 const arrayBuffer = textEncoder.encodeInto("data send success"); 328 329 abilityConnectionManager.sendData(this.sessionId, arrayBuffer.buffer).then(() => { 330 hilog.info(0x0000, 'testTag', "sendMessage success"); 331 }).catch(() => { 332 hilog.info(0x0000, 'testTag', "sendMessage failed"); 333 }) 334 ``` 335 336##### Sending Images 337 338After 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.) 339 340 ```ts 341 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 342 import { hilog } from '@kit.PerformanceAnalysisKit'; 343 import CameraService from '../model/CameraService'; 344 import { photoAccessHelper } from '@kit.MediaLibraryKit'; 345 import { image } from '@kit.ImageKit'; 346 import { fileIo as fs } from '@kit.CoreFileKit'; 347 348 try { 349 let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); 350 photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; 351 photoSelectOptions.maxSelectNumber = 5; 352 let photoPicker = new photoAccessHelper.PhotoViewPicker(); 353 photoPicker.select(photoSelectOptions).then((photoSelectResult) => { 354 if (!photoSelectResult) { 355 hilog.error(0x0000, 'testTag', 'photoSelectResult = null'); 356 return; 357 } 358 359 let file = fs.openSync(photoSelectResult.photoUris[0], fs.OpenMode.READ_ONLY); 360 hilog.info(0x0000, 'testTag', 'file.fd:' + file.fd); 361 362 let imageSourceApi: image.ImageSource = image.createImageSource(file.fd); 363 if (imageSourceApi) { 364 imageSourceApi.createPixelMap().then((pixelMap) => { 365 abilityConnectionManager.sendImage(this.sessionId, pixelMap) 366 }); 367 } else { 368 hilog.info(0x0000, 'testTag', 'imageSourceApi is undefined'); 369 } 370 }) 371 } catch (error) { 372 hilog.error(0x0000, 'testTag', 'photoPicker failed with error: ' + JSON.stringify(error)); 373 } 374 ``` 375 376##### Sending Transport Streams 377 378After 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.) 379 380 ```ts 381 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 382 import { hilog } from '@kit.PerformanceAnalysisKit'; 383 384 hilog.info(0x0000, 'testTag', 'startStream'); 385 abilityConnectionManager.createStream(sessionId ,{name: 'receive', role: 0}).then(async (streamId) => { 386 let surfaceParam: abilityConnectionManager.SurfaceParam = { 387 width: 640, 388 height: 480, 389 format: 1 390 } 391 let surfaceId = abilityConnectionManager.getSurfaceId(streamId, surfaceParam); 392 hilog.info(0x0000, 'testTag', 'surfaceId is'+surfaceId); 393 AppStorage.setOrCreate<string>('surfaceId', surfaceId); 394 await CameraService.initCamera(surfaceId, 0); 395 abilityConnectionManager.startStream(streamId); 396 }) 397 ``` 398<!--DelEnd--> 399#### Ending Collaboration 400 401After 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. 402 403 ```ts 404 import { abilityConnectionManager } from '@kit.DistributedServiceKit'; 405 import { hilog } from '@kit.PerformanceAnalysisKit'; 406 407 hilog.info(0x0000, 'testTag', 'disconnectRemoteAbility begin'); 408 if (this.sessionId == -1) { 409 hilog.info(0x0000, 'testTag', 'Invalid session ID.'); 410 return; 411 } 412 abilityConnectionManager.disconnect(this.sessionId); 413 414 hilog.info(0x0000, 'testTag', 'destroyAbilityConnectionSession called'); 415 abilityConnectionManager.destroyAbilityConnectionSession(this.sessionId); 416 ``` 417 418 419### Debugging and Verification 420 421After application development is complete, you can install the application on device A and device B. The test procedure is as follows: 422 4231. Tap the **Connect** button of the application on device A. The application on device B is started. 4242. 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. 425<!--Del--> 4263. 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. 4274. 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. 4285. 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. 429<!--DelEnd--> 4306. 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. 431 432## FAQs 433 434### What should I do if the application on device A fails to start the application on device B? 435 436**Possible Cause** 437 438- 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. 439 440- 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. 441 442**Solution** 443 444- 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: 445 446 ```shell 447 hdc shell 448 hidumper -s 4700 -a "buscenter -l remote_device_info" 449 ``` 450 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**. 451 452- For cause 2, add the desired to the device list to ensure that it is selected during device query and selection. 453 454### 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? 455 456**Possible Cause** 457 458During 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. 459 460**Solution** 461 462[Apply for a continuous task](../task-management/continuous-task.md). 463 464<!--no_check-->