Merge "Add metadata to validate incoming administrator during transfer of ownership."
diff --git a/api/current.txt b/api/current.txt
index db27817..bfb1934 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6354,6 +6354,7 @@
     field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
     field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
     field public static final java.lang.String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+    field public static final java.lang.String SUPPORT_TRANSFER_OWNERSHIP_META_DATA = "android.app.support_transfer_ownership";
   }
 
   public class DeviceAdminService extends android.app.Service {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index aa05b76..302d52f 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -467,6 +467,31 @@
     public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE =
             "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
 
+    /**
+     * Name under which a device administration component indicates whether it supports transfer of
+     * ownership. This meta-data is of type <code>boolean</code>. A value of <code>true</code>
+     * allows this administrator to be used as a target administrator for a transfer. If the value
+     * is <code>false</code>, ownership cannot be transferred to this administrator. The default
+     * value is <code>false</code>.
+     * <p>This metadata is used to avoid ownership transfer migration to an administrator with a
+     * version which does not yet support it.
+     * <p>Usage:
+     * <pre>
+     * &lt;receiver name="..." android:permission="android.permission.BIND_DEVICE_ADMIN"&gt;
+     *     &lt;meta-data
+     *         android:name="android.app.device_admin"
+     *         android:resource="@xml/..." /&gt;
+     *     &lt;meta-data
+     *         android:name="android.app.support_transfer_ownership"
+     *         android:value="true" /&gt;
+     * &lt;/receiver&gt;
+     * </pre>
+     *
+     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+     */
+    public static final String SUPPORT_TRANSFER_OWNERSHIP_META_DATA =
+            "android.app.support_transfer_ownership";
+
     private DevicePolicyManager mManager;
     private ComponentName mWho;
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0455949..cc4d29e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9096,6 +9096,11 @@
      * will be received in the
      * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback.
      *
+     * <p>The incoming target administrator must have the
+     * {@link DeviceAdminReceiver#SUPPORT_TRANSFER_OWNERSHIP_META_DATA} <code>meta-data</code> tag
+     * included in its corresponding <code>receiver</code> component with a value of {@code true}.
+     * Otherwise an {@link IllegalArgumentException} will be thrown.
+     *
      * @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
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0d9d85f..4e29935 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12096,6 +12096,11 @@
         final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
                 /* throwForMissingPermission= */ true);
         checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
+        if (!incomingDeviceInfo.getActivityInfo().metaData
+                .getBoolean(DeviceAdminReceiver.SUPPORT_TRANSFER_OWNERSHIP_META_DATA, false)) {
+            throw new IllegalArgumentException("Provided target does not support "
+                    + "ownership transfer.");
+        }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {