Merge "Exfiltrate cert code from DevicePolicyManager"
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
new file mode 100644
index 0000000..a2bc195
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
+import android.util.Log;
+
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.R;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Set;
+
+public class CertificateMonitor {
+    protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
+    protected static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
+
+    private final DevicePolicyManagerService mService;
+    private final DevicePolicyManagerService.Injector mInjector;
+    private final Handler mHandler;
+
+    public CertificateMonitor(final DevicePolicyManagerService service,
+            final DevicePolicyManagerService.Injector injector, final Handler handler) {
+        mService = service;
+        mInjector = injector;
+        mHandler = handler;
+
+        // Broadcast filter for changes to the trusted certificate store. Listens on the background
+        // handler to avoid blocking time-critical tasks on the main handler thread.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mInjector.mContext.registerReceiverAsUser(
+                mRootCaReceiver, UserHandle.ALL, filter, null, mHandler);
+    }
+
+    public String installCaCert(final UserHandle userHandle, byte[] certBuffer) {
+        // Convert certificate data from X509 format to PEM.
+        byte[] pemCert;
+        try {
+            X509Certificate cert = parseCert(certBuffer);
+            pemCert = Credentials.convertToPem(cert);
+        } catch (CertificateException | IOException ce) {
+            Log.e(LOG_TAG, "Problem converting cert", ce);
+            return null;
+        }
+
+        try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
+            return keyChainConnection.getService().installCaCertificate(pemCert);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+        } catch (InterruptedException e1) {
+            Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+            Thread.currentThread().interrupt();
+        }
+        return null;
+    }
+
+    public void uninstallCaCerts(final UserHandle userHandle, final String[] aliases) {
+        try (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);
+        } catch (InterruptedException ie) {
+            Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    public List<String> getInstalledCaCertificates(UserHandle userHandle)
+            throws RemoteException, RuntimeException {
+        try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) {
+            return conn.getService().getUserCaAliases().getList();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return null;
+        } catch (AssertionError e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void onCertificateApprovalsChanged(int userId) {
+        mHandler.post(() -> updateInstalledCertificates(UserHandle.of(userId)));
+    }
+
+    /**
+     * Broadcast receiver for changes to the trusted certificate store.
+     */
+    private final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (StorageManager.inCryptKeeperBounce()) {
+                return;
+            }
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+            updateInstalledCertificates(UserHandle.of(userId));
+        }
+    };
+
+    private void updateInstalledCertificates(final UserHandle userHandle) {
+        if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+            return;
+        }
+
+        final List<String> installedCerts;
+        try {
+            installedCerts = getInstalledCaCertificates(userHandle);
+        } catch (RemoteException | RuntimeException e) {
+            Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+            return;
+        }
+        mService.onInstalledCertificatesChanged(userHandle, installedCerts);
+
+        final int pendingCertificateCount =
+                installedCerts.size() - mService.getAcceptedCaCertificates(userHandle).size();
+        if (pendingCertificateCount != 0) {
+            final Notification noti = buildNotification(userHandle, pendingCertificateCount);
+            mInjector.getNotificationManager().notifyAsUser(
+                    LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
+        } else {
+            mInjector.getNotificationManager().cancelAsUser(
+                    LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle);
+        }
+    }
+
+    private Notification buildNotification(UserHandle userHandle, int pendingCertificateCount) {
+        final Context userContext;
+        try {
+            userContext = mInjector.createContextAsUser(userHandle);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+            return null;
+        }
+
+        final Resources resources = mInjector.getResources();
+        final int smallIconId;
+        final String contentText;
+
+        int parentUserId = userHandle.getIdentifier();
+
+        if (mService.getProfileOwner(userHandle.getIdentifier()) != null) {
+            contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
+                    mService.getProfileOwnerName(userHandle.getIdentifier()));
+            smallIconId = R.drawable.stat_sys_certificate_info;
+            parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
+        } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
+            final String ownerName = mService.getDeviceOwnerName();
+            contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
+                    mService.getDeviceOwnerName());
+            smallIconId = R.drawable.stat_sys_certificate_info;
+        } else {
+            contentText = resources.getString(R.string.ssl_ca_cert_noti_by_unknown);
+            smallIconId = android.R.drawable.stat_sys_warning;
+        }
+
+        // Create an intent to launch an activity showing information about the certificate.
+        Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
+        dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount);
+        dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
+
+        // The intent should only be allowed to resolve to a system app.
+        ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
+                mInjector.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
+        if (targetInfo != null) {
+            dialogIntent.setComponent(targetInfo.getComponentName());
+        }
+
+        PendingIntent notifyIntent = mInjector.pendingIntentGetActivityAsUser(userContext, 0,
+                dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
+                UserHandle.of(parentUserId));
+
+        return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY)
+                .setSmallIcon(smallIconId)
+                .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
+                        pendingCertificateCount))
+                .setContentText(contentText)
+                .setContentIntent(notifyIntent)
+                .setShowWhen(false)
+                .setColor(R.color.system_notification_accent_color)
+                .build();
+    }
+
+    private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException {
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(
+                certBuffer));
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 536d454..ecbd312 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -114,7 +114,6 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -184,7 +183,6 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -193,9 +191,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
 import java.text.DateFormat;
 import java.text.NumberFormat;
 import java.util.ArrayList;
