• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![how-abilityconnectmanager-works](figures/how-abilityconnectmanager-works.png)
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:&nbsp;string,&nbsp;context:&nbsp;Context,&nbsp;peerInfo:&nbsp;peerInfo,&nbsp;connectOpt:&nbsp;ConnectOption):&nbsp;number; | Creates a session between applications.|
108| destroyAbilityConnectionSession(sessionId:&nbsp;number):&nbsp;void; | Destroys a session between applications.|
109| connect(sessionId:&nbsp;number):&nbsp;Promise&lt;ConnectResult&gt;; | Connects to the ability on the source side.|
110| acceptConnect(sessionId:&nbsp;number,&nbsp;token:&nbsp;string):&nbsp;Promise&lt;void&gt;; | Connects to the ability on the sink side.|
111| disconnect(sessionId:&nbsp;number):&nbsp;void; | Disconnects the ability connection.|
112| on(type:&nbsp;'connect'&nbsp;\| &nbsp;'disconnect'&nbsp;\| &nbsp;'receiveMessage'&nbsp;\| &nbsp;'receiveData'&nbsp;\| &nbsp;'receiveImage',&nbsp;sessionId:&nbsp;number,&nbsp;callback:&nbsp;Callback&lt;EventCallbackInfo&gt;):&nbsp;void | Enable listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, **receiveData**, and **receiveImage **<!--DelEnd-->events.|
113| off(type:&nbsp;'connect'&nbsp;\| &nbsp;'disconnect'&nbsp;\| &nbsp;'receiveMessage'&nbsp;\| &nbsp;'receiveData'&nbsp;\| &nbsp;'receiveImage',&nbsp;'connect',&nbsp;sessionId:&nbsp;number,&nbsp;callback?:&nbsp;Callback&lt;EventCallbackInfo&gt;):&nbsp;void | Cancels listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, **receiveData**, and **receiveImage **<!--DelEnd-->events.|
114| sendMessage(sessionId:&nbsp;number,&nbsp;msg:&nbsp;string):&nbsp;Promise&lt;void&gt;; | Sends a text message.|
115|<!--DelRow--> sendData(sessionId:&nbsp;number,&nbsp;data:&nbsp;ArrayBuffer):&nbsp;Promise&lt;void&gt;; | Sends byte streams (supported only for system applications).|
116|<!--DelRow--> sendImage(sessionId:&nbsp;number,&nbsp;image:&nbsp;image.PixelMap):&nbsp;Promise&lt;void&gt;; | Sends an image (supported only for system applications).|
117|<!--DelRow--> createStream(sessionId:&nbsp;number,&nbsp;param:&nbsp;StreamParam):&nbsp;Promise&lt;number&gt;; | Creates transport streams (supported only for system applications).|
118|<!--DelRow--> destroyStream(sessionId:&nbsp;number):&nbsp;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