Move the support_transfer_ownership_metadata inside the device admin
descriptor XML.

Bug: 73750678
Test: cts-tradefed run cts-dev --module CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.MixedDeviceOwnerHostSideTransferTest#testTransferOwner
Test: cts-tradefed run cts-dev --module CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.MixedProfileOwnerHostSideTransferTest#testTransferOwner
Test: cts-tradefed run cts-dev --module CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.MixedDeviceOwnerHostSideTransferTest#testTransferOwnerNoMetadata
Test: cts-tradefed run cts-dev --module CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.MixedProfileOwnerHostSideTransferTest#testTransferOwnerNoMetadata
Test: cts-tradefed run cts-dev --module CtsAdminTestCases --test android.admin.cts.DeviceAdminInfoTest
Change-Id: I8df63af29dbcb23332e2f291b64b8782f25a751b
diff --git a/api/current.txt b/api/current.txt
index 2113d5d..b45c2f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6322,6 +6322,7 @@
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
+    method public boolean supportsTransferOwnership();
     method public boolean usesPolicy(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.admin.DeviceAdminInfo> CREATOR;
@@ -6386,7 +6387,6 @@
     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_OWNERSHIP_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNERSHIP_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/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index ed2aaf9..5fbe5b3 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,31 +16,31 @@
 
 package android.app.admin;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.content.res.Resources.NotFoundException;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.Xml;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -266,6 +266,12 @@
      */
     int mUsesPolicies;
 
+    /**
+     * Whether this administrator can be a target in an ownership transfer.
+     *
+     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+     */
+    boolean mSupportsTransferOwnership;
 
     /**
      * Constructor.
@@ -347,6 +353,12 @@
                                     + getComponent() + ": " + policyName);
                         }
                     }
+                } else if (tagName.equals("support-transfer-ownership")) {
+                    if (parser.next() != XmlPullParser.END_TAG) {
+                        throw new XmlPullParserException(
+                                "support-transfer-ownership tag must be empty.");
+                    }
+                    mSupportsTransferOwnership = true;
                 }
             }
         } catch (NameNotFoundException e) {
@@ -360,6 +372,7 @@
     DeviceAdminInfo(Parcel source) {
         mActivityInfo = ActivityInfo.CREATOR.createFromParcel(source);
         mUsesPolicies = source.readInt();
+        mSupportsTransferOwnership = source.readBoolean();
     }
 
     /**
@@ -458,6 +471,13 @@
         return sRevKnownPolicies.get(policyIdent).tag;
     }
 
+    /**
+     * Return true if this administrator can be a target in an ownership transfer.
+     */
+    public boolean supportsTransferOwnership() {
+        return mSupportsTransferOwnership;
+    }
+
     /** @hide */
     public ArrayList<PolicyInfo> getUsedPolicies() {
         ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>();
@@ -502,6 +522,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         mActivityInfo.writeToParcel(dest, flags);
         dest.writeInt(mUsesPolicies);
+        dest.writeBoolean(mSupportsTransferOwnership);
     }
 
     /**
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 8c30fc4..1c9477d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -505,31 +505,6 @@
     public static final String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE =
             "android.app.extra.TRANSFER_OWNERSHIP_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 d42fa99..6511f21 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9302,9 +9302,10 @@
      * after calling this method.
      *
      * <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.
+     * <code>&lt;support-transfer-ownership /&gt;</code> tag inside the
+     * <code>&lt;device-admin&gt;&lt;/device-admin&gt;</code> tags in the xml file referenced by
+     * {@link DeviceAdminReceiver#DEVICE_ADMIN_META_DATA}. 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
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ab8a6c4..83c9f7c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12658,8 +12658,7 @@
         final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
                 /* throwForMissingPermission= */ true);
         checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
-        if (!incomingDeviceInfo.getActivityInfo().metaData
-                .getBoolean(DeviceAdminReceiver.SUPPORT_TRANSFER_OWNERSHIP_META_DATA, false)) {
+        if (!incomingDeviceInfo.supportsTransferOwnership()) {
             throw new IllegalArgumentException("Provided target does not support "
                     + "ownership transfer.");
         }