• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Cross-Device Migration
2
3## Overview
4
5When the environment changes, for example, when a user goes outdoors or when a more appropriate device is detected, the user can migrate an ongoing task to another device for better experience. The application on the source device can automatically exit, depending on the setting. A typical cross-device migration scenario is as follows: You migrate a video playback task from a tablet to a smart TV. The video application on the tablet exits. From the perspective of application development, cross-device migration enables the UIAbility component running on device A to migrate to and keep running on device B. After the migration is complete, the UIAbility component on device A automatically exits (depending on the setting).
6
7The core task of cross-device migration is to migrate the status (including page components and status variables) of an application to the target device so that users can have continuous application experience on that device. This means that operations performed by a user on one device can be quickly switched and seamlessly connected in the same application on another device.
8
9Cross-device migration supports the following features:
10
11- Saving and restoring custom data
12
13- Saving and restoring page routing information and page component status data
14
15- Checking application compatibility
16
17- Dynamically setting the migration state (**ACTIVE** by default)
18
19  For example, for an editing application, only the text editing page needs to be migrated to the target device. In this case, you can call [setMissionContinueState](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10) for precise control.
20
21- Determining whether to restore the page stack (restored by default)
22
23  If an application wants to customize the page to be displayed after being migrated to the target device, you can use [SUPPORT_CONTINUE_PAGE_STACK_KEY](../reference/apis/js-apis-app-ability-wantConstant.md#wantconstantparams) for precise control.
24
25- Determining whether to exit the application on the source device after a successful migration (application exit by default)
26
27  You can use [SUPPORT_CONTINUE_SOURCE_EXIT_KEY](../reference/apis/js-apis-app-ability-wantConstant.md#wantconstantparams) for precise control.
28
29  > **NOTE**
30  >
31  > You only need to develop an application with the migration capabilities. System applications will trigger cross-device migration.
32
33
34## Working Principles
35
36![hop-cross-device-migration](figures/hop-cross-device-migration.png)
37
381. On the source device, the UIAbility uses the [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) callback to save service data to be continued. For example, to complete cross-device migration, a browser application uses the [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) callback to save service data such as the page URL. The system automatically saves the page status, such as the current browsing progress.
392. The distributed framework provides a mechanism for saving and restoring application UI, page stacks, and service data across devices. It sends the data from the source device to the target device.
403. On the target device, the same UIAbility uses the [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) callback to restore the service data.
41
42
43## Constraints
44
45- Cross-device migration must be performed between the same UIAbility component. In other words, **bundleName**, **abilityName**, and **signature** of the component on the two devices must be the same.
46- For better user experience, the data to be transmitted using the **wantParam** parameter must be less than 100 KB.
47- Certain ArkUI components can be restored to a given state on the target device after migration. For details, see [restoreId](../reference/arkui-ts/ts-universal-attributes-restoreId.md).
48
49## How to Develop
50
511. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file).
52
532. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization).
54
553. Configure the **continuable** tag under **abilities** in the [module.json5 file](../quick-start/module-configuration-file.md).
56
57   ```json
58   {
59     "module": {
60       // ...
61       "abilities": [
62         {
63           // ...
64           "continuable": true, // Configure the UIAbility to support migration.
65         }
66       ]
67     }
68   }
69   ```
70
71   > **NOTE**
72   >
73   > Configure the application launch type. For details, see [UIAbility Component Launch Type](uiability-launch-type.md).
74
754. Implement [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) in the UIAbility on the source device.
76
77   When a migration is triggered for the UIAbility, [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) is called on the source device. You can save the data in this method to implement application compatibility check and migration decision.
78
79   - Saving data to migrate: You can save the data to migrate in key-value pairs in **wantParam**.
80   - Checking application compatibility: You can obtain the application version on the target device from **wantParam.version** in the [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) callback and compare it with the [application version on the source device](../faqs/faqs-bundle-management.md).
81   - Making a migration decision: You can determine whether migration is supported based on the return value of [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue).
82
83   ```ts
84   import UIAbility from '@ohos.app.ability.UIAbility';
85   import AbilityConstant from '@ohos.app.ability.AbilityConstant';
86
87   export default class EntryAbility extends UIAbility {
88     onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
89       let version = wantParam.version;
90       let targetDevice = wantParam.targetDevice;
91       console.info(`onContinue version = ${version}, targetDevice: ${targetDevice}`); // Prepare data to migrate.
92
93       // Obtain the application version on the source device.
94       let versionSrc: number = -1; // Enter the version number obtained.
95
96       // Compatibility verification
97       if (version !== versionSrc) {
98         // Return MISMATCH when the compatibility check fails.
99         return AbilityConstant.OnContinueResult.MISMATCH;
100       }
101
102       // Save the data to migrate in the custom field (for example, data) of wantParam.
103       const continueInput = 'Data to migrate';
104       wantParam['data'] = continueInput;
105
106       return AbilityConstant.OnContinueResult.AGREE;
107     }
108   }
109   ```
110
1115. On the source device, call APIs to restore data and load the UI. The APIs vary according to the cold or hot start mode in use. For the UIAbility on the target device, implement [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) to restore the data.
112
113
114   The **launchReason** parameter in the [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) callback specifies whether the launch is triggered by migration. If the launch is triggered by migration, you must obtain the saved data from **want** and call **restoreWindowStage()** to trigger page restoration, including page stack information, after data restoration.
115
116   ```ts
117      import UIAbility from '@ohos.app.ability.UIAbility';
118      import AbilityConstant from '@ohos.app.ability.AbilityConstant';
119      import Want from '@ohos.app.ability.Want';
120
121      export default class EntryAbility extends UIAbility {
122        storage : LocalStorage = new LocalStorage();
123
124        onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
125          console.info('EntryAbility onCreate')
126          if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
127            // Obtain the data saved.
128            let continueInput = '';
129            if (want.parameters != undefined) {
130              continueInput = JSON.stringify(want.parameters.data);
131              console.info(`continue input ${continueInput}`)
132            }
133            // Display the data on the current page.
134            this.context.restoreWindowStage(this.storage);
135          }
136        }
137
138        onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
139           console.info('EntryAbility onNewWant')
140           if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
141             // Obtain the user data from the want parameter.
142             let continueInput = '';
143             if (want.parameters != undefined) {
144               continueInput = JSON.stringify(want.parameters.data);
145               console.info(`continue input ${continueInput}`);
146             }
147             this.context.restoreWindowStage(this.storage);
148           }
149         }
150      }
151   ```
152
153## Configuring Optional Migration Features
154
155### Dynamically Setting the Migration State
156
157Since API version 10, you can dynamically set the migration state. Specifically, you can enable or disable migration as required by calling [setMissionContinueState()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10). By default, **ACTIVE** is set for an application, indicating that migration is enabled.
158
159**Time for Setting the Migration State**
160
161The change of the migration state can be implemented based on service requirements. Typical methods are as follows:
162
163Call the API in the [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) callback of the UIAbility to set the migration state when the application is created.
164
165```ts
166// EntryAbility.ets
167import UIAbility from '@ohos.app.ability.UIAbility';
168import AbilityConstant from '@ohos.app.ability.AbilityConstant';
169import Want from '@ohos.app.ability.Want';
170
171export default class EntryAbility extends UIAbility {
172  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
173    // ...
174    this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
175      console.info(`setMissionContinueState: ${JSON.stringify(result)}`);
176    });
177    // ...
178  }
179}
180```
181
182Call the API in the **onPageShow()** callback of the page to set the migration state when a single page is displayed.
183
184```ts
185// PageName.ets
186import AbilityConstant from '@ohos.app.ability.AbilityConstant';
187import common from '@ohos.app.ability.common';
188@Entry
189@Component
190struct PageName {
191  private context = getContext(this) as common.UIAbilityContext;
192  build() {
193    // ...
194  }
195  // ...
196  onPageShow(){
197  // When the page is displayed, set the migration state to ACTIVE.
198    this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
199      console.info(`setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
200    });
201  }
202}
203```
204
205Set the migration state in the event of a component.
206
207```ts
208// PageName.ets
209import AbilityConstant from '@ohos.app.ability.AbilityConstant';
210import common from '@ohos.app.ability.common';
211
212@Entry
213@Component
214struct PageName {
215  private context = getContext(this) as common.UIAbilityContext;
216  build() {
217    // ...
218    Button() {
219      // ...
220    }.onClick(()=>{
221    // When the button is clicked, set the migration state to ACTIVE.
222      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
223        console.info(`setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
224      });
225    })
226  }
227}
228```
229
230**Ensuring Migration Continuity**
231
232During UI page loading, the application on the target device may have executed the command to set its own migration state (for example, set the state to **INACTIVE** in [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) during cold start or to **INACTIVE** during hot start since the application has opened a page that cannot be migrated). To ensure a migration back to the source device, you must set the migration state to **ACTIVE** in [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant).
233
234```ts
235// EntryAbility.ets
236import UIAbility from '@ohos.app.ability.UIAbility';
237import AbilityConstant from '@ohos.app.ability.AbilityConstant';
238import Want from '@ohos.app.ability.Want';
239
240export default class EntryAbility extends UIAbility {
241  // ...
242  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
243    // ...
244    // Set the migration state to ACTIVE when the launch is caused by migration. This setting copes with cold start.
245    this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
246        console.info(`setMissionContinueState INACTIVE result: ${JSON.stringify(result)}`);
247    });
248  }
249
250  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
251    // ...
252    // Set the migration state to ACTIVE when the launch is caused by migration. This setting copes with hot start.
253    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
254      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
255        console.info(`setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
256      });
257    }
258  }
259  // ...
260}
261```
262
263### Migrating the Page Stack on Demand
264
265Configure whether to restore the page stack. By default, the page stack is restored. If an application does not want to use the page stack restored by the system, you can set the page to be displayed on the target device in **onWindowStageRestore()**. For details about the configuration, see [SUPPORT_CONTINUE_PAGE_STACK_KEY](../reference/apis/js-apis-app-ability-wantConstant.md#wantconstantparams).
266
267The index and second routes exist in the page stack of the application on the source device. However, the application on the target device wants to display a specified page, rather than that on the source device.
268
269Example: A UIAbility does not want automatically restored page stack information.
270
271```ts
272// EntryAbility.ets
273import UIAbility from '@ohos.app.ability.UIAbility';
274import AbilityConstant from '@ohos.app.ability.AbilityConstant';
275import wantConstant from '@ohos.app.ability.wantConstant';
276import window from '@ohos.window';
277
278export default class EntryAbility extends UIAbility {
279  // ...
280
281  onContinue(wantParam: Record<string, Object>) {
282    console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
283    wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
284    return AbilityConstant.OnContinueResult.AGREE;
285  }
286
287  onWindowStageRestore(windowStage: window.WindowStage) {
288      // Set the page to be displayed after the migration.
289    windowStage.loadContent('pages/Index', (err, data) => {
290      if (err.code) {
291        return;
292      }
293    });
294  }
295}
296```
297
298### Exiting the Application on Demand
299
300Configure whether to exit the application on the source device after a successful migration. By default, the application on the source device exits by default. If you do not want the application on the source device to automatically exit after a successful migration, set the [SUPPORT_CONTINUE_SOURCE_EXIT_KEY](../reference/apis/js-apis-app-ability-wantConstant.md#wantconstantparams) parameter.
301
302Example: A UIAbility on the source device does not exit after a successful migration.
303
304```ts
305import UIAbility from '@ohos.app.ability.UIAbility';
306import AbilityConstant from '@ohos.app.ability.AbilityConstant';
307import wantConstant from '@ohos.app.ability.wantConstant';
308
309export default class EntryAbility extends UIAbility {
310  // ...
311
312  onContinue(wantParam: Record<string, Object>) {
313    console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
314    wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
315    return AbilityConstant.OnContinueResult.AGREE;
316  }
317}
318```
319
320## Verification Guide
321
322A mission center demo is provided for you to verify the migration capability of your application. The following walks you through on how to verify migration by using the demo.
323
324### Compiling and Installing the Demo
325
326#### Environment Configuration
327
328[Switch to the full SDK](../faqs/full-sdk-switch-guide.md) on DevEco Studio. This is because the mission center uses the system API [@ohos.distributedDeviceManager](../reference/apis/js-apis-distributedDeviceManager.md), which is not provided in the public SDK.
329
330> **NOTE**
331>
332> The screenshots in this section are for reference only. The DevEco Studio and SDK versions in use prevail.
333
334#### Demo Download
335
336Download the mission center demo from [Sample Code](https://gitee.com/openharmony/ability_dmsfwk/tree/master/services/dtbschedmgr/test/missionCenterDemo/dmsDemo/entry/src/main).
337
338#### Building Project Files
339
340a. Create an empty OpenHarmony project and replace the corresponding folders with the downloaded files.
341
342![hop-cross-device-migration](figures/hop-cross-device-migration1.png)
343
344b. Complete the signature, build, and installation.
345
346The default signature permission provided by the automatic signature template of DevEco Studio is normal. The mission center demo requires the **ohos.permission.MANAGE_MISSIONS** permission, which is at the system_core level. Therefore, you must escalate the permission to the system_core level. Specifically, change **"apl":"normal_core"** to **"apl":"system_core"** in the **UnsignedReleasedProfileTemplate.json** file in **openharmony\*apiVersion*\toolchains\lib**. Then sign the files as follows:
347
3481. Choose **File > Project Structure**.
349
350   ![hop-cross-device-migration](figures/hop-cross-device-migration2.png)
351
3522. Click **Signing Configs** and click **OK**.
353
354   ![hop-cross-device-migration](figures/hop-cross-device-migration3.png)
355
3563. Connect to the developer board and run the demo.
357
358### Device Networking
359
3601. Open the calculators of devices A and B.
3612. Click the arrow in the upper right corner to select device B.
3623. Select a trusted device on device B. The PIN is displayed.
3634. Enter the PIN on device A.
3645. Verify the networking. Enter a number on device A. If the number is displayed on device B, the networking is successful.
365
366### Initiation Migration
367
3681. Open your application on device B, and open the mission center demo on device A. The name of device A (Local: OpenHarmony 3.2) and the name of device B (OpenHarmony 3.2) are displayed on device A.
369
370   ![hop-cross-device-migration](figures/hop-cross-device-migration4.png)
371
3722. Click the name of device B. The application of device B is displayed.
373
374   ![hop-cross-device-migration](figures/hop-cross-device-migration5.png)
375
3763. Drag the application to the name of device A. The application on device A is started, and the application on device B exits.
377
378   ![hop-cross-device-migration](figures/hop-cross-device-migration6.png)
379