Merge "Add API for checking which CA certs were installed by the DO/PO"
diff --git a/api/test-current.txt b/api/test-current.txt
index 5e5c8b5..e496a96 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6266,6 +6266,7 @@
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
     method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
+    method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(android.os.UserHandle);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6585793..449cca3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7958,4 +7958,28 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+
+    /**
+     * Called by the system to get a list of CA certificates that were installed by the device or
+     * profile owner.
+     *
+     * <p> The caller must be the target user's Device Owner/Profile owner or hold the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+     *
+     * @param user The user for whom to retrieve information.
+     * @return list of aliases identifying CA certificates installed by the device or profile owner
+     * @throws SecurityException if the caller does not have permission to retrieve information
+     *         about the given user's CA certificates.
+     *
+     * @hide
+     */
+    @TestApi
+    public List<String> getOwnerInstalledCaCerts(@NonNull UserHandle user) {
+        try {
+            return mService.getOwnerInstalledCaCerts(user).getList();
+        } 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 ec97c2c..97a4678 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -27,6 +27,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.StringParceledListSlice;
 import android.graphics.Bitmap;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -349,4 +350,5 @@
     boolean resetPasswordWithToken(in ComponentName admin, String password, in byte[] token, int flags);
 
     boolean isDefaultInputMethodSetByOwner(in UserHandle user);
+    StringParceledListSlice getOwnerInstalledCaCerts(in UserHandle user);
 }
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b685431..c4bb72c 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -29,8 +29,8 @@
     byte[] getCertificate(String alias);
     byte[] getCaCertificates(String alias);
 
-    // APIs used by CertInstaller
-    void installCaCertificate(in byte[] caCertificate);
+    // APIs used by CertInstaller and DevicePolicyManager
+    String installCaCertificate(in byte[] caCertificate);
 
     // APIs used by DevicePolicyManager
     boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8b94ca0..9c3ecd0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -99,6 +99,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -166,6 +167,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
@@ -243,10 +245,14 @@
 
     private static final String TAG_DEFAULT_INPUT_METHOD_SET = "default-ime-set";
 
+    private static final String TAG_OWNER_INSTALLED_CA_CERT = "owner-installed-ca-cert";
+
     private static final String ATTR_ID = "id";
 
     private static final String ATTR_VALUE = "value";
 
+    private static final String ATTR_ALIAS = "alias";
+
     private static final String TAG_INITIALIZATION_BUNDLE = "initialization-bundle";
 
     private static final String TAG_PASSWORD_TOKEN_HANDLE = "password-token";
@@ -480,6 +486,7 @@
         final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
         final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
 
+        // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
         final ArraySet<String> mAcceptedCaCertificates = new ArraySet<>();
 
         // This is the list of component allowed to start lock task mode.
@@ -504,6 +511,9 @@
 
         boolean mDefaultInputMethodSet = false;
 
+        // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
+        Set<String> mOwnerInstalledCaCerts = new ArraySet<>();
+
         // Used for initialization of users created by createAndManageUsers.
         boolean mAdminBroadcastPending = false;
         PersistableBundle mInitBundle = null;
@@ -518,6 +528,7 @@
     final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
 
     final Handler mHandler;
+    final Handler mBackgroundHandler;
 
     /** Listens on any device, even when mHasFeature == false. */
     final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
@@ -619,6 +630,25 @@
                 handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
             } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) {
                 clearWipeProfileNotification();
+            } else if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
+                mBackgroundHandler.post(() ->  {
+                    try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
+                            UserHandle.of(userHandle))) {
+                        final List<String> caCerts =
+                                keyChainConnection.getService().getUserCaAliases().getList();
+                        synchronized (DevicePolicyManagerService.this) {
+                            if (getUserData(userHandle).mOwnerInstalledCaCerts
+                                    .retainAll(caCerts)) {
+                                saveSettingsLocked(userHandle);
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                        Slog.w(LOG_TAG, "error talking to IKeyChainService", e);
+                        Thread.currentThread().interrupt();
+                    } catch (RemoteException e) {
+                        Slog.w(LOG_TAG, "error talking to IKeyChainService", e);
+                    }
+                });
             }
         }
 
@@ -1786,6 +1816,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
         mIsWatch = mInjector.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_WATCH);
+        mBackgroundHandler = BackgroundThread.getHandler();
 
         // Broadcast filter for changes to the trusted certificate store. These changes get a
         // separate intent filter so we can listen to them even when device_admin is off.
@@ -1807,6 +1838,7 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
@@ -2580,6 +2612,12 @@
                 out.endTag(null, TAG_DEFAULT_INPUT_METHOD_SET);
             }
 