@@ -398,6 +393,7 @@
      */
     boolean mIsWatch;
 
+    private final CertificateMonitor mCertificateMonitor;
     private final SecurityLogMonitor mSecurityLogMonitor;
     private NetworkLogger mNetworkLogger;
 
@@ -530,19 +526,6 @@
     final Handler mHandler;
     final Handler mBackgroundHandler;
 
-    /** Listens on any device, even when mHasFeature == false. */
-    final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (StorageManager.inCryptKeeperBounce()) {
-                return;
-            }
-            final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
-            new MonitoringCertNotificationTask(DevicePolicyManagerService.this, mInjector)
-                    .execute(userHandle);
-        }
-    };
-
     /** Listens only if mHasFeature == true. */
     final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -630,25 +613,6 @@
                 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);
-                    }
-                });
             }
         }
 
@@ -1527,7 +1491,7 @@
     @VisibleForTesting
     static class Injector {
 
-        private final Context mContext;
+        public final Context mContext;
 
         Injector(Context context) {
             mContext = context;
@@ -1720,6 +1684,12 @@
             return "/data/system/";
         }
 
+        PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
+                @NonNull Intent intent, int flags, Bundle options, UserHandle user) {
+            return PendingIntent.getActivityAsUser(
+                    context, requestCode, intent, flags, options, user);
+        }
+
         void registerContentObserver(Uri uri, boolean notifyForDescendents,
                 ContentObserver observer, int userHandle) {
             mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents,
@@ -1810,6 +1780,7 @@
         mLocalService = new LocalService();
         mLockPatternUtils = injector.newLockPatternUtils();
 
+        // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false?
         mSecurityLogMonitor = new SecurityLogMonitor(this);
 
         mHasFeature = mInjector.getPackageManager()
@@ -1818,27 +1789,20 @@
                 .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.
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_STARTED);
-        filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        mContext.registerReceiverAsUser(mRootCaReceiver, UserHandle.ALL, filter, null, mHandler);
+        // Needed when mHasFeature == false, because it controls the certificate warning text.
+        mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
 
         if (!mHasFeature) {
             // Skip the rest of the initialization
             return;
         }
 
-        filter = new IntentFilter();
+        IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BOOT_COMPLETED);
         filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
         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();
@@ -3083,33 +3047,43 @@
     }
 
     /**
-     * Remove deleted CA certificates from the "approved" list for a particular user, counting
-     * the number still remaining to approve.
+     * Clean up internal state when the set of installed trusted CA certificates changes.
      *
      * @param userHandle user to check for. This must be a real user and not, for example,
      *        {@link UserHandle#ALL}.
      * @param installedCertificates the full set of certificate authorities currently installed for
      *        {@param userHandle}. After calling this function, {@code mAcceptedCaCertificates} will
      *        correspond to some subset of this.
-     *
-     * @return number of certificates yet to be approved by {@param userHandle}.
      */
-    protected synchronized int retainAcceptedCertificates(final UserHandle userHandle,
+    protected void onInstalledCertificatesChanged(final UserHandle userHandle,
             final @NonNull Collection<String> installedCertificates) {
+        if (!mHasFeature) {
+            return;
+        }
         enforceManageUsers();
 
-        if (!mHasFeature) {
-            return installedCertificates.size();
-        } else {
+        synchronized (this) {
             final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
 
-            // Remove deleted certificates. Flush xml if necessary.
-            if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) {
+            boolean changed = false;
+            changed |= policy.mAcceptedCaCertificates.retainAll(installedCertificates);
+            changed |= policy.mOwnerInstalledCaCerts.retainAll(installedCertificates);
+            if (changed) {
                 saveSettingsLocked(userHandle.getIdentifier());
             }
+        }
+    }
 
-            // Trim approved certificates from the count.
-            return installedCertificates.size() - policy.mAcceptedCaCertificates.size();
+    /**
+     * Internal method used by {@link CertificateMonitor}.
+     */
+    protected Set<String> getAcceptedCaCertificates(final UserHandle userHandle) {
+        if (!mHasFeature) {
+            return Collections.<String> emptySet();
+        }
+        synchronized (this) {
+            final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
+            return policy.mAcceptedCaCertificates;
         }
     }
 
@@ -4690,7 +4664,7 @@
             }
             saveSettingsLocked(userId);
         }
-        new MonitoringCertNotificationTask(this, mInjector).execute(userId);
+        mCertificateMonitor.onCertificateApprovalsChanged(userId);
         return true;
     }
 
@@ -4713,8 +4687,7 @@
                     getUserData(userInfo.id).mAcceptedCaCertificates.clear();
                     saveSettingsLocked(userInfo.id);
                 }
-
-                new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id);
+                mCertificateMonitor.onCertificateApprovalsChanged(userId);
             }
         }
     }
@@ -4722,79 +4695,47 @@
     @Override
     public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer)
             throws RemoteException {
-        enforceCanManageCaCerts(admin, callerPackage);
-
-        byte[] pemCert;
-        try {
-            X509Certificate cert = parseCert(certBuffer);
-            pemCert = Credentials.convertToPem(cert);
-        } catch (CertificateException ce) {
-            Log.e(LOG_TAG, "Problem converting cert", ce);
-            return false;
-        } catch (IOException ioe) {
-            Log.e(LOG_TAG, "Problem reading cert", ioe);
+        if (!mHasFeature) {
             return false;
         }
+        enforceCanManageCaCerts(admin, callerPackage);
 
-        final UserHandle userHandle = UserHandle.of(mInjector.userHandleGetCallingUserId());
+        final String alias;
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
         final long id = mInjector.binderClearCallingIdentity();
-        String alias = null;
         try {
-            try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
-                    userHandle)) {
-                alias = keyChainConnection.getService().installCaCertificate(pemCert);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+            alias = mCertificateMonitor.installCaCert(userHandle, certBuffer);
+            if (alias == null) {
+                Log.w(LOG_TAG, "Problem installing cert");
+                return false;
             }
-        } catch (InterruptedException e1) {
-            Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
-            Thread.currentThread().interrupt();
         } 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;
-    }
 
-    private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException {
-        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
-        return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(
-                certBuffer));
+        synchronized (this) {
+            getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias);
+            saveSettingsLocked(userHandle.getIdentifier());
+        }
+        return true;
     }
 
     @Override
     public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) {
+        if (!mHasFeature) {
+            return;
+        }
         enforceCanManageCaCerts(admin, callerPackage);
 
         final int userId = mInjector.userHandleGetCallingUserId();
-        final UserHandle userHandle = UserHandle.of(userId);
         final long id = mInjector.binderClearCallingIdentity();
         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);
-                return;
-            }
-        } catch (InterruptedException ie) {
-            Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
-            Thread.currentThread().interrupt();
-            return;
+            mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases);
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
+
         synchronized (this) {
             if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) {
                 saveSettingsLocked(userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java b/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java
deleted file mode 100644
index 1933fe7..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.security.KeyChain.KeyChainConnection;
-import android.util.Log;
-
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
-    protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
-    protected static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
-
-    private final DevicePolicyManagerService mService;
-    private final DevicePolicyManagerService.Injector mInjector;
-
-    public MonitoringCertNotificationTask(final DevicePolicyManagerService service,
-            final DevicePolicyManagerService.Injector injector) {
-        super();
-        mService = service;
-        mInjector = injector;
-    }
-
-    @Override
-    protected Void doInBackground(Integer... params) {
-        int userHandle = params[0];
-
-        if (userHandle == UserHandle.USER_ALL) {
-            for (UserInfo userInfo : mInjector.getUserManager().getUsers(true)) {
-                repostOrClearNotification(userInfo.getUserHandle());
-            }
-        } else {
-            repostOrClearNotification(UserHandle.of(userHandle));
-        }
-        return null;
-    }
-
-    private void repostOrClearNotification(UserHandle userHandle) {
-        if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
-            return;
-        }
-
-        // Call out to KeyChain to check for CAs which are waiting for approval.
-        final int pendingCertificateCount;
-        try {
-            pendingCertificateCount = mService.retainAcceptedCertificates(
-                    userHandle, getInstalledCaCertificates(userHandle));
-        } catch (RemoteException | RuntimeException e) {
-            Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
-            return;
-        }
-
-        if (pendingCertificateCount != 0) {
-            showNotification(userHandle, pendingCertificateCount);
-        } else {
-            mInjector.getNotificationManager().cancelAsUser(
-                    LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle);
-        }
-    }
-
-    private void showNotification(UserHandle userHandle, int pendingCertificateCount) {
-        // Create a context for the target user.
-        final Context userContext;
-        try {
-            userContext = mInjector.createContextAsUser(userHandle);
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
-            return;
-        }
-
-        // Build and show a warning notification
-        int smallIconId;
-        String contentText;
-        int parentUserId = userHandle.getIdentifier();
-        Resources resources = mInjector.getResources();
-        if (mService.getProfileOwner(userHandle.getIdentifier()) != null) {
-            contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
-                    mService.getProfileOwnerName(userHandle.getIdentifier()));
-            smallIconId = R.drawable.stat_sys_certificate_info;
-            parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
-        } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
-            contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
-                    mService.getDeviceOwnerName());
-            smallIconId = R.drawable.stat_sys_certificate_info;
-        } else {
-            contentText = resources.getString(R.string.ssl_ca_cert_noti_by_unknown);
-            smallIconId = android.R.drawable.stat_sys_warning;
-        }
-
-        Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
-        dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        // TODO this next line is taken from original notification code in
-        // {@link DevicePolicyManagerService} but not a very good way of doing it. Do it better.
-        dialogIntent.setPackage("com.android.settings");
-        dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount);
-        dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
-        PendingIntent notifyIntent = PendingIntent.getActivityAsUser(userContext, 0,
-                dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
-                UserHandle.of(parentUserId));
-
-        final Notification noti =
-                new Notification.Builder(userContext, SystemNotificationChannels.SECURITY)
-                        .setSmallIcon(smallIconId)
-                        .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
-                                pendingCertificateCount))
-                        .setContentText(contentText)
-                        .setContentIntent(notifyIntent)
-                        .setShowWhen(false)
-                        .setColor(R.color.system_notification_accent_color)
-                        .build();
-
-        mInjector.getNotificationManager().notifyAsUser(
-                LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
-    }
-
-    private List<String> getInstalledCaCertificates(UserHandle userHandle)
-            throws RemoteException, RuntimeException {
-        try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) {
-            return conn.getService().getUserCaAliases().getList();
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            return null;
-        } catch (AssertionError e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index ca9285b..a6ce1d5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -17,13 +17,18 @@
 
 import android.app.IActivityManager;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
 import android.media.IAudioService;
 import android.net.IIpConnectivityMetrics;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Looper;
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
@@ -302,6 +307,12 @@
         }
 
         @Override
+        PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
+                Intent intent, int flags, Bundle options, UserHandle user) {
+            return null;
+        }
+
+        @Override
         void registerContentObserver(Uri uri, boolean notifyForDescendents,
                 ContentObserver observer, int userHandle) {
             mContentObservers.put(new Pair<Uri, Integer>(uri, userHandle), observer);