[DO NOT MERGE] Rollup changes from R

This change includes the following commits from internal R branch:

7a7578a Remove config_auto_cert_approval from CertInstaller
313c05a Unify manual and programmatic key installation flows
91a7c21 Added functionality to select type of certificate to be installed from the Settings app
2e6c20e Confirm credential after warning dialog when installing CA certificate
ffac486 Separated flow when installing different certificate types
2138394 Created dialogs when installing certificates outside Settings
d5aa3c3 Added invalid certificate dialog
adecd13 Updated WiFi certificate flow
5d14580 Add an exported flag in manifest
10d9c4f Fix strings for certificate installation in CertInstaller
8df978d Modify name certificate dialog
9cdfff2 Install WiFi certificate from Files app
c43d233 [WifiInstaller] Remove Passpoint profile before installing
2411689 Prevent guest user from installing credentials
a9ec06a Update CertInstaller message to include app name.

Bug: 161347472
Test: builds & manual testing
Change-Id: I01a48a7d91c40a83db1f21b29c0d8df2724604c7
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7d543fb..886b7ca 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -38,6 +38,7 @@
 
         <activity-alias android:name=".InstallCertAsUser"
              android:targetActivity=".CertInstallerMain"
+             android:exported="true"
              android:permission="com.android.certinstaller.INSTALL_AS_USER">
             <intent-filter>
                 <action android:name="android.credentials.INSTALL_AS_USER"/>
diff --git a/res/layout/name_certificate_dialog.xml b/res/layout/name_certificate_dialog.xml
new file mode 100644
index 0000000..baa15d9
--- /dev/null
+++ b/res/layout/name_certificate_dialog.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="16dp">
+
+        <TextView android:id="@+id/error"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:textColor="@color/red"
+                  android:textStyle="bold"
+                  android:visibility="gone" />
+
+        <TextView android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:text="@string/certificate_name"
+                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textColor="?android:attr/colorAccent"/>
+
+        <EditText android:id="@+id/certificate_name"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  style="@style/dialog_edit_text"/>
+
+    </LinearLayout>
+</ScrollView>
diff --git a/res/layout/name_credential_dialog.xml b/res/layout/name_credential_dialog.xml
deleted file mode 100644
index 6dd46ff..0000000
--- a/res/layout/name_credential_dialog.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-    <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:padding="15dip">
-
-        <TextView android:id="@+id/error"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:textColor="@color/red"
-                android:textStyle="bold"
-                android:visibility="gone" />
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/credential_name"
-            android:textAppearance="?android:attr/textAppearanceMedium" />
-
-        <EditText android:id="@+id/credential_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="True"/>
-
-        <LinearLayout
-            android:id="@+id/credential_usage_group"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical" >
-
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="12dp"
-                android:text="@string/credential_usage_label"
-                android:textAppearance="?android:attr/textAppearanceMedium" />
-
-            <Spinner
-                android:id="@+id/credential_usage"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:entries="@array/credential_usage" />
-
-            <TextView
-                android:id="@+id/credential_capabilities_warning"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="12dp"
-                android:text="@string/certificate_capabilities_warning"
-                android:textColor="@color/red" />
-
-        </LinearLayout>
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingTop="12dp"
-            android:text="@string/credential_info" />
-
-        <TextView android:id="@+id/credential_info"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-
-    </LinearLayout>
-</ScrollView>
diff --git a/res/layout/select_certificate_usage_dialog.xml b/res/layout/select_certificate_usage_dialog.xml
new file mode 100644
index 0000000..6a053a4
--- /dev/null
+++ b/res/layout/select_certificate_usage_dialog.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="16dp">
+
+        <LinearLayout
+            android:id="@+id/certificate_usage_group"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/select_certificate_usage_title"
+                style="@style/dialog_title"/>
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1dip"
+                android:background="?android:attr/dividerHorizontal"/>
+
+            <RadioGroup
+                android:id="@+id/certificate_usage"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp">
+
+                <RadioButton
+                    android:id="@+id/user_certificate"
+                    style="@style/dialog_button"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/user_certificate"
+                    android:checked="true"/>
+
+                <RadioButton
+                    android:id="@+id/wifi_certificate"
+                    style="@style/dialog_button"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/wifi_certificate"/>
+
+            </RadioGroup>
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1dip"
+                android:background="?android:attr/dividerHorizontal"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 062c1ce..25f6905 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -15,8 +15,6 @@
 -->
 
 <resources>
-  <bool name="config_auto_cert_approval">true</bool>
-
   <string name="config_system_install_component" translatable="false">com.android.settings/com.android.settings.security.CredentialStorage</string>
 </resources>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 020a014..7cf78ce 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -26,10 +26,10 @@
     <string name="extracting_pkcs12">Extracting\u2026</string>
     <!-- Title of dialog to enter password for pkcs12 file -->
     <string name="pkcs12_file_password_dialog_title">Extract from %s</string>
-    <!-- Title of dialog to name a credential -->
-    <string name="name_credential_dialog_title">Name the certificate</string>
-    <!-- Description for the credential name input box -->
-    <string name="credential_name">Certificate name:</string>
+    <!-- Title of a dialog. This title is asking the user to name the certificate they're installing on their device. -->
+    <string name="name_credential_dialog_title">Name this certificate</string>
+    <!-- Input field in a dialog. This dialog is asking the user to name the certificate they are installing on their device. -->
+    <string name="certificate_name">Certificate name</string>
     <!-- Title of the credential info -->
     <!-- Description for the credential password input box -->
     <string name="credential_password">Type the password to extract the certificates.</string>
@@ -58,14 +58,49 @@
     <string name="action_missing_private_key">Private key required to install a certificate</string>
     <string name="action_missing_user_cert">Certificate required to install a private key</string>
 
-    <!-- toast message -->
-    <string name="cert_is_added"><xliff:g id="credential">%s</xliff:g> is installed.</string>
+    <!-- Title of dialog to select the certificate usage when installing a certificate outside Settings -->
+    <string name="select_certificate_usage_title">Choose a certificate type</string>
+    <!-- Title of dialog that lets the user know that they must install CA certificates
+        in Settings. -->
+    <string name="redirect_ca_certificate_title">Install CA certificates in Settings</string>
+    <!-- Message of dialog that lets the user know that they must install CA certificates in
+        Settings. The placeholder is the name of the app that was trying to install the CA
+        certificate. -->
+    <string name="redirect_ca_certificate_with_app_info_message">This certificate from
+        <xliff:g id="requesting_app" example="Some App">%1$s</xliff:g> must be installed in
+        Settings. Only install CA certificates from organizations you trust.</string>
+    <!-- Button of dialog to redirect user when installing a CA certificate outside Settings -->
+    <string name="redirect_ca_certificate_close_button">Close</string>
+    <!-- Title of dialog to inform user that certificate selected is invalid -->
+    <string name="invalid_certificate_title">Can\'t use this file</string>
+    <!-- Button of dialog to inform user that certificate selected is invalid -->
+    <string name="invalid_certificate_close_button">Close</string>
+    <!-- Message of dialog to inform user that certificate selected is invalid -->
+    <string name="invalid_certificate_message">This file can\'t be used as a <xliff:g id="certificate_usage">%1$s</xliff:g></string>
+    <!-- Default certificate type used in dialog to inform user that certificate selected is invalid -->
+    <string name="certificate">Certificate</string>
+    <!-- CA certificate type used in dialog to inform user that certificate selected is invalid -->
+    <string name="ca_certificate">CA certificate</string>
+    <!-- User certificate type used in dialog to inform user that certificate selected is invalid -->
+    <string name="user_certificate">VPN &amp; app user certificate</string>
+    <!-- WiFi certificate type used in dialog to inform user that certificate selected is invalid -->
+    <string name="wifi_certificate">Wi\u2011Fi certificate</string>
+
+
+    <!-- Confirmation toast. This toast lets the user know that they successfully installed a CA certificate on their device. -->
+    <string name="ca_cert_is_added">CA certificate installed</string>
+    <!-- Confirmation toast. This toast lets the user know that they successfully installed a user certificate on their device. -->
+    <string name="user_cert_is_added">User certificate installed</string>
+    <!-- Confirmation toast. This toast lets the user know that they successfully installed a Wi-Fi certificate on their device. -->
+    <string name="wifi_cert_is_added">Wi\u2011Fi certificate installed</string>
     <!-- toast message -->
     <string name="cert_too_large_error">Couldn\'t install because the certificate size is too large.</string>
     <!-- toast message -->
     <string name="cert_missing_error">Couldn\'t install because the certificate file couldn\'t be located.</string>
     <!-- toast message -->
     <string name="cert_read_error">Couldn\'t install because the certificate file couldn\'t be read.</string>
+    <!-- toast message -->
+    <string name="cert_temp_error">Temporary failure. Please try again later.</string>
 
     <!-- Message displayed when a user other than the owner on a multi-user system tries to
          install a certificate into the certificate store. [CHAR LIMIT=NONE] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b1d493e..7bac29f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -28,4 +28,20 @@
         <item name="android:textAppearance">@android:style/TextAppearance.Material.Subhead</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
+    <style name="dialog_title">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">20sp</item>
+        <item name="android:layout_marginBottom">16dp</item>
+    </style>
+    <style name="dialog_edit_text">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:backgroundTint">?android:attr/colorAccent</item>
+        <item name="android:singleLine">True</item>
+    </style>
+    <style name="dialog_button">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:layout_marginBottom">12dp</item>
+        <item name="android:layout_marginLeft">4dp</item>
+    </style>
 </resources>
diff --git a/src/com/android/certinstaller/CertInstaller.java b/src/com/android/certinstaller/CertInstaller.java
index dd849a7..3a6b7b3 100644
--- a/src/com/android/certinstaller/CertInstaller.java
+++ b/src/com/android/certinstaller/CertInstaller.java
@@ -21,25 +21,24 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.KeyguardManager;
 import android.app.ProgressDialog;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Process;
+import android.security.Credentials;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.security.KeyStore;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.EditText;
-import android.widget.Spinner;
+import android.widget.RadioGroup;
 import android.widget.Toast;
 
 import java.io.Serializable;
@@ -57,17 +56,15 @@
     private static final int NAME_CREDENTIAL_DIALOG = 1;
     private static final int PKCS12_PASSWORD_DIALOG = 2;
     private static final int PROGRESS_BAR_DIALOG = 3;
+    private static final int REDIRECT_CA_CERTIFICATE_DIALOG = 4;
+    private static final int SELECT_CERTIFICATE_USAGE_DIALOG = 5;
+    private static final int INVALID_CERTIFICATE_DIALOG = 6;
 
     private static final int REQUEST_SYSTEM_INSTALL_CODE = 1;
-    private static final int REQUEST_CONFIRM_CREDENTIALS = 2;
 
     // key to states Bundle
     private static final String NEXT_ACTION_KEY = "na";
 
-    // Values for usage type spinner
-    private static final int USAGE_TYPE_SYSTEM = 0;
-    private static final int USAGE_TYPE_WIFI = 1;
-
     private final ViewHelper mView = new ViewHelper();
 
     private int mState;
@@ -98,20 +95,8 @@
                 toastErrorAndFinish(R.string.no_cert_to_saved);
                 finish();
             } else {
-                // Confirm credentials if there's _only_ a CA certificate
-                // NOTE: This will affect WiFi CA certificates - those should not require
-                // confirming the lock screen credentials but the code currently cannot skip the
-                // confirmation for WiFi CA certificates because the user designates the certificate
-                // to a UID only after this stage.
-                if (mCredentials.hasCaCerts() && !mCredentials.hasPrivateKey() &&
-                        !mCredentials.hasUserCertificate()) {
-                    KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
-                    Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null);
-                    if (intent == null) { // No screenlock
-                        extractPkcs12OrInstall();
-                    } else {
-                        startActivityForResult(intent, REQUEST_CONFIRM_CREDENTIALS);
-                    }
+                if (installingCaCertificate()) {
+                    extractPkcs12OrInstall();
                 } else {
                     if (mCredentials.hasUserCertificate() && !mCredentials.hasPrivateKey()) {
                         toastErrorAndFinish(R.string.action_missing_private_key);
@@ -129,6 +114,11 @@
         }
     }
 
+    private boolean installingCaCertificate() {
+        return mCredentials.hasCaCerts() && !mCredentials.hasPrivateKey() &&
+                !mCredentials.hasUserCertificate();
+    }
+
     @Override
     protected void onResume() {
         super.onResume();
@@ -164,7 +154,7 @@
                 return createPkcs12PasswordDialog();
 
             case NAME_CREDENTIAL_DIALOG:
-                return createNameCredentialDialog();
+                return createNameCertificateDialog();
 
             case PROGRESS_BAR_DIALOG:
                 ProgressDialog dialog = new ProgressDialog(this);
@@ -173,6 +163,15 @@
                 dialog.setCancelable(false);
                 return dialog;
 
+            case REDIRECT_CA_CERTIFICATE_DIALOG:
+                return createRedirectCaCertificateDialog();
+
+            case SELECT_CERTIFICATE_USAGE_DIALOG:
+                return createSelectCertificateUsageDialog();
+
+            case INVALID_CERTIFICATE_DIALOG:
+                return createInvalidCertificateDialog();
+
             default:
                 return null;
         }
@@ -189,25 +188,14 @@
                 }
 
                 Log.d(TAG, "credential is added: " + mCredentials.getName());
