Merge "Use device policy suggestions when choosing alias"
diff --git a/src/com/android/keychain/KeyChainActivity.java b/src/com/android/keychain/KeyChainActivity.java
index 23aa1a1..6b876c4 100644
--- a/src/com/android/keychain/KeyChainActivity.java
+++ b/src/com/android/keychain/KeyChainActivity.java
@@ -17,15 +17,20 @@
 package com.android.keychain;
 
 import android.app.Activity;
+import android.app.admin.IDevicePolicyManager;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.security.Credentials;
 import android.security.IKeyChainAliasCallback;
 import android.security.KeyChain;
@@ -49,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.concurrent.ExecutionException;
 import java.util.List;
 
 import javax.security.auth.x500.X500Principal;
@@ -118,7 +124,7 @@
                     // onActivityResult is called with REQUEST_UNLOCK
                     return;
                 }
-                showCertChooserDialog();
+                chooseCertificate();
                 return;
             case UNLOCK_REQUESTED:
                 // we've already asked, but have not heard back, probably just rotated.
@@ -134,8 +140,59 @@
         }
     }
 
-    private void showCertChooserDialog() {
-        new AliasLoader().execute();
+    private void chooseCertificate() {
+        // Start loading the set of certs to choose from now- if device policy doesn't return an
+        // alias, having aliases loading already will save some time waiting for UI to start.
+        final AliasLoader loader = new AliasLoader();
+        loader.execute();
+
+        final IKeyChainAliasCallback.Stub callback = new IKeyChainAliasCallback.Stub() {
+            @Override public void alias(String alias) {
+                // Use policy-suggested alias if provided
+                if (alias != null) {
+                    finish(alias);
+                    return;
+                }
+
+                // No suggested alias - instead finish loading and show UI to pick one
+                final CertificateAdapter certAdapter;
+                try {
+                    certAdapter = loader.get();
+                } catch (InterruptedException | ExecutionException e) {
+                    Log.e(TAG, "Loading certificate aliases interrupted", e);
+                    finish(null);
+                    return;
+                }
+                runOnUiThread(new Runnable() {
+                    @Override public void run() {
+                        displayCertChooserDialog(certAdapter);
+                    }
+                });
+            }
+        };
+
+        // Give a profile or device owner the chance to intercept the request, if a private key
+        // access listener is registered with the DevicePolicyManagerService.
+        IDevicePolicyManager devicePolicyManager = IDevicePolicyManager.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+
+        String host = getIntent().getStringExtra(KeyChain.EXTRA_HOST);
+        int port = getIntent().getIntExtra(KeyChain.EXTRA_PORT, -1);
+        String url = getIntent().getStringExtra(KeyChain.EXTRA_URL);
+        String alias = getIntent().getStringExtra(KeyChain.EXTRA_ALIAS);
+
+        try {
+            devicePolicyManager.choosePrivateKeyAlias(host, port, url, alias, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not bind to DevicePolicyManagerService", e);
+
+            // Proceed without a suggested alias.
+            try {
+                callback.alias(null);
+            } catch (RemoteException shouldNeverHappen) {
+                finish(null);
+            }
+        }
     }
 
     private class AliasLoader extends AsyncTask<Void, Void, CertificateAdapter> {
@@ -147,9 +204,6 @@
             Collections.sort(aliasList);
             return new CertificateAdapter(aliasList);
         }
-        @Override protected void onPostExecute(CertificateAdapter adapter) {
-            displayCertChooserDialog(adapter);
-        }
     }
 
     private void displayCertChooserDialog(final CertificateAdapter adapter) {
@@ -363,7 +417,7 @@
             case REQUEST_UNLOCK:
                 if (mKeyStore.isUnlocked()) {
                     mState = State.INITIAL;
-                    showCertChooserDialog();
+                    chooseCertificate();
                 } else {
                     // user must have canceled unlock, give up
                     mState = State.UNLOCK_CANCELED;