+            for (final String cert : policy.mOwnerInstalledCaCerts) {
+                out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT);
+                out.attribute(null, ATTR_ALIAS, cert);
+                out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
+            }
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -2696,6 +2734,7 @@
             policy.mAdminList.clear();
             policy.mAdminMap.clear();
             policy.mAffiliationIds.clear();
+            policy.mOwnerInstalledCaCerts.clear();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -2791,6 +2830,8 @@
                             parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DEFAULT_INPUT_METHOD_SET.equals(tag)) {
                     policy.mDefaultInputMethodSet = true;
+                } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
+                    policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -4691,17 +4732,15 @@
             return false;
         }
 
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final UserHandle userHandle = UserHandle.of(mInjector.userHandleGetCallingUserId());
         final long id = mInjector.binderClearCallingIdentity();
+        String alias = null;
         try {
-            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
-            try {
-                keyChainConnection.getService().installCaCertificate(pemCert);
-                return true;
+            try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
+                    userHandle)) {
+                alias = keyChainConnection.getService().installCaCertificate(pemCert);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
-            } finally {
-                keyChainConnection.close();
             }
         } catch (InterruptedException e1) {
             Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
@@ -4709,6 +4748,16 @@
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
+        if (alias == null) {
+            Log.w(LOG_TAG, "Problem installing cert");
+        } else {
+            synchronized (this) {
+                final int userId = userHandle.getIdentifier();
+                getUserData(userId).mOwnerInstalledCaCerts.add(alias);
+                saveSettingsLocked(userId);
+            }
+            return true;
+        }
         return false;
     }
 
@@ -4722,25 +4771,31 @@
     public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) {
         enforceCanManageCaCerts(admin, callerPackage);
 
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final int userId = mInjector.userHandleGetCallingUserId();
+        final UserHandle userHandle = UserHandle.of(userId);
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
-            try {
+            try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
+                    userHandle)) {
                 for (int i = 0 ; i < aliases.length; i++) {
                     keyChainConnection.getService().deleteCaCertificate(aliases[i]);
                 }
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
-            } finally {
-                keyChainConnection.close();
+                return;
             }
         } catch (InterruptedException ie) {
             Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
             Thread.currentThread().interrupt();
+            return;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
+        synchronized (this) {
+            if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) {
+                saveSettingsLocked(userId);
+            }
+        }
     }
 
     @Override
@@ -6731,6 +6786,7 @@
         }
         final DevicePolicyData policyData = getUserData(userId);
         policyData.mDefaultInputMethodSet = false;
+        policyData.mOwnerInstalledCaCerts.clear();
         saveSettingsLocked(userId);
         clearUserPoliciesLocked(userId);
         mOwners.removeProfileOwner(userId);
