Publish DevicePolicyManager CA certificate APIs

Exposes these methods:
 - hasCaCertInstalled
 - hasAnyCaCertsInstalled
 - installCaCert
 - uninstallCaCert

Allows device and profile owners to perform some certificate management
including querying for and enabling/disabling specific CA certificates.

Change-Id: I4aa8a1a8601b234e30acde99dfa382e04cb62495
diff --git a/api/current.txt b/api/current.txt
index 474d5b2..cfb23d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5214,7 +5214,10 @@
     method public int getPasswordQuality(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
+    method public boolean hasAnyCaCertsInstalled();
+    method public boolean hasCaCertInstalled(byte[]);
     method public boolean hasGrantedPolicy(android.content.ComponentName, int);
+    method public boolean installCaCert(android.content.ComponentName, byte[]);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
@@ -5252,6 +5255,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
+    method public void uninstallCaCert(android.content.ComponentName, byte[]);
     method public void wipeData(int);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 04be028..1f0ced0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1506,12 +1506,11 @@
      *
      * @return false if the certBuffer cannot be parsed or installation is
      *         interrupted, otherwise true
-     * @hide
      */
-    public boolean installCaCert(byte[] certBuffer) {
+    public boolean installCaCert(ComponentName who, byte[] certBuffer) {
         if (mService != null) {
             try {
-                return mService.installCaCert(certBuffer);
+                return mService.installCaCert(who, certBuffer);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1521,13 +1520,14 @@
 
     /**
      * Uninstalls the given certificate from the list of User CAs, if present.
-     *
-     * @hide
      */
-    public void uninstallCaCert(byte[] certBuffer) {
+    public void uninstallCaCert(ComponentName who, byte[] certBuffer) {
         if (mService != null) {
             try {
-                mService.uninstallCaCert(certBuffer);
+                final String alias = getCaCertAlias(certBuffer);
+                mService.uninstallCaCert(who, alias);
+            } catch (CertificateException e) {
+                Log.w(TAG, "Unable to parse certificate", e);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1536,10 +1536,8 @@
 
     /**
      * Returns whether there are any user-installed CA certificates.
-     *
-     * @hide
      */
-    public static boolean hasAnyCaCertsInstalled() {
+    public boolean hasAnyCaCertsInstalled() {
         TrustedCertificateStore certStore = new TrustedCertificateStore();
         Set<String> aliases = certStore.userAliases();
         return aliases != null && !aliases.isEmpty();
@@ -1547,18 +1545,10 @@
 
     /**
      * Returns whether this certificate has been installed as a User CA.
-     *
-     * @hide
      */
     public boolean hasCaCertInstalled(byte[] certBuffer) {
-        TrustedCertificateStore certStore = new TrustedCertificateStore();
-        String alias;
-        byte[] pemCert;
         try {
-            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
-            X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
-                            new ByteArrayInputStream(certBuffer));
-            return certStore.getCertificateAlias(cert) != null;
+            return getCaCertAlias(certBuffer) != null;
         } catch (CertificateException ce) {
             Log.w(TAG, "Could not parse certificate", ce);
         }
@@ -1566,6 +1556,17 @@
     }
 
     /**
+     * Returns the alias of a given CA certificate in the certificate store, or null if it
+     * doesn't exist.
+     */
+    private static String getCaCertAlias(byte[] certBuffer) throws CertificateException {
+        final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        final X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
+                              new ByteArrayInputStream(certBuffer));
+        return new TrustedCertificateStore().getCertificateAlias(cert);
+    }
+
+    /**
      * Called by an application that is administering the device to disable all cameras
      * on the device.  After setting this, no applications will be able to access any cameras
      * on the device.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f8df780..b25341d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -115,8 +115,8 @@
     String getProfileOwnerName(int userHandle);
     void setProfileEnabled(in ComponentName who);
 
-    boolean installCaCert(in byte[] certBuffer);
-    void uninstallCaCert(in byte[] certBuffer);
+    boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
+    void uninstallCaCert(in ComponentName admin, in String alias);
 
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
     void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 9d6d76e..0da2b99 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -23,7 +23,9 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.security.InvalidKeyException;
@@ -437,6 +439,14 @@
      * Caller should call unbindService on the result when finished.
      */
     public static KeyChainConnection bind(Context context) throws InterruptedException {
+        return bindAsUser(context, Process.myUserHandle());
+    }
+
+    /**
+     * @hide
+     */
+    public static KeyChainConnection bindAsUser(Context context, UserHandle user)
+            throws InterruptedException {
         if (context == null) {
             throw new NullPointerException("context == null");
         }
@@ -459,9 +469,10 @@
         Intent intent = new Intent(IKeyChainService.class.getName());
         ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
         intent.setComponent(comp);
-        boolean isBound = context.bindService(intent,
-                                              keyChainServiceConnection,
-                                              Context.BIND_AUTO_CREATE);
+        boolean isBound = context.bindServiceAsUser(intent,
+                                                    keyChainServiceConnection,
+                                                    Context.BIND_AUTO_CREATE,
+                                                    user);
         if (!isBound) {
             throw new AssertionError("could not bind to KeyChainService");
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5cfe0f1..32aa91b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1307,7 +1307,7 @@
     private void manageMonitoringCertificateNotification(Intent intent) {
         final NotificationManager notificationManager = getNotificationManager();
 
-        final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
+        final boolean hasCert = !(new TrustedCertificateStore().userAliases().isEmpty());
         if (! hasCert) {
             if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
                 for (UserInfo user : mUserManager.getUsers()) {
@@ -2361,13 +2361,19 @@
         return !"".equals(state);
     }
 
-    public boolean installCaCert(byte[] certBuffer) throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
-        KeyChainConnection keyChainConnection = null;
+    public boolean installCaCert(ComponentName who, byte[] certBuffer) throws RemoteException {
+        if (who == null) {
+            mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
+        } else {
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            }
+        }
+
         byte[] pemCert;
         try {
             X509Certificate cert = parseCert(certBuffer);
-            pemCert =  Credentials.convertToPem(cert);
+            pemCert = Credentials.convertToPem(cert);
         } catch (CertificateException ce) {
             Log.e(LOG_TAG, "Problem converting cert", ce);
             return false;
@@ -2375,20 +2381,24 @@
             Log.e(LOG_TAG, "Problem reading cert", ioe);
             return false;
         }
+
+        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final long id = Binder.clearCallingIdentity();
         try {
-            keyChainConnection = KeyChain.bind(mContext);
+            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
             try {
                 keyChainConnection.getService().installCaCertificate(pemCert);
                 return true;
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
             } finally {
-                if (keyChainConnection != null) {
-                    keyChainConnection.close();
-                    keyChainConnection = null;
-                }
+                keyChainConnection.close();
             }
         } catch (InterruptedException e1) {
             Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
             Thread.currentThread().interrupt();
+        } finally {
+            Binder.restoreCallingIdentity(id);
         }
         return false;
     }
@@ -2400,34 +2410,31 @@
                 certBuffer));
     }
 
-    public void uninstallCaCert(final byte[] certBuffer) {
-        mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
-        TrustedCertificateStore certStore = new TrustedCertificateStore();
-        String alias = null;
-        try {
-            X509Certificate cert = parseCert(certBuffer);
-            alias = certStore.getCertificateAlias(cert);
-        } catch (CertificateException ce) {
-            Log.e(LOG_TAG, "Problem creating X509Certificate", ce);
-            return;
-        } catch (IOException ioe) {
-            Log.e(LOG_TAG, "Problem reading certificate", ioe);
-            return;
+    public void uninstallCaCert(ComponentName who, String alias) {
+        if (who == null) {
+            mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
+        } else {
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            }
         }
+
+        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final long id = Binder.clearCallingIdentity();
         try {
-            KeyChainConnection keyChainConnection = KeyChain.bind(mContext);
-            IKeyChainService service = keyChainConnection.getService();
+            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
             try {
-                service.deleteCaCertificate(alias);
+                keyChainConnection.getService().deleteCaCertificate(alias);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
             } finally {
                 keyChainConnection.close();
-                keyChainConnection = null;
             }
         } catch (InterruptedException ie) {
             Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
             Thread.currentThread().interrupt();
+        } finally {
+            Binder.restoreCallingIdentity(id);
         }
     }