-                Toast.makeText(this, getString(R.string.cert_is_added, mCredentials.getName()),
-                        Toast.LENGTH_LONG).show();
-
-                if (mCredentials.includesVpnAndAppsTrustAnchors()) {
-                    // more work to do, don't finish just yet
-                    new InstallVpnAndAppsTrustAnchorsTask().execute();
-                    return;
+                if (mCredentials.getCertUsageSelected().equals(Credentials.CERTIFICATE_USAGE_WIFI)) {
+                    Toast.makeText(this, R.string.wifi_cert_is_added, Toast.LENGTH_LONG).show();
+                } else {
+                    Toast.makeText(this, R.string.user_cert_is_added, Toast.LENGTH_LONG).show();
                 }
                 setResult(RESULT_OK);
                 finish();
                 break;
-            case REQUEST_CONFIRM_CREDENTIALS:
-                if (resultCode == RESULT_OK) {
-                    extractPkcs12OrInstall();
-                    return;
-                }
-                // Failed to confirm credentials, do nothing.
-                finish();
-                break;
             default:
                 Log.w(TAG, "unknown request code: " + requestCode);
                 finish();
@@ -223,14 +211,19 @@
                 new Pkcs12ExtractAction("").run(this);
             }
         } else {
-            MyAction action = new InstallOthersAction();
-            action.run(this);
+            if (mCredentials.calledBySettings()) {
+                MyAction action = new InstallOthersAction();
+                action.run(this);
+            } else {
+                createRedirectOrSelectUsageDialog();
+            }
         }
     }
 
     private class InstallVpnAndAppsTrustAnchorsTask extends AsyncTask<Void, Void, Boolean> {
 
-        @Override protected Boolean doInBackground(Void... unused) {
+        @Override
+        protected Boolean doInBackground(Void... unused) {
             try {
                 try (KeyChainConnection keyChainConnection = KeyChain.bind(CertInstaller.this)) {
                     return mCredentials.installVpnAndAppsTrustAnchors(CertInstaller.this,
@@ -242,8 +235,11 @@
             }
         }
 
-        @Override protected void onPostExecute(Boolean success) {
+        @Override
+        protected void onPostExecute(Boolean success) {
             if (success) {
+                Toast.makeText(getApplicationContext(), R.string.ca_cert_is_added,
+                        Toast.LENGTH_LONG).show();
                 setResult(RESULT_OK);
             }
             finish();
@@ -266,13 +262,38 @@
             return;
         }
 
-        nameCredential();
+        if (validCertificateSelected()) {
+            installCertificateOrShowNameDialog();
+        } else {
+            showDialog(INVALID_CERTIFICATE_DIALOG);
+        }
     }
 
-    private void nameCredential() {
+    private boolean validCertificateSelected() {
+        switch (mCredentials.getCertUsageSelected()) {
+            case Credentials.CERTIFICATE_USAGE_CA:
+                return mCredentials.hasOnlyVpnAndAppsTrustAnchors();
+            case Credentials.CERTIFICATE_USAGE_USER:
+                return mCredentials.hasUserCertificate()
+                        && !mCredentials.hasOnlyVpnAndAppsTrustAnchors();
+            case Credentials.CERTIFICATE_USAGE_WIFI:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void installCertificateOrShowNameDialog() {
         if (!mCredentials.hasAnyForSystemInstall()) {
             toastErrorAndFinish(R.string.no_cert_to_saved);
+        } else if (mCredentials.hasOnlyVpnAndAppsTrustAnchors()) {
+            // If there's only a CA certificate to install, then it's going to be used
+            // as a trust anchor. Install it and skip importing to Keystore.
+
+            // more work to do, don't finish just yet
+            new InstallVpnAndAppsTrustAnchorsTask().execute();
         } else {
+            // Name is required if installing User certificate
             showDialog(NAME_CREDENTIAL_DIALOG);
         }
     }
@@ -302,7 +323,15 @@
         removeDialog(PROGRESS_BAR_DIALOG);
         if (success) {
             removeDialog(PKCS12_PASSWORD_DIALOG);
-            nameCredential();
+            if (mCredentials.calledBySettings()) {
+                if (validCertificateSelected()) {
+                    installCertificateOrShowNameDialog();
+                } else {
+                    showDialog(INVALID_CERTIFICATE_DIALOG);
+                }
+            } else {
+                createRedirectOrSelectUsageDialog();
+            }
         } else {
             showDialog(PKCS12_PASSWORD_DIALOG);
             mView.setText(R.id.credential_password, "");
@@ -310,6 +339,103 @@
         }
     }
 
+    private void createRedirectOrSelectUsageDialog() {
+        if (mCredentials.hasOnlyVpnAndAppsTrustAnchors()) {
+            showDialog(REDIRECT_CA_CERTIFICATE_DIALOG);
+        } else {
+            showDialog(SELECT_CERTIFICATE_USAGE_DIALOG);
+        }
+    }
+
+    public CharSequence getCallingAppLabel() {
+        final String callingPkg = mCredentials.getReferrer();
+        if (callingPkg == null) {
+            Log.e(TAG, "Cannot get calling calling AppPackage");
+            return null;
+        }
+
+        final PackageManager pm = getPackageManager();
+        final ApplicationInfo appInfo;
+        try {
+            appInfo = pm.getApplicationInfo(callingPkg, PackageManager.MATCH_DISABLED_COMPONENTS);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Unable to find info for package: " + callingPkg);
+            return null;
+        }
+
+        return appInfo.loadLabel(pm);
+    }
+
+    private Dialog createRedirectCaCertificateDialog() {
+        final String message = getString(
+                R.string.redirect_ca_certificate_with_app_info_message, getCallingAppLabel());
+        Dialog d = new AlertDialog.Builder(this)
+                .setTitle(R.string.redirect_ca_certificate_title)
+                .setMessage(message)
+                .setPositiveButton(R.string.redirect_ca_certificate_close_button,
+                        (dialog, id) -> toastErrorAndFinish(R.string.cert_not_saved))
+                .create();
+        d.setOnCancelListener(dialog -> toastErrorAndFinish(R.string.cert_not_saved));
+        return d;
+    }
+
+    private Dialog createSelectCertificateUsageDialog() {
+        ViewGroup view = (ViewGroup) View.inflate(this, R.layout.select_certificate_usage_dialog,
+                null);
+        mView.setView(view);
+
+        RadioGroup radioGroup = view.findViewById(R.id.certificate_usage);
+        radioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+            switch (checkedId) {
+                case R.id.user_certificate:
+                    mCredentials.setCertUsageSelectedAndUid(Credentials.CERTIFICATE_USAGE_USER);
+                    break;
+                case R.id.wifi_certificate:
+                    mCredentials.setCertUsageSelectedAndUid(Credentials.CERTIFICATE_USAGE_WIFI);
+                default:
+                    Slog.i(TAG, "Unknown selection for scope");
+            }
+        });
+
+
+        final Context appContext = getApplicationContext();
+        Dialog d = new AlertDialog.Builder(this)
+                .setView(view)
+                .setPositiveButton(android.R.string.ok, (dialog, id) -> {
+                    showDialog(NAME_CREDENTIAL_DIALOG);
+                })
+                .setNegativeButton(android.R.string.cancel,
+                        (dialog, id) -> toastErrorAndFinish(R.string.cert_not_saved))
+                .create();
+        d.setOnCancelListener(dialog -> toastErrorAndFinish(R.string.cert_not_saved));
+        return d;
+    }
+
+    private Dialog createInvalidCertificateDialog() {
+        Dialog d = new AlertDialog.Builder(this)
+                .setTitle(R.string.invalid_certificate_title)
+                .setMessage(getString(R.string.invalid_certificate_message,
+                        getCertificateUsageName()))
+                .setPositiveButton(R.string.invalid_certificate_close_button,
+                        (dialog, id) -> toastErrorAndFinish(R.string.cert_not_saved))
+                .create();
+        d.setOnCancelListener(dialog -> finish());
+        return d;
+    }
+
+    String getCertificateUsageName() {
+        switch (mCredentials.getCertUsageSelected()) {
+            case Credentials.CERTIFICATE_USAGE_CA:
+                return getString(R.string.ca_certificate);
+            case Credentials.CERTIFICATE_USAGE_USER:
+                return getString(R.string.user_certificate);
+            case Credentials.CERTIFICATE_USAGE_WIFI:
+                return getString(R.string.wifi_certificate);
+            default:
+                return getString(R.string.certificate);
+        }
+    }
+
     private Dialog createPkcs12PasswordDialog() {
         View view = View.inflate(this, R.layout.password_dialog, null);
         mView.setView(view);
@@ -337,53 +463,23 @@
         return d;
     }
 
-    private Dialog createNameCredentialDialog() {
-        ViewGroup view = (ViewGroup) View.inflate(this, R.layout.name_credential_dialog, null);
+    private Dialog createNameCertificateDialog() {
+        ViewGroup view = (ViewGroup) View.inflate(this, R.layout.name_certificate_dialog, null);
         mView.setView(view);
         if (mView.getHasEmptyError()) {
             mView.showError(R.string.name_empty_error);
             mView.setHasEmptyError(false);
         }
-        mView.setText(R.id.credential_info, mCredentials.getDescription(this).toString());
-        final EditText nameInput = view.findViewById(R.id.credential_name);
-        if (mCredentials.isInstallAsUidSet()) {
-            view.findViewById(R.id.credential_usage_group).setVisibility(View.GONE);
-        } else {
-            final Spinner usageSpinner = view.findViewById(R.id.credential_usage);
-            final View ca_capabilities_warning = view.findViewById(R.id.credential_capabilities_warning);
-
-            usageSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
-                @Override
-                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-                    switch ((int) id) {
-                        case USAGE_TYPE_SYSTEM:
-                            ca_capabilities_warning.setVisibility(
-                                    mCredentials.includesVpnAndAppsTrustAnchors() ?
-                                    View.VISIBLE : View.GONE);
-                            mCredentials.setInstallAsUid(KeyStore.UID_SELF);
-                            break;
-                        case USAGE_TYPE_WIFI:
-                            ca_capabilities_warning.setVisibility(View.GONE);
-                            mCredentials.setInstallAsUid(Process.WIFI_UID);
-                            break;
-                        default:
-                            Log.w(TAG, "Unknown selection for scope: " + id);
-                    }
-                }
-
-                @Override
-                public void onNothingSelected(AdapterView<?> parent) {
-                }
-            });
-        }
+        final EditText nameInput = view.findViewById(R.id.certificate_name);
         nameInput.setText(getDefaultName());
         nameInput.selectAll();
         final Context appContext = getApplicationContext();
+
         Dialog d = new AlertDialog.Builder(this)
                 .setView(view)
                 .setTitle(R.string.name_credential_dialog_title)
                 .setPositiveButton(android.R.string.ok, (dialog, id) -> {
-                    String name = mView.getText(R.id.credential_name);
+                    String name = mView.getText(R.id.certificate_name);
                     if (TextUtils.isEmpty(name)) {
                         mView.setHasEmptyError(true);
                         removeDialog(NAME_CREDENTIAL_DIALOG);
@@ -391,16 +487,7 @@
                     } else {
                         removeDialog(NAME_CREDENTIAL_DIALOG);
                         mCredentials.setName(name);
-
-                        // install everything to system keystore
-                        try {
-                            startActivityForResult(
-                                    mCredentials.createSystemInstallIntent(appContext),
-                                    REQUEST_SYSTEM_INSTALL_CODE);
-                        } catch (ActivityNotFoundException e) {
-                            Log.w(TAG, "systemInstall(): " + e);
-                            toastErrorAndFinish(R.string.cert_not_saved);
-                        }
+                        installCertificateToKeystore(appContext);
                     }
                 })
                 .setNegativeButton(android.R.string.cancel,
@@ -410,6 +497,17 @@
         return d;
     }
 
+    private void installCertificateToKeystore(Context context) {
+        try {
+            startActivityForResult(
+                    mCredentials.createSystemInstallIntent(context),
+                    REQUEST_SYSTEM_INSTALL_CODE);
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "installCertificateToKeystore(): ", e);
+            toastErrorAndFinish(R.string.cert_not_saved);
+        }
+    }
+
     private String getDefaultName() {
         String name = mCredentials.getName();
         if (TextUtils.isEmpty(name)) {
diff --git a/src/com/android/certinstaller/CertInstallerMain.java b/src/com/android/certinstaller/CertInstallerMain.java
index 7e93884..4cec5fc 100644
--- a/src/com/android/certinstaller/CertInstallerMain.java
+++ b/src/com/android/certinstaller/CertInstallerMain.java
@@ -16,10 +16,14 @@
 
 package com.android.certinstaller;
 
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.UserManager;
 import android.preference.PreferenceActivity;
 import android.provider.DocumentsContract;
@@ -28,6 +32,8 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import libcore.io.IoUtils;
+
 import java.io.BufferedInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -35,8 +41,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import libcore.io.IoUtils;
-
 /**
  * The main class for installing certificates to the system keystore. It reacts
  * to the public {@link Credentials#INSTALL_ACTION} intent.
@@ -46,6 +50,7 @@
 
     private static final int REQUEST_INSTALL = 1;
     private static final int REQUEST_OPEN_DOCUMENT = 2;
+    private static final int REQUEST_CONFIRM_CREDENTIALS = 3;
 
     private static final String INSTALL_CERT_AS_USER_CLASS = ".InstallCertAsUser";
 
@@ -72,7 +77,8 @@
         setResult(RESULT_CANCELED);
 
         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
-        if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)) {
+        if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)
+                || userManager.isGuestUser()) {
             finish();
             return;
         }
@@ -99,27 +105,56 @@
             // If bundle is empty of any actual credentials, ask user to open.
             // 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.containsKey(Credentials.EXTRA_INSTALL_AS_UID)))) {
-                final String[] mimeTypes = MIME_MAPPINGS.keySet().toArray(new String[0]);
-                final Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
-                openIntent.setType("*/*");
-                openIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
-                openIntent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
-                startActivityForResult(openIntent, REQUEST_OPEN_DOCUMENT);
+            if (nullOrEmptyBundle(bundle) || bundleContainsNameOnly(bundle)
+                    || bundleContainsInstallAsUidOnly(bundle)
+                    || bundleContainsExtraCertificateUsageOnly(bundle)) {
+
+                // Confirm credentials if there's only a CA certificate
+                if (installingCaCertificate(bundle)) {
+                    confirmDeviceCredential();
+                } else {
+                    startOpenDocumentActivity();
+                }
             } else {
-                final Intent installIntent = new Intent(this, CertInstaller.class);
-                installIntent.putExtras(intent);
-                startActivityForResult(installIntent, REQUEST_INSTALL);
+                startInstallActivity(intent);
             }
         } else if (Intent.ACTION_VIEW.equals(action)) {
             startInstallActivity(intent.getType(), intent.getData());
         }
     }
 
+    private boolean nullOrEmptyBundle(Bundle bundle) {
+        return bundle == null || bundle.isEmpty();
+    }
+
+    private boolean bundleContainsNameOnly(Bundle bundle) {
+        return bundle.size() == 1 && bundle.containsKey(KeyChain.EXTRA_NAME);
+    }
+
+    private boolean bundleContainsInstallAsUidOnly(Bundle bundle) {
+        return bundle.size() == 1 && bundle.containsKey(Credentials.EXTRA_INSTALL_AS_UID);
+    }
+
+    private boolean bundleContainsExtraCertificateUsageOnly(Bundle bundle) {
+        return bundle.size() == 1 && bundle.containsKey(Credentials.EXTRA_CERTIFICATE_USAGE);
+    }
+
+    private boolean installingCaCertificate(Bundle bundle) {
+        return bundle != null && bundle.size() == 1 && Credentials.CERTIFICATE_USAGE_CA.equals(
+                bundle.getString(Credentials.EXTRA_CERTIFICATE_USAGE));
+    }
+
+    private void confirmDeviceCredential() {
+        KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
+        Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null,
+                null);
+        if (intent == null) { // No screenlock
+            startOpenDocumentActivity();
+        } else {
+            startActivityForResult(intent, REQUEST_CONFIRM_CREDENTIALS);
+        }
+    }
+
     // The maximum amount of data to read into memory before aborting.
     // Without a limit, a sufficiently-large file will run us out of memory.  A
     // typical certificate or WiFi config is under 10k, so 10MiB should be more
@@ -146,6 +181,33 @@
         return bytes.toByteArray();
     }
 
