• 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(serviceName:&nbsp;string,&nbsp;context:&nbsp;Context,&nbsp;peerInfo:&nbsp;PeerInfo,&nbsp;connectOptions:&nbsp;ConnectOptions):&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;sessionId:&nbsp;number,&nbsp;callback:&nbsp;Callback&lt;EventCallbackInfo&gt;):&nbsp;void | Enable listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, and **receiveData**<!--DelEnd-->events.|
113| off(type:&nbsp;'connect'&nbsp;\| &nbsp;'disconnect'&nbsp;\| &nbsp;'receiveMessage'&nbsp;\| &nbsp;'receiveData',&nbsp;sessionId:&nbsp;number,&nbsp;callback?:&nbsp;Callback&lt;EventCallbackInfo&gt;):&nbsp;void | Cancels listening for <!--Del-->the **connect**, **disconnect**, **receiveMessage**, and **receiveData**<!--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    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-->