Add ability to install credentials as other UID

We need the ability to install from the system UID to wifi UID
to explicitly bind WiFi credentials to the WiFi profile. This adds the
ability for Wifi Settings to invoke installation of a PKCS12 file for
the wifi UID.

Bug: 8183258
Change-Id: I26970e563d68311b60dcdc78cd529322c5807368
diff --git a/src/com/android/certinstaller/CertFile.java b/src/com/android/certinstaller/CertFile.java
index 401c1a3..5b4bfbf 100644
--- a/src/com/android/certinstaller/CertFile.java
+++ b/src/com/android/certinstaller/CertFile.java
@@ -131,9 +131,17 @@
 
         String fileName = file.getName();
         Bundle bundle = getIntent().getExtras();
-        String name = ((bundle == null)
-                       ? fileName
-                       : bundle.getString(KeyChain.EXTRA_NAME, fileName));
+
+        final String name;
+        final int installAsUid;
+        if (bundle == null) {
+            name = fileName;
+            installAsUid = -1;
+        } else {
+            name = bundle.getString(KeyChain.EXTRA_NAME, fileName);
+            installAsUid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+        }
+
         if (file.exists()) {
             if (file.length() < MAX_FILE_SIZE) {
                 byte[] data = Util.readFile(file);
@@ -143,7 +151,7 @@
                     return;
                 }
                 mCertFile = file;
-                install(fileName, name, data);
+                install(fileName, name, installAsUid, data);
             } else {
                 Log.w(TAG, "cert file is too large: " + file.length());
                 toastError(CERT_TOO_LARGE_ERROR);
@@ -176,9 +184,10 @@
                 Environment.MEDIA_MOUNTED);
     }
 
-    private void install(String fileName, String name, byte[] value) {
+    private void install(String fileName, String name, int uid, byte[] value) {
         Intent intent = new Intent(this, CertInstaller.class);
         intent.putExtra(KeyChain.EXTRA_NAME, name);
+        intent.putExtra(Credentials.EXTRA_INSTALL_AS_UID, uid);
         if (fileName.endsWith(Credentials.EXTENSION_PFX)
                 || fileName.endsWith(Credentials.EXTENSION_P12)) {
             intent.putExtra(KeyChain.EXTRA_PKCS12, value);
diff --git a/src/com/android/certinstaller/CertFileList.java b/src/com/android/certinstaller/CertFileList.java
index 5e2b681..1d32c26 100644
--- a/src/com/android/certinstaller/CertFileList.java
+++ b/src/com/android/certinstaller/CertFileList.java
@@ -21,6 +21,7 @@
 import android.os.FileObserver;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
+import android.security.Credentials;
 import android.util.Log;
 import android.widget.Toast;
 
diff --git a/src/com/android/certinstaller/CertInstallerMain.java b/src/com/android/certinstaller/CertInstallerMain.java
index 7d7ed6e..9b10c07 100644
--- a/src/com/android/certinstaller/CertInstallerMain.java
+++ b/src/com/android/certinstaller/CertInstallerMain.java
@@ -60,14 +60,28 @@
         Intent intent = getIntent();
         String action = (intent == null) ? null : intent.getAction();
 
-        if (Credentials.INSTALL_ACTION.equals(action)) {
+        if (Credentials.INSTALL_ACTION.equals(action)
+                || Credentials.INSTALL_AS_USER_ACTION.equals(action)) {
             Bundle bundle = intent.getExtras();
+
+            /*
+             * There is a special INSTALL_AS_USER action that this activity is
+             * aliased to, but you have to have a permission to call it. If the
+             * caller got here any other way, remove the extra that we allow in
+             * that INSTALL_AS_USER path.
+             */
+            if (bundle != null && !Credentials.INSTALL_AS_USER_ACTION.equals(action)) {
+                bundle.remove(Credentials.EXTRA_INSTALL_AS_UID);
+            }
+
             // If bundle is empty of any actual credentials, install from external storage.
             // Otherwise, pass extras to CertInstaller to install those credentials.
             // Either way, we use KeyChain.EXTRA_NAME as the default name if available.
             if (bundle == null
                     || bundle.isEmpty()
-                    || (bundle.size() == 1 && bundle.containsKey(KeyChain.EXTRA_NAME))) {
+                    || (bundle.size() == 1
+                        && (bundle.containsKey(KeyChain.EXTRA_NAME)
+                            || bundle.containsKey(Credentials.EXTRA_INSTALL_AS_UID)))) {
                 if (!isSdCardPresent()) {
                     Toast.makeText(this, R.string.sdcard_not_present,
                             Toast.LENGTH_SHORT).show();
diff --git a/src/com/android/certinstaller/CredentialHelper.java b/src/com/android/certinstaller/CredentialHelper.java
index f9c35eb..5f59387 100644
--- a/src/com/android/certinstaller/CredentialHelper.java
+++ b/src/com/android/certinstaller/CredentialHelper.java
@@ -64,6 +64,7 @@
     private HashMap<String, byte[]> mBundle = new HashMap<String, byte[]>();
 
     private String mName = "";
+    private int mUid = -1;
     private PrivateKey mUserKey;
     private X509Certificate mUserCert;
     private List<X509Certificate> mCaCerts = new ArrayList<X509Certificate>();
@@ -83,6 +84,9 @@
             mName = name;
         }
 
+        mUid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+        bundle.remove(Credentials.EXTRA_INSTALL_AS_UID);
+
         Log.d(TAG, "# extras: " + bundle.size());
         for (String key : bundle.keySet()) {
             byte[] bytes = bundle.getByteArray(key);
@@ -249,6 +253,7 @@
         // To prevent the private key from being sniffed, we explicitly spell
         // out the intent receiver class.
         intent.setClassName("com.android.settings", "com.android.settings.CredentialStorage");
+        intent.putExtra(Credentials.EXTRA_INSTALL_AS_UID, mUid);
         try {
             if (mUserKey != null) {
                 intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_NAME,