Allow device owner and profile owner on an org-owned device to set MTE policy.

Test: unit tests
Test: TestDPC with COPE and DO
Bug: 244290023
Change-Id: Icd8135f2ae491ac5a3b736337499cf734ee6f2e3
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index dd82bc1..83398d6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3913,34 +3913,46 @@
     public static @interface MtePolicy {}
 
     /**
-     * Set MTE policy for device. MTE_ENABLED does not necessarily enable MTE if set on a device
-     * that does not support MTE.
-     *
-     * The default policy is MTE_NOT_CONTROLLED_BY_POLICY.
-     *
-     * Memory Tagging Extension (MTE) is a CPU extension that allows to protect against certain
+     * Called by a device owner or profile owner of an organization-owned device to set the Memory
+     * Tagging Extension (MTE) policy. MTE is a CPU extension that allows to protect against certain
      * classes of security problems at a small runtime performance cost overhead.
      *
-     * @param policy the policy to be set
+     * <p>The MTE policy can only be set to {@link #MTE_DISABLED} if called by a device owner.
+     * Otherwise a {@link SecurityException} will be thrown.
+     *
+     * @throws SecurityException if caller is not device owner or profile owner of org-owned device
+     *     or if called on a parent instance
+     * @param policy the MTE policy to be set
      */
     public void setMtePolicy(@MtePolicy int policy) {
-        // TODO(b/244290023): implement
-        // This is SecurityException to temporarily make ParentProfileTest happy.
-        // This is not used.
-        throw new SecurityException("not implemented");
+        throwIfParentInstance("setMtePolicy");
+        if (mService != null) {
+            try {
+                mService.setMtePolicy(policy);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
-     * Get currently set MTE policy. This is not necessarily the same as the state of MTE on the
-     * device, as the device might not support MTE.
+     * Called by a device owner, a profile owner of an organization-owned device or the system to
+     * get the Memory Tagging Extension (MTE) policy
      *
-     * @return the currently set policy
+     * @throws SecurityException if caller is not device owner or profile owner of org-owned device
+     *                           or system uid, or if called on a parent instance
+     * @return the currently set MTE policy
      */
     public @MtePolicy int getMtePolicy() {
-        // TODO(b/244290023): implement
-        // This is SecurityException to temporarily make ParentProfileTest happy.
-        // This is not used.
-        throw new SecurityException("not implemented");
+        throwIfParentInstance("setMtePolicy");
+        if (mService != null) {
+            try {
+                return mService.getMtePolicy();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return MTE_NOT_CONTROLLED_BY_POLICY;
     }
 
     // TODO: Expose this as SystemAPI once we add the query API
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8a40265..5383dca 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -571,4 +571,7 @@
 
     void setApplicationExemptions(String packageName, in int[]exemptions);
     int[] getApplicationExemptions(String packageName);
+
+    void setMtePolicy(int flag);
+    int getMtePolicy();
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 1573edb..5610ac4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -17,6 +17,7 @@
 package com.android.settingslib;
 
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+import static android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 
 import android.annotation.NonNull;
@@ -733,6 +734,26 @@
     }
 
     /**
+     * Checks whether MTE (Advanced memory protection) controls are disabled by the enterprise
+     * policy.
+     */
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static EnforcedAdmin checkIfMteIsDisabled(Context context) {
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        if (dpm.getMtePolicy() == MTE_NOT_CONTROLLED_BY_POLICY) {
+            return null;
+        }
+        EnforcedAdmin admin =
+                RestrictedLockUtils.getProfileOrDeviceOwner(
+                        context, UserHandle.of(UserHandle.USER_SYSTEM));
+        if (admin != null) {
+            return admin;
+        }
+        int profileId = getManagedProfileId(context, UserHandle.USER_SYSTEM);
+        return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
+    }
+
+    /**
      * Show restricted setting dialog.
      */
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 9af30ba..4634ff5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -163,6 +163,7 @@
             "preferential_network_service_config";
     private static final String TAG_PROTECTED_PACKAGES = "protected_packages";
     private static final String TAG_SUSPENDED_PACKAGES = "suspended-packages";
+    private static final String TAG_MTE_POLICY = "mte-policy";
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
     private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -222,6 +223,8 @@
     int numNetworkLoggingNotifications = 0;
     long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
 
+    @DevicePolicyManager.MtePolicy int mtePolicy = DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+
     ActiveAdmin parentAdmin;
     final boolean isParent;
 
@@ -620,6 +623,9 @@
             }
             out.endTag(null, TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIGS);
         }
+        if (mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+            writeAttributeValueToXml(out, TAG_MTE_POLICY, mtePolicy);
+        }
     }
 
     private List<String> ssidsToStrings(Set<WifiSsid> ssids) {
@@ -906,6 +912,8 @@
                 if (!configs.isEmpty()) {
                     mPreferentialNetworkServiceConfigs = configs;
                 }
+            } else if (TAG_MTE_POLICY.equals(tag)) {
+                mtePolicy = parser.getAttributeInt(null, ATTR_VALUE);
             } else {
                 Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
@@ -1338,5 +1346,8 @@
             }
             pw.decreaseIndent();
         }
+
+        pw.print("mtePolicy=");
+        pw.println(mtePolicy);
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e93809d..c42ddf8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5387,7 +5387,8 @@
                         }
                         if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
                             throw new UnsupportedOperationException(
-                                    "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
+                                    "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE"
+                                        + " devices");
                         }
                         mUserManager.evictCredentialEncryptionKey(callingUserId);
                     }
@@ -19240,4 +19241,55 @@
                 KEEP_PROFILES_RUNNING_FLAG,
                 DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
     }
+
+    @Override
+    public void setMtePolicy(int flags) {
+        final Set<Integer> allowedModes =
+                Set.of(
+                        DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY,
+                        DevicePolicyManager.MTE_DISABLED,
+                        DevicePolicyManager.MTE_ENABLED);
+        Preconditions.checkArgument(
+                allowedModes.contains(flags), "Provided mode is not one of the allowed values.");
+        final CallerIdentity caller = getCallerIdentity();
+        if (flags == DevicePolicyManager.MTE_DISABLED) {
+            Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+        } else {
+            Preconditions.checkCallAuthorization(
+                    isDefaultDeviceOwner(caller)
+                            || isProfileOwnerOfOrganizationOwnedDevice(caller));
+        }
+        synchronized (getLockObject()) {
+            ActiveAdmin admin =
+                    getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                            UserHandle.USER_SYSTEM);
+            if (admin != null) {
+                final String memtagProperty = "arm64.memtag.bootctl";
+                if (flags == DevicePolicyManager.MTE_ENABLED) {
+                    mInjector.systemPropertiesSet(memtagProperty, "memtag");
+                } else if (flags == DevicePolicyManager.MTE_DISABLED) {
+                    mInjector.systemPropertiesSet(memtagProperty, "memtag-off");
+                }
+                admin.mtePolicy = flags;
+                saveSettingsLocked(caller.getUserId());
+            }
+        }
+    }
+
+    @Override
+    public int getMtePolicy() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDefaultDeviceOwner(caller)
+                        || isProfileOwnerOfOrganizationOwnedDevice(caller)
+                        || isSystemUid(caller));
+        synchronized (getLockObject()) {
+            ActiveAdmin admin =
+                    getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                            UserHandle.USER_SYSTEM);
+            return admin != null
+                    ? admin.mtePolicy
+                    : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+        }
+    }
 }