Call PROFILE/DEVICE_OWNER_CHANGED broadcast and onTransferCompleted callback upon a successful transfer.

Test: cts-tradefed run cts-dev --module DevicePolicyManager --test com.android.cts.devicepolicy.MixedProfileOwnerHostSideTransferTest#testTransferOwnerChangedBroadcast
Test: cts-tradefed run cts-dev --module DevicePolicyManager --test com.android.cts.devicepolicy.MixedProfileOwnerHostSideTransferTest#testTransferCompleteCallback
Test: cts-tradefed run cts-dev --module DevicePolicyManager --test com.android.cts.devicepolicy.MixedDeviceOwnerHostSideTransferTest#testTransferOwnerChangedBroadcast
Test: cts-tradefed run cts-dev --module DevicePolicyManager --test com.android.cts.devicepolicy.MixedDeviceOwnerHostSideTransferTest#testTransferCompleteCallback
Bug: 69542936
Bug: 69543044
Change-Id: Ifbe3ac0029794eba185e538e5a490073d5309f0b
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 2e697ac..aa05b76 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -29,10 +29,14 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.security.KeyChain;
 
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -438,6 +442,31 @@
     //  TO DO: describe syntax.
     public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
 
+    /**
+     * Broadcast action: notify the newly transferred administrator that the transfer
+     * from the original administrator was successful.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_TRANSFER_OWNERSHIP_COMPLETE =
+            "android.app.action.TRANSFER_OWNERSHIP_COMPLETE";
+
+    /**
+     * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
+     * allows a mobile device management application to pass data to the management application
+     * instance after owner transfer.
+     *
+     * <p>
+     * If the transfer is successful, the new device owner receives the data in
+     * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)}.
+     * The bundle is not changed during the ownership transfer.
+     *
+     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+     */
+    public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE =
+            "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+
     private DevicePolicyManager mManager;
     private ComponentName mWho;
 
@@ -860,6 +889,20 @@
      }
 
     /**
+     * Called on the newly assigned owner (either device owner or profile owner) when the ownership
+     * transfer has completed successfully.
+     *
+     * <p> The {@code bundle} parameter allows the original owner to pass data
+     * to the new one.
+     *
+     * @param context the running context as per {@link #onReceive}
+     * @param bundle the data to be passed to the new owner
+     */
+    public void onTransferOwnershipComplete(@NonNull Context context,
+            @Nullable PersistableBundle bundle) {
+    }
+
+    /**
      * Intercept standard device administrator broadcasts.  Implementations
      * should not override this method; it is better to implement the
      * convenience callbacks for each action.
@@ -921,6 +964,10 @@
             onUserAdded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
         } else if (ACTION_USER_REMOVED.equals(action)) {
             onUserRemoved(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+        } else if (ACTION_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
+            PersistableBundle bundle =
+                    intent.getParcelableExtra(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE);
+            onTransferOwnershipComplete(context, bundle);
         }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0b74741..e46e1cc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1124,6 +1124,7 @@
      *
      * This broadcast is sent only to the primary user.
      * @see #ACTION_PROVISION_MANAGED_DEVICE
+     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_OWNER_CHANGED
@@ -1709,6 +1710,16 @@
     public static final int ID_TYPE_MEID = 8;
 
     /**
+     * Broadcast action: sent when the profile owner is set, changed or cleared.
+     *
+     * This broadcast is sent only to the user managed by the new profile owner.
+     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PROFILE_OWNER_CHANGED =
+            "android.app.action.PROFILE_OWNER_CHANGED";
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -8990,41 +9001,34 @@
         }
     }
 
-    //TODO STOPSHIP Add link to onTransferComplete callback when implemented.
     /**
-     * Transfers the current administrator. All policies from the current administrator are
-     * migrated to the new administrator. The whole operation is atomic - the transfer is either
-     * complete or not done at all.
+     * Changes the current administrator to another one. All policies from the current
+     * administrator are migrated to the new administrator. The whole operation is atomic -
+     * the transfer is either complete or not done at all.
      *
-     * Depending on the current administrator (device owner, profile owner, corporate owned
-     * profile owner), you have the following expected behaviour:
+     * <p>Depending on the current administrator (device owner, profile owner), you have the
+     * following expected behaviour:
      * <ul>
      *     <li>A device owner can only be transferred to a new device owner</li>
      *     <li>A profile owner can only be transferred to a new profile owner</li>
-     *     <li>A corporate owned managed profile can have two cases:
-     *          <ul>
-     *              <li>If the device owner and profile owner are the same package,
-     *              both will be transferred.</li>
-     *              <li>If the device owner and profile owner are different packages,
-     *              and if this method is called from the profile owner, only the profile owner
-     *              is transferred. Similarly, if it is called from the device owner, only
-     *              the device owner is transferred.</li>
-     *          </ul>
-     *     </li>
      * </ul>
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param target Which {@link DeviceAdminReceiver} we want the new administrator to be.
-     * @param bundle Parameters - This bundle allows the current administrator to pass data to the
-     *               new administrator. The parameters will be received in the
-     *               onTransferComplete callback.
-     * @hide
+     * <p>Use the {@code bundle} parameter to pass data to the new administrator. The parameters
+     * will be received in the
+     * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with
+     * @param target which {@link DeviceAdminReceiver} we want the new administrator to be
+     * @param bundle data to be sent to the new administrator
+     * @throws SecurityException if {@code admin} is not a device owner nor a profile owner
+     * @throws IllegalArgumentException if {@code admin} or {@code target} is {@code null},
+     * both are components in the same package or {@code target} is not an active admin
      */
-    public void transferOwner(@NonNull ComponentName admin, @NonNull ComponentName target,
+    public void transferOwnership(@NonNull ComponentName admin, @NonNull ComponentName target,
             PersistableBundle bundle) {
-        throwIfParentInstance("transferOwner");
+        throwIfParentInstance("transferOwnership");
         try {
-            mService.transferOwner(admin, target, bundle);
+            mService.transferOwnership(admin, target, bundle);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5916a62..1d8ddee 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -387,5 +387,5 @@
     boolean isLogoutEnabled();
 
     List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
-    void transferOwner(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
+    void transferOwnership(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 52bfcde..72bcdf6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -567,6 +567,10 @@
     <!-- Made protected in P (was introduced in JB-MR2) -->
     <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
 
+    <!-- Added in P -->
+    <protected-broadcast android:name="android.app.action.PROFILE_OWNER_CHANGED" />
+    <protected-broadcast android:name="android.app.action.TRANSFER_OWNER_COMPLETE" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->