# Migrate existing devices to AMAPI

Devices already managed by your custom DPC can be migrated to Android Device
Policy (ADP) and take advantage of the Android Management API.

> [!NOTE]
> **Note:** This process is transparent to end users. It is a one-way only process (it cannot be undone once completed) and it cannot be used to migrate a device from one EMM to another or from one enterprise to another.

## Prerequisites

- The device is already managed by your EMM with a custom DPC.
- Your custom DPC is integrated with the [AMAPI SDK](https://developers.google.com/android/management/sdk-integration).
- The device is enrolled with [Google Play EMM API](https://developers.google.com/android/work/play/emm-api).
- The device belongs to a [Managed Google Play Accounts enterprise](https://support.google.com/googleplay/work/answer/7042221).
- The device runs Android 9 or later.
- In case of work profiles on company-owned devices, the device must run Android 11 or later.

> [!IMPORTANT]
> **Important:** This process requires version 1.1.4+ of the AMAPI SDK library. You can find the latest available release in the [AMAPI SDK library's release
> notes](https://developers.google.com/android/management/sdk-release-notes). **We strongly recommend to always use the latest version of the library to
> benefits from the available bug fixes and improvements.**

## Integrate with the AMAPI SDK in your custom DPC

The migration process requires the custom DPC application to integrate the AMAPI
SDK. You can find more information about this library and how to add it to your
application in the [AMAPI SDK integration guide](https://developers.google.com/android/management/sdk-integration).

## Steps to migrate a device

1. Set up a [policy](https://developers.google.com/android/management/create-policy) intended to be used by the device after it is migrated to AMAPI. For the best user experience, this should be equivalent to the policy already enforced on the device by your DPC. The policy in AMAPI must belong to the same enterprise to which the device already belongs in the Play EMM API. Note that a given enterprise has the same name in both AMAPI and the Play EMM API.
2. Create a **migration token** for the device by calling [`enterprises.migrationTokens.create`](https://developers.google.com/android/management/reference/rest/v1/enterprises.migrationTokens/create).
3. Send the [`value`](https://developers.google.com/android/management/reference/rest/v1/enterprises.migrationTokens#MigrationToken.FIELDS.value) of this migration token to your custom DPC.
4. Make sure Android Device Policy is installed on the device by using [Play
   EMM API](https://developers.google.com/android/work/play/emm-api).
5. Use [`DpcMigrationClientFactory`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClientFactory) to create a [`DpcMigrationClient`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient)
6. On the `DpcMigrationClient`, call the [`migrateDeviceManagementToAndroidManagementApi`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient#migrateDeviceManagementToAndroidManagementApi(android.content.ComponentName,android.content.ComponentName,com.google.android.managementapi.dpcmigration.model.DpcMigrationRequest)) method. This completes the migration.
7. The [`deviceState`](https://developers.google.com/android/management/reference/rest/v1/enterprises.devices#devicestate) changes to [`ACTIVE`](https://developers.google.com/android/management/reference/rest/v1/enterprises.devices#DeviceState.ENUM_VALUES.ACTIVE), and you will receive a `STATUS_REPORT` message through the [Pub/Sub](https://developers.google.com/android/management/notifications) channel.

Once the migration completes the calling app loses its Device Owner or Profile
Owner privileges, as these are transferred to Android Device Policy. This
process can be represented by the following sequence diagram:

![DPC Migration sequence diagram](https://developers.google.com/static/android/management/images/dpcmigration_sequence.png)

**Note:** The device must be connected to the internet to begin migration. The
process is designed to be resilient to network disconnections during the process
of migration, so that the key operations requiring network connectivity are done
before the actual transfer of device owner or profile owner rights from your DPC
to Android Device Policy happens.

### Migration token

A migration token is requested by the EMM server to signal the intention to
migrate a particular device that is managed by a custom DPC. A migration token
can be used till the migration is completed successfully or till it expires.

### Custom DPC integration

First you need to create a [`DpcMigrationRequest`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationRequest), passing
the token, and if necessary, the list of configured Wi-Fi networks to its
builder:

    // Create a DpcMigrationRequest
    DpcMigrationRequest request =
            DpcMigrationRequest.builder()
                .setMigrationToken(token)
                .build();

You can then use get a [`DpcMigrationClient`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient) and start the
migration process with
[`migrateDeviceManagementToAndroidManagementApi`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient#migrateDeviceManagementToAndroidManagementApi(android.content.ComponentName,android.content.ComponentName,com.google.android.managementapi.dpcmigration.model.DpcMigrationRequest)):

    // Create a DpcMigrationClient
    DpcMigrationClient dpcMigrationClient = DpcMigrationClientFactory.create(context);
    try {
      // Use helper function to retrieve Admin component name
      var adminComponentName = getAdminComponent(context);

      ListenableFuture<DpcMigrationAttempt> futureAttempt =
              dpcMigrationClient.migrateDeviceManagementToAndroidManagementApi(
                  new ComponentName(context, DpcMigrationNotificationReceiver.class),
                  adminComponentName,
                  request);
      // handle futureAttempt
    } catch (RuntimeException e) {
      // send failure feedback: "Error: " + e
    }

## Setting up a `NotificationReceiverService` and track the migration

Implement a [`NotificationReceiverService`](https://developers.google.com/android/management/sdk-integration#notification_receiver_service)
in your custom DPC.

> [!IMPORTANT]
> **Important:** This feature requires implementing a `NotificationReceiverService` even if you don't intend to track the progress of the migration.

The migration process is tracked on the device through a
[`DpcMigrationAttempt`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationAttempt).

You can directly use the one returned by
[`migrateDeviceManagementToAndroidManagementApi`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient#migrateDeviceManagementToAndroidManagementApi(android.content.ComponentName,android.content.ComponentName,com.google.android.managementapi.dpcmigration.model.DpcMigrationRequest)) or use
[`getMigrationAttempt`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient#getMigrationAttempt(com.google.android.managementapi.dpcmigration.model.GetDpcMigrationAttemptRequest)) and
[`listMigrationAttempts`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationClient#listMigrationAttempts()) methods to get and list
migration attempts.

    // Passing an empty name, we retrieve the last attempt
    var request = GetDpcMigrationAttemptRequest.builder().build();

    var attempt = client.getMigrationAttempt(request);

You can optionally set up a [`DpcMigrationListener`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/DpcMigrationListener) using
your [`NotificationReceiverService`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/notification/NotificationReceiverService), to listen for
status updates for the [`DpcMigrationAttempt`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationAttempt).

    // DpcMigrationNotificationReceiver for callback handling
    public class DpcMigrationNotificationReceiver extends NotificationReceiverService
        implements DpcMigrationListener {

      @Override
      protected DpcMigrationListener getDpcMigrationListener() {
        return this;
      }

      @Override
      public void onMigrationStateChanged(DpcMigrationAttempt migrationAttempt) {
        // send success feedback
      }
    }

## Handle Wi-Fi networks

If there are Wi-Fi networks managed by the custom DPC, [AMAPI ONC policy](https://developers.google.com/android/management/reference/rest/v1/enterprises.policies#Policy.FIELDS.open_network_configuration)
should match configurations of these networks for AMAPI to start managing them
smoothly. The interaction of DPC migration with Wi-Fi management varies
depending on the management mode.

### Fully managed devices and work profiles on company-owned devices

During migration, Android Device Policy assumes that any Wi-Fi network
[configured in policy](https://developers.google.com/android/management/configure-networks) having the same SSID and security type of a
configured Wi-Fi network on the device is identical to the matching configured
Wi-Fi network. Hence, Wi-Fi networks configured by custom DPC are untouched
after migration until there is a change in the ONC policy corresponding to the
network. However, if the custom DPC is uninstalled after migration, then the
Wi-Fi networks configured by custom DPC are removed automatically. Android
Device Policy continues to enforce policy, and if any of these networks are
configured in policy, the networks as configured in policy are added as usual.

### Work profile on a personal device

For technical reasons, Wi-Fi networks configured by the custom DPC need to be
removed by the custom DPC for Android Device Policy to start managing these
Wi-Fi networks. The AMAPI SDK takes care of this and removes such Wi-Fi networks
before transferring the ownership from the custom DPC to Android Device Policy
but it requires the custom DPC to pass information about these networks in
[`DpcMigrationRequest`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationRequest). After migration, networks
[configured in policy](https://developers.google.com/android/management/configure-networks) will be added normally, so it is recommended that the
networks added by the custom DPC should be configured in policy as well.

There are some points to be aware of:

- If the active network is a Wi-Fi network configured by custom DPC, the device can briefly be offline during migration.
- Only the Wi-Fi networks configured by custom DPC should be passed in [`DpcMigrationRequest`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationRequest), otherwise migration fails if a network cannot be removed by AMAPI SDK (e.g. user added Wi-Fi network).
- Wi-Fi networks should be passed in [`DpcMigrationRequest`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationRequest) only when the custom DPC is a profile owner on a personally-owned device, otherwise migration fails.
- For technical reasons, Android 12 is an exceptional case where the networks passed in [`DpcMigrationRequest`](https://developers.google.com/android/management/reference/amapi/com/google/android/managementapi/dpcmigration/model/DpcMigrationRequest) are ignored and all Wi-Fi networks configured by the custom DPC are removed automatically. Further, it is a requirement for the custom DPC to have [`ACCESS_WIFI_STATE`](https://developer.android.com/reference/android/Manifest.permission#ACCESS_WIFI_STATE) permission on Android 12 for work profiles on personally-owned devices, otherwise migration fails.

## Caveats

Here are some caveats related to this feature.

### Enterprise-specific ID

For work profiles on Android 12 and later, the [enterprise-specific id](https://developers.google.com/android/management/reference/rest/v1/enterprises.devices#HardwareInfo.FIELDS.enterprise_specific_id),
which can be accessed from [`DevicePolicyManager.getEnrollmentSpecificId`](https://developer.android.com/reference/android/app/admin/DevicePolicyManager#getEnrollmentSpecificId())
does not change at the time of the migration. However, if a work profile managed
by Android Device Policy is created again on the device (for example after
deleting the previous one or after factory-resetting the device), the
enterprise-specific ID will change at that point.

### Work profiles on fully managed devices

This feature is not supported on fully managed devices which have a work profile
running Android 9 or 10. Migrating these devices must not be attempted, and
regardless of whether an error is raised, such devices are not supported for DPC
migration.

### RESET_PASSWORD command

If the device has a lock screen password then the [`RESET_PASSWORD` command](https://developers.google.com/android/management/reference/rest/v1/enterprises.devices/issueCommand#CommandType.ENUM_VALUES.RESET_PASSWORD)
will succeed only after the user confirms their credentials
(e.g. re-entering their PIN) after the migration. If the device has no lock
screen password, the `RESET_PASSWORD` command can be used right after the
migration.