Merge "Add ability to install credentials as other UID"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 031cc72..b2880d6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -5,6 +5,9 @@
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
+    <permission android:name="com.android.certinstaller.INSTALL_AS_USER"
+                android:protectionLevel="signature" />
+
     <application android:label="@string/app_name"
                  android:allowBackup="false">
         <activity android:name=".CertInstallerMain"
@@ -23,6 +26,15 @@
             </intent-filter>
         </activity>
 
+        <activity-alias android:name=".InstallCertAsUser"
+                        android:targetActivity=".CertInstallerMain"
+                        android:permission="com.android.certinstaller.INSTALL_AS_USER">
+            <intent-filter>
+                <action android:name="android.credentials.INSTALL_AS_USER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
+
         <activity android:name=".CertInstaller"
                   android:theme="@style/Transparent"
                   android:configChanges="orientation|keyboardHidden"
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,