@@ -7127,29 +7183,32 @@
     }
 
     private void enforceFullCrossUsersPermission(int userHandle) {
-        enforceSystemUserOrPermission(userHandle,
+        enforceSystemUserOrPermissionIfCrossUser(userHandle,
                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
     private void enforceCrossUsersPermission(int userHandle) {
-        enforceSystemUserOrPermission(userHandle,
+        enforceSystemUserOrPermissionIfCrossUser(userHandle,
                 android.Manifest.permission.INTERACT_ACROSS_USERS);
     }
 
-    private void enforceSystemUserOrPermission(int userHandle, String permission) {
-        if (userHandle < 0) {
-            throw new IllegalArgumentException("Invalid userId " + userHandle);
-        }
-        final int callingUid = mInjector.binderGetCallingUid();
-        if (userHandle == UserHandle.getUserId(callingUid)) {
-            return;
-        }
-        if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
+    private void enforceSystemUserOrPermission(String permission) {
+        if (!(isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID)) {
             mContext.enforceCallingOrSelfPermission(permission,
                     "Must be system or have " + permission + " permission");
         }
     }
 
+    private void enforceSystemUserOrPermissionIfCrossUser(int userHandle, String permission) {
+        if (userHandle < 0) {
+            throw new IllegalArgumentException("Invalid userId " + userHandle);
+        }
+        if (userHandle == mInjector.userHandleGetCallingUserId()) {
+            return;
+        }
+        enforceSystemUserOrPermission(permission);
+    }
+
     private void enforceManagedProfile(int userHandle, String message) {
         if(!isManagedProfile(userHandle)) {
             throw new SecurityException("You can not " + message + " outside a managed profile.");
@@ -7184,6 +7243,21 @@
                 "Only profile owner, device owner and system may call this method.");
     }
 
+    private void enforceProfileOwnerOrFullCrossUsersPermission(int userId) {
+        if (userId == mInjector.userHandleGetCallingUserId()) {
+            synchronized (this) {
+                if (getActiveAdminWithPolicyForUidLocked(null,
+                        DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
+                                != null) {
+                    // Device Owner/Profile Owner may access the user it runs on.
+                    return;
+                }
+            }
+        }
+        // Otherwise, INTERACT_ACROSS_USERS_FULL permission, system UID or root UID is required.
+        enforceSystemUserOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    }
+
     private void ensureCallerPackage(@Nullable String packageName) {
         if (packageName == null) {
             Preconditions.checkState(isCallerWithSystemUid(),
@@ -10897,4 +10971,14 @@
         }
         return getUserData(userId).mDefaultInputMethodSet;
     }
+
+    @Override
+    public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) {
+        final int userId = user.getIdentifier();
+        enforceProfileOwnerOrFullCrossUsersPermission(userId);
+        synchronized (this) {
+            return new StringParceledListSlice(
+                    new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts));
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 756514b..f797f31 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -49,6 +49,8 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
 import android.telephony.TelephonyManager;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -122,6 +124,34 @@
     public DevicePolicyManager dpm;
     public DevicePolicyManagerServiceTestable dpms;
 
+    /*
+     * The CA cert below is the content of cacert.pem as generated by:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     */
+    private static final String TEST_CA =
+            "-----BEGIN CERTIFICATE-----\n" +
+            "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
+            "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
+            "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
+            "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
+            "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
+            "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
+            "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
+            "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
+            "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
+            "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
+            "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
+            "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
+            "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
+            "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
+            "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
+            "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
+            "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
+            "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
+            "wQ==\n" +
+            "-----END CERTIFICATE-----\n";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -3916,6 +3946,124 @@
         assertFalse(dpm.isDefaultInputMethodSetByOwner(secondUser));
     }
 
+    public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception {
+        setDeviceOwner();
+
+        mContext.packageName = admin1.getPackageName();
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        verifyCanGetOwnerInstalledCaCerts(admin1);
+    }
+
+    public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception {
+        setAsProfileOwner(admin1);
+
+        mContext.packageName = admin1.getPackageName();
+        verifyCanGetOwnerInstalledCaCerts(admin1);
+        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1);
+    }
+
+    public void testGetOwnerInstalledCaCertsForDelegate() throws Exception {
+        setAsProfileOwner(admin1);
+
+        final String delegate = "com.example.delegate";
+        final int delegateUid = setupPackageInPackageManager(delegate, 20988);
+        dpm.setCertInstallerPackage(admin1, delegate);
+
+        mContext.packageName = delegate;
+        mContext.binder.callingUid = delegateUid;
+        verifyCanGetOwnerInstalledCaCerts(null);
+        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null);
+    }
+
+    private void verifyCanGetOwnerInstalledCaCerts(ComponentName caller) throws Exception {
+        final UserHandle user = UserHandle.getUserHandleForUid(mContext.binder.callingUid);
+        final int ownerUid = user.equals(UserHandle.SYSTEM) ?
+                DpmMockContext.CALLER_SYSTEM_USER_UID : DpmMockContext.CALLER_UID;
+
+        mContext.applicationInfo = new ApplicationInfo();
+        mContext.userContexts.put(user, mContext);
+        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        // Install a CA cert.
+        final String alias = "cert";
+        final byte[] caCert = TEST_CA.getBytes();
+        when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+                .thenReturn(alias);
+        assertTrue(dpm.installCaCert(caller, caCert));
+        when(mContext.keyChainConnection.getService().getUserCaAliases())
+                .thenReturn(asSlice(new String[] {alias}));
+        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        flushTasks();
+
+        // Device Owner / Profile Owner can find out which CA certs were installed by itself.
+        final String packageName = mContext.packageName;
+        mContext.packageName = admin1.getPackageName();
+        final long callerIdentity = mContext.binder.clearCallingIdentity();
+        mContext.binder.callingUid = ownerUid;
+        List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
+        assertNotNull(ownerInstalledCaCerts);
+        assertEquals(1, ownerInstalledCaCerts.size());
+        assertTrue(ownerInstalledCaCerts.contains(alias));
+
+        // Restarting the DPMS should not lose information.
+        initializeDpms();
+        assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+
+        // System can find out which CA certs were installed by the Device Owner / Profile Owner.
+        mContext.packageName = "com.android.frameworks.servicestests";
+        mContext.binder.clearCallingIdentity();
+        assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+
+        // Remove the CA cert.
+        mContext.packageName = packageName;
+        mContext.binder.restoreCallingIdentity(callerIdentity);
+        reset(mContext.keyChainConnection.getService());
+        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        flushTasks();
+
+        // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
+        // Owner.
+        mContext.packageName = admin1.getPackageName();
+        mContext.binder.callingUid = ownerUid;
+        ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
+        assertNotNull(ownerInstalledCaCerts);
+        assertTrue(ownerInstalledCaCerts.isEmpty());
+
+        mContext.packageName = packageName;
+        mContext.binder.restoreCallingIdentity(callerIdentity);
+    }
+
+    private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(ComponentName caller)
+            throws Exception {
+        final UserHandle user = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
+
+        mContext.applicationInfo = new ApplicationInfo();
+        mContext.userContexts.put(user, mContext);
+        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        // Install a CA cert.
+        final String alias = "cert";
+        final byte[] caCert = TEST_CA.getBytes();
+        when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+                .thenReturn(alias);
+        assertTrue(dpm.installCaCert(caller, caCert));
+        when(mContext.keyChainConnection.getService().getUserCaAliases())
+                .thenReturn(asSlice(new String[] {alias}));
+        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        flushTasks();
+
+        // Removing the Profile Owner should clear the information which CA certs were installed
+        // by it.
+        mContext.packageName = admin1.getPackageName();
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        dpm.clearProfileOwner(admin1);
+        mContext.packageName = "com.android.frameworks.servicestests";
+        mContext.binder.clearCallingIdentity();
+        final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
+        assertNotNull(ownerInstalledCaCerts);
+        assertTrue(ownerInstalledCaCerts.isEmpty());
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
@@ -3978,4 +4126,31 @@
     private static StringParceledListSlice asSlice(String[] s) {
         return new StringParceledListSlice(Arrays.asList(s));
     }
+
+    private void flushTasks() throws Exception {
+        Boolean tasksFlushed[] = new Boolean[] {false};
+        final Runnable tasksFlushedNotifier = () -> {
+            synchronized (tasksFlushed) {
+                tasksFlushed[0] = true;
+                tasksFlushed.notify();
+            }
+        };
+
+        // Flush main thread handler.
+        dpms.mHandler.post(tasksFlushedNotifier);
+        synchronized (tasksFlushed) {
+            if (!tasksFlushed[0]) {
+                tasksFlushed.wait();
+            }
+        }
+
+        // Flush background thread handler.
+        tasksFlushed[0] = false;
+        dpms.mBackgroundHandler.post(tasksFlushedNotifier);
+        synchronized (tasksFlushed) {
+            if (!tasksFlushed[0]) {
+                tasksFlushed.wait();
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 258b393..7d017c5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -316,6 +316,41 @@
 
     public ApplicationInfo applicationInfo = null;
 
+    // We have to keep track of broadcast receivers registered for a given intent ourselves as the
+    // DPM unit tests mock out the package manager and PackageManager.queryBroadcastReceivers() does
+    // not work.
+    private class BroadcastReceiverRegistration {
+        public final BroadcastReceiver receiver;
+        public final IntentFilter filter;
+        public final Handler scheduler;
+
+        public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter,
+                Handler scheduler) {
+            this.receiver = receiver;
+            this.filter = filter;
+            this.scheduler = scheduler;
+        }
+
+        public void sendBroadcastIfApplicable(int userId, Intent intent) {
+            final BroadcastReceiver.PendingResult result = new BroadcastReceiver.PendingResult(
+                    0 /* resultCode */, null /* resultData */, null /* resultExtras */,
+                    0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId,
+                    0 /* flags */);
+            if (filter.match(null, intent, false, "DpmMockContext") > 0) {
+                if (scheduler != null) {
+                    scheduler.post(() -> {
+                        receiver.setPendingResult(result);
+                        receiver.onReceive(DpmMockContext.this, intent);
+                    });
+                } else {
+                    receiver.setPendingResult(result);
+                    receiver.onReceive(DpmMockContext.this, intent);
+                }
+            }
+        }
+    }
+    private List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>();
+
     public DpmMockContext(Context context, File dataDir) {
         realTestContext = context;
 
@@ -476,6 +511,13 @@
                 .thenReturn(isRunning);
     }
 
+    public void injectBroadcast(Intent intent) {
+        final int userId = UserHandle.getUserId(binder.getCallingUid());
+        for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) {
+            receiver.sendBroadcastIfApplicable(userId, intent);
+        }
+    }
+
     @Override
     public Resources getResources() {
         return resources;
@@ -681,24 +723,28 @@
 
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, null));
         return spiedContext.registerReceiver(receiver, filter);
     }
 
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
             String broadcastPermission, Handler scheduler) {
+        mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, scheduler));
         return spiedContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
     }
 
     @Override
     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
             IntentFilter filter, String broadcastPermission, Handler scheduler) {
+        mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, scheduler));
         return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
                 scheduler);
     }
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
+        mBroadcastReceivers.removeIf(r -> r.receiver == receiver);
         spiedContext.unregisterReceiver(receiver);
     }