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 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 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  351 3522. Click **Signing Configs** and click **OK**. 353 354  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  371 3722. Click the name of device B. The application of device B is displayed. 373 374  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  379