+    private void startInstallActivity(Intent intent) {
+        final Intent installIntent = new Intent(this, CertInstaller.class);
+        if (intent.getExtras() != null && intent.getExtras().getString(Intent.EXTRA_REFERRER)
+                != null) {
+            Log.v(TAG, String.format(
+                    "Removing referrer extra with value %s which was not meant to be included",
+                    intent.getBundleExtra(Intent.EXTRA_REFERRER)));
+            intent.removeExtra(Intent.EXTRA_REFERRER);
+        }
+        installIntent.putExtras(intent);
+
+        try {
+            // The referrer is passed as an extra because the launched-from package needs to be
+            // obtained here and not in the CertInstaller.
+            // It is also safe to add the referrer as an extra because the CertInstaller activity
+            // is not exported, which means it cannot be called from other apps.
+            IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
+            installIntent.putExtra(Intent.EXTRA_REFERRER,
+                    activityTaskManager.getLaunchedFromPackage(getActivityToken()));
+            startActivityForResult(installIntent, REQUEST_INSTALL);
+        } catch (RemoteException e) {
+            Log.v(TAG, "Could not talk to activity manager.", e);
+            Toast.makeText(this, R.string.cert_temp_error, Toast.LENGTH_LONG).show();
+            finish();
+        }
+    }
+
     private void startInstallActivity(String mimeType, Uri uri) {
         if (mimeType == null) {
             mimeType = getContentResolver().getType(uri);
@@ -165,8 +227,10 @@
                 in = getContentResolver().openInputStream(uri);
 
                 final byte[] raw = readWithLimit(in);
-                startInstallActivity(target, raw);
 
+                Intent intent = getIntent();
+                intent.putExtra(target, raw);
+                startInstallActivity(intent);
             } catch (IOException e) {
                 Log.e(TAG, "Failed to read certificate: " + e);
                 Toast.makeText(this, R.string.cert_read_error, Toast.LENGTH_LONG).show();
@@ -176,13 +240,6 @@
         }
     }
 
-    private void startInstallActivity(String target, byte[] value) {
-        Intent intent = new Intent(this, CertInstaller.class);
-        intent.putExtra(target, value);
-
-        startActivityForResult(intent, REQUEST_INSTALL);
-    }
-
     private void startWifiInstallActivity(String mimeType, Uri uri) {
         Intent intent = new Intent(this, WiFiInstaller.class);
         try (BufferedInputStream in =
@@ -198,19 +255,40 @@
         }
     }
 
+    private void startOpenDocumentActivity() {
+        final String[] mimeTypes = MIME_MAPPINGS.keySet().toArray(new String[0]);
+        final Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+        openIntent.setType("*/*");
+        openIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
+        openIntent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+        startActivityForResult(openIntent, REQUEST_OPEN_DOCUMENT);
+    }
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_OPEN_DOCUMENT) {
-            if (resultCode == RESULT_OK) {
-                startInstallActivity(null, data.getData());
-            } else {
+        switch (requestCode) {
+            case REQUEST_INSTALL:
+                setResult(resultCode);
                 finish();
-            }
-        } else if (requestCode == REQUEST_INSTALL) {
-            setResult(resultCode);
-            finish();
-        } else {
-            Log.w(TAG, "unknown request code: " + requestCode);
+                break;
+            case REQUEST_OPEN_DOCUMENT:
+                if (resultCode == RESULT_OK) {
+                    startInstallActivity(null, data.getData());
+                } else {
+                    finish();
+                }
+                break;
+            case REQUEST_CONFIRM_CREDENTIALS:
+                if (resultCode == RESULT_OK) {
+                    startOpenDocumentActivity();
+                    return;
+                }
+                // Failed to confirm credentials, do nothing.
+                finish();
+                break;
+            default:
+                Log.w(TAG, "unknown request code: " + requestCode);
+                break;
         }
     }
 }
diff --git a/src/com/android/certinstaller/CredentialHelper.java b/src/com/android/certinstaller/CredentialHelper.java
index 4ec2e7e..a1e9314 100644
--- a/src/com/android/certinstaller/CredentialHelper.java
+++ b/src/com/android/certinstaller/CredentialHelper.java
@@ -16,20 +16,24 @@
 
 package com.android.certinstaller;
 
+import static android.security.KeyStore.UID_SELF;
+
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.security.Credentials;
-import android.security.KeyChain;
 import android.security.IKeyChainService;
+import android.security.KeyChain;
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
+
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.DEROctetString;
@@ -39,9 +43,9 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.security.KeyFactory;
+import java.security.KeyStore;
 import java.security.KeyStore.PasswordProtection;
 import java.security.KeyStore.PrivateKeyEntry;
-import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
@@ -64,6 +68,7 @@
     private static final String DATA_KEY = "data";
     private static final String CERTS_KEY = "crts";
     private static final String USER_KEY_ALGORITHM = "user_key_algorithm";
+    private static final String SETTINGS_PACKAGE = "com.android.settings";
 
     private static final String TAG = "CredentialHelper";
 
@@ -71,6 +76,8 @@
     private HashMap<String, byte[]> mBundle = new HashMap<String, byte[]>();
 
     private String mName = "";
+    private String mCertUsageSelected = "";
+    private String mReferrer = "";
     private int mUid = -1;
     private PrivateKey mUserKey;
     private X509Certificate mUserCert;
@@ -91,9 +98,21 @@
             mName = name;
         }
 
-        mUid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+        String certUsageSelected = bundle.getString(Credentials.EXTRA_CERTIFICATE_USAGE);
+        bundle.remove(Credentials.EXTRA_CERTIFICATE_USAGE);
+        if (certUsageSelected != null) {
+            setCertUsageSelectedAndUid(certUsageSelected);
+        } else {
+            mUid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+        }
         bundle.remove(Credentials.EXTRA_INSTALL_AS_UID);
 
+        String referrer = bundle.getString(Intent.EXTRA_REFERRER);
+        bundle.remove(Intent.EXTRA_REFERRER);
+        if (referrer != null) {
+            mReferrer = referrer;
+        }
+
         Log.d(TAG, "# extras: " + bundle.size());
         for (String key : bundle.keySet()) {
             byte[] bytes = bundle.getByteArray(key);
@@ -196,6 +215,14 @@
         return mBundle.containsKey(Credentials.EXTRA_PRIVATE_KEY);
     }
 
+    int getUidFromCertificateUsage(String certUsage) {
+        if (Credentials.CERTIFICATE_USAGE_WIFI.equals(certUsage)) {
+            return Process.WIFI_UID;
+        } else {
+            return UID_SELF;
+        }
+    }
+
     boolean hasUserCertificate() {
         return (mUserCert != null);
     }
@@ -262,16 +289,17 @@
         return mName;
     }
 
-    void setInstallAsUid(int uid) {
-        mUid = uid;
+    void setCertUsageSelectedAndUid(String certUsageSelected) {
+        mCertUsageSelected = certUsageSelected;
+        mUid = getUidFromCertificateUsage(certUsageSelected);
     }
 
-    boolean isInstallAsUidSet() {
-        return mUid != -1;
+    String getCertUsageSelected() {
+        return mCertUsageSelected;
     }
 
-    int getInstallAsUid() {
-        return mUid;
+    boolean calledBySettings() {
+        return mReferrer != null && mReferrer.equals(SETTINGS_PACKAGE);
     }
 
     Intent createSystemInstallIntent(final Context context) {
@@ -281,22 +309,17 @@
         intent.setComponent(ComponentName.unflattenFromString(
                 context.getString(R.string.config_system_install_component)));
         intent.putExtra(Credentials.EXTRA_INSTALL_AS_UID, mUid);
+        intent.putExtra(Credentials.EXTRA_USER_KEY_ALIAS, mName);
         try {
             if (mUserKey != null) {
-                intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_NAME,
-                        Credentials.USER_PRIVATE_KEY + mName);
                 intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_DATA,
                         mUserKey.getEncoded());
             }
             if (mUserCert != null) {
-                intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_NAME,
-                        Credentials.USER_CERTIFICATE + mName);
                 intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_DATA,
                         Credentials.convertToPem(mUserCert));
             }
             if (!mCaCerts.isEmpty()) {
-                intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_NAME,
-                        Credentials.CA_CERTIFICATE + mName);
                 X509Certificate[] caCerts
                         = mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
                 intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_DATA,
@@ -340,12 +363,8 @@
     }
 
     private void maybeApproveCaCert(Context context, String alias) {
-        // Some CTS verifier test asks testers to reset auto approved CA cert by removing
-        // lock sreen, but it's not possible if we don't have Android lock screen. (e.g.
-        // Android is running in the container).  In this case, disable auto cert approval.
         final KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
-        if (keyguardManager.isDeviceSecure(UserHandle.myUserId())
-                && context.getResources().getBoolean(R.bool.config_auto_cert_approval)) {
+        if (keyguardManager.isDeviceSecure(UserHandle.myUserId())) {
             // Since the cert is installed by real user, the cert is approved by the user
             final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
             dpm.approveCaCert(alias, UserHandle.myUserId(), true);
@@ -432,14 +451,14 @@
     }
 
     /**
-     * Returns whether this credential contains CA certificates to be used as trust anchors
+     * Returns true if this credential contains _only_ CA certificates to be used as trust anchors
      * for VPN and apps.
      */
-    public boolean includesVpnAndAppsTrustAnchors() {
+    public boolean hasOnlyVpnAndAppsTrustAnchors() {
         if (!hasCaCerts()) {
             return false;
         }
-        if (getInstallAsUid() != android.security.KeyStore.UID_SELF) {
+        if (mUid != UID_SELF) {
             // VPN and Apps trust anchors can only be installed under UID_SELF
             return false;
         }
@@ -452,4 +471,8 @@
             return true;
         }
     }
+
+    public String getReferrer() {
+        return mReferrer;
+    }
 }
diff --git a/src/com/android/certinstaller/WiFiInstaller.java b/src/com/android/certinstaller/WiFiInstaller.java
index 90b8eb7..41827f6 100644
--- a/src/com/android/certinstaller/WiFiInstaller.java
+++ b/src/com/android/certinstaller/WiFiInstaller.java
@@ -93,6 +93,13 @@
                         public void run() {
                             boolean success = true;
                             try {
+                                mWifiManager.removePasspointConfiguration(
+                                        mPasspointConfig.getHomeSp().getFqdn());
+                            } catch (IllegalArgumentException e) {
+                                // Do nothing. This is expected if a profile with this FQDN does not
+                                // exist.
+                            }
+                            try {
                                 mWifiManager.addOrUpdatePasspointConfiguration(mPasspointConfig);
                             } catch (RuntimeException rte) {
                                 Log.w(TAG, "Caught exception while installing wifi config: " +