[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 3f45295f7b -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/KeyChain/+/15591122

Change-Id: Ie94421a512998ccf655e88d9101ad30ccd8621e5
diff --git a/Android.bp b/Android.bp
index 456ddd6..6f8bd07 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,8 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_app {
     name: "KeyChain",
+    static_libs: [
+        "bouncycastle-unbundled",
+        "com.google.android.material_material",
+    ],
     srcs: ["src/**/*.java"],
     platform_apis: true,
     certificate: "platform",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9d1601f..fae32b6 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -11,13 +11,16 @@
             android:allowBackup="false"
             android:usesCleartextTraffic="false"
             android:theme="@android:style/Theme.DeviceDefault.DayNight">
-        <service android:name="com.android.keychain.KeyChainService">
+        <service android:name="com.android.keychain.KeyChainService"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.security.IKeyChainService"/>
             </intent-filter>
         </service>
         <activity android:name="com.android.keychain.KeyChainActivity"
+                  android:exported="true"
                   android:theme="@style/KeyChainTransparent"
+                  android:launchMode="singleTop"
 		  android:excludeFromRecents="true">
 	    <intent-filter>
 	        <action android:name="com.android.keychain.CHOOSER"/>
diff --git a/OWNERS b/OWNERS
index 907b626..7218804 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,5 @@
 # Core Enterprise security team
-eranm@google.com
+acjohnston@google.com
 pgrafov@google.com
 rubinxu@google.com
 
@@ -12,3 +12,4 @@
 
 # Emeritus owners
 rgl@google.com
+eranm@google.com
diff --git a/res/layout/keychain_activity.xml b/res/layout/keychain_activity.xml
new file mode 100644
index 0000000..b96c504
--- /dev/null
+++ b/res/layout/keychain_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2021 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.
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <androidx.coordinatorlayout.widget.CoordinatorLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true" />
+
+</RelativeLayout>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index dec5f2d..fe50c19 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Jy kan sertifikate installeer van \'n PKCS#12-lêer met \'n %1$s- of \'n %2$s-uitbreiding wat in eksterne berging geleë is."</string>
     <string name="allow_button" msgid="3030990695030371561">"Kies"</string>
     <string name="deny_button" msgid="3766539809121892584">"Weier"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Laai tans sertifikate …"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s soek tans vir \'n sertifikaat …"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index e2b44f0..819529c 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"የ%1$s ወይም %2$s ቅጥያቸው ውጫዊ ማከማቻ ላይ የሚገኝ የPKCS#12 ፋይል የእውቅና ማረጋገጫ ምስክር ወረቀቶችን መጫን ይችላሉ።"</string>
     <string name="allow_button" msgid="3030990695030371561">"ምረጥ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ከልክል"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"የእውቅና ማረጋገጫ በመጫን ላይ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s የእውቅና ማረጋገጫን በመፈተሽ ላይ ነው…"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index fff837f..f98a0ac 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"‏يمكنك تثبيت شهادات من ملف PKCS#12 بامتداد %1$s أو %2$s، حيث إن هذا الملف موجود في وحدة التخزين الخارجية."</string>
     <string name="allow_button" msgid="3030990695030371561">"تحديد"</string>
     <string name="deny_button" msgid="3766539809121892584">"رفض"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"جارٍ تحميل الشهادات..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"‏جارٍ التحقّق من قِبل %s بحثًا عن شهادة…"</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index b8f054d..b98f03b 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"আপুনি বাহ্যিক সঞ্চয়াগাৰত থকা %1$s বা %2$s এক্সটেনশ্বনযুক্ত এটা PKCS#12 ফাইলৰ পৰা প্ৰমাণপত্ৰসমূহ ইনষ্টল কৰিব পাৰে৷"</string>
     <string name="allow_button" msgid="3030990695030371561">"বাছনি কৰক"</string>
     <string name="deny_button" msgid="3766539809121892584">"প্ৰত্যাখ্যান কৰক"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"প্ৰমাণপত্ৰসমূহ ল’ড কৰি থকা হৈছে..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%sএ প্ৰমাণপত্ৰ আছে নে নাই সেয়া পৰীক্ষা কৰি আছে…"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 9d97183..a44203a 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Siz sertifikatları xarici yaddaşda yerləşən, %1$s və ya %2$s artırması olan PKCS#12 fayldan quraşdıra bilərsiniz."</string>
     <string name="allow_button" msgid="3030990695030371561">"Seçin"</string>
     <string name="deny_button" msgid="3766539809121892584">"İmtina et"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Sertifikatlar yüklənir..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s sertifikatı yoxlayır…"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index da2c756..a607e37 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Možete da instalirate sertifikate iz PKCS#12 datoteke sa ekstenzijom %1$s ili %2$s koji se nalazi u spoljnoj memoriji."</string>
     <string name="allow_button" msgid="3030990695030371561">"Izaberi"</string>
     <string name="deny_button" msgid="3766539809121892584">"Odbij"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Učitavaju se sertifikati…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s proverava da li postoji sertifikat…"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index f99b572..de554f3 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Сертыфікаты можна ўсталяваць з файла PKCS#12 з пашырэннем %1$s або %2$s, якi знаходзіцца на вонкавым сховішчы."</string>
     <string name="allow_button" msgid="3030990695030371561">"Выбраць"</string>
     <string name="deny_button" msgid="3766539809121892584">"Адмовіць"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Ідзе загрузка сертыфікатаў…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s правярае наяўнасць сертыфіката…"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 3cd43ac..df3c993 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Можете да инсталирате сертификати от файл с формат PKCS#12 с разширение %1$s или %2$s, намиращ се във външното хранилище."</string>
     <string name="allow_button" msgid="3030990695030371561">"Избиране"</string>
     <string name="deny_button" msgid="3766539809121892584">"Отказ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Сертификатите се зареждат..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s проверява за сертификат…"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index c63fe73..d9b8e05 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"বহিরাগত সঞ্চয়স্থানে অবস্থিত %1$s বা %2$s এক্সটেনশান সহ PKCS#12 ফাইল থেকে আপনি আপনার সার্টিফিকেটগুলি ইনস্টল করতে পারেন।"</string>
     <string name="allow_button" msgid="3030990695030371561">"বেছে নিন"</string>
     <string name="deny_button" msgid="3766539809121892584">"অস্বীকার করুন"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"সার্টিফিকেট লোড করা হচ্ছে..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s সার্টিফিকেট আছে কিনা চেক করছে…"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 94f708c..e526907 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Možete instalirati certifikate iz PKCS#12 fajla sa ekstenzijom %1$s ili %2$s, koji se nalazi u vanjskoj pohrani."</string>
     <string name="allow_button" msgid="3030990695030371561">"Odaberi"</string>
     <string name="deny_button" msgid="3766539809121892584">"Odbij"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Učitavanje potvrda..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s provjerava postoji li certifikat…"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index d4156b0..5d3eb57 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Pots instal·lar certificats des d\'un fitxer PKCS#12 amb una extensió %1$s o %2$s ubicada en un emmagatzematge extern."</string>
     <string name="allow_button" msgid="3030990695030371561">"Selecciona"</string>
     <string name="deny_button" msgid="3766539809121892584">"Denega"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"S\'estan carregant els certificats..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s està cercant un certificat…"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 35e9982..b4c0645 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Certifikáty můžete instalovat ze souboru PKCS#12 s příponou %1$s nebo %2$s, umístěného v externím úložišti."</string>
     <string name="allow_button" msgid="3030990695030371561">"Vybrat"</string>
     <string name="deny_button" msgid="3766539809121892584">"Odmítnout"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Načítání certifikátů..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s vyhledává certifikát…"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7ebec70..1828cfa 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Du kan installere certifikater fra en PKCS#12-fil med en %1$s- eller en %2$s-udvidelse, der er placeret på et eksternt lager."</string>
     <string name="allow_button" msgid="3030990695030371561">"Vælg"</string>
     <string name="deny_button" msgid="3766539809121892584">"Afvis"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Indlæser certifikater..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s indlæser et certifikat…"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c7cf5d4..135aa80 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Du kannst Zertifikate aus einer PKCS#12-Datei mit einer %1$s- oder %2$s-Erweiterung installieren, die sich auf externen Speichermedien befinden."</string>
     <string name="allow_button" msgid="3030990695030371561">"Auswählen"</string>
     <string name="deny_button" msgid="3766539809121892584">"Ablehnen"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Zertifikate werden geladen…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s sucht nach einem Zertifikat…"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 034df08..7d183e5 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Μπορείτε να εγκαταστήσετε πιστοποιητικά από ένα αρχείο PKCS#12 με επέκταση %1$s ή %2$s που βρίσκονται σε εξωτερική συσκευή αποθήκευσης."</string>
     <string name="allow_button" msgid="3030990695030371561">"Επιλογή"</string>
     <string name="deny_button" msgid="3766539809121892584">"Απόρριψη"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Φόρτωση πιστοποιητικών…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"Το %s ελέγχει για πιστοποιητικό…"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 5962360..4df0c2b 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"You can install certificates from a PKCS#12 file with a %1$s or a %2$s extension located in external storage."</string>
     <string name="allow_button" msgid="3030990695030371561">"Select"</string>
     <string name="deny_button" msgid="3766539809121892584">"Deny"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Loading certificates..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s is checking for a certificate…"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 5962360..4df0c2b 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"You can install certificates from a PKCS#12 file with a %1$s or a %2$s extension located in external storage."</string>
     <string name="allow_button" msgid="3030990695030371561">"Select"</string>
     <string name="deny_button" msgid="3766539809121892584">"Deny"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Loading certificates..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s is checking for a certificate…"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 5962360..4df0c2b 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"You can install certificates from a PKCS#12 file with a %1$s or a %2$s extension located in external storage."</string>
     <string name="allow_button" msgid="3030990695030371561">"Select"</string>
     <string name="deny_button" msgid="3766539809121892584">"Deny"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Loading certificates..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s is checking for a certificate…"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 5962360..4df0c2b 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"You can install certificates from a PKCS#12 file with a %1$s or a %2$s extension located in external storage."</string>
     <string name="allow_button" msgid="3030990695030371561">"Select"</string>
     <string name="deny_button" msgid="3766539809121892584">"Deny"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Loading certificates..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s is checking for a certificate…"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index cc9fbc9..f0c015c 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎You can install certificates from a PKCS#12 file with a %1$s or a %2$s extension located in external storage.‎‏‎‎‏‎"</string>
     <string name="allow_button" msgid="3030990695030371561">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎Select‎‏‎‎‏‎"</string>
     <string name="deny_button" msgid="3766539809121892584">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎Deny‎‏‎‎‏‎"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎Loading certificates...‎‏‎‎‏‎"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎%s is checking for a certificate…‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 051841d..97b0af2 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Puedes instalar los certificados desde un archivo PKCS#12 con extensión %1$s o %2$s a través de una ubicación de almacenamiento externa."</string>
     <string name="allow_button" msgid="3030990695030371561">"Seleccionar"</string>
     <string name="deny_button" msgid="3766539809121892584">"Rechazar"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Cargando certificados…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s está buscando un certificado…"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 0d5574f..7bef59f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Puedes instalar certificados desde un archivo PKCS#12 con extensión %1$s o %2$s ubicado en el almacenamiento externo."</string>
     <string name="allow_button" msgid="3030990695030371561">"Seleccionar"</string>
     <string name="deny_button" msgid="3766539809121892584">"Denegar"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Cargando certificados..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s está comprobando si hay un certificado…"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 0757465..0bbc664 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Võite installida sertifikaadid failist PKCS#12 laiendiga %1$s või %2$s, mis asub välismäluseadmel."</string>
     <string name="allow_button" msgid="3030990695030371561">"Vali"</string>
     <string name="deny_button" msgid="3766539809121892584">"Keeldu"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Sertifikaatide laadimine …"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s kontrollib, kas sertifikaat on olemas …"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index c7fc130..60a48fb 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Ziurtagiriak kanpoko memorian daukazun eta %1$s edo %2$s luzapena duen PKCS#12 fitxategi batetik instala ditzakezu."</string>
     <string name="allow_button" msgid="3030990695030371561">"Hautatu"</string>
     <string name="deny_button" msgid="3766539809121892584">"Ukatu"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Ziurtagiriak kargatzen…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ziurtagiri bat egiaztatzen ari da…"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 1f1fddf..563be4f 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"‏با یک افرونه %2$s یا %1$s واقع در حافظه خارجی می‌توانید مجوزهارا از یک فایل PKCS#12 نصب کنید."</string>
     <string name="allow_button" msgid="3030990695030371561">"انتخاب"</string>
     <string name="deny_button" msgid="3766539809121892584">"مجاز نبودن"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"درحال بار کردن مجوز..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"‏%s درحال بررسی گواهینامه است…"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 8542c43..98b6812 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Voit asentaa varmenteita PKCS#12-tiedostosta, jos ulkoisessa tallennustilassa on %1$s tai %2$s-laajennus."</string>
     <string name="allow_button" msgid="3030990695030371561">"Valitse"</string>
     <string name="deny_button" msgid="3766539809121892584">"Hylkää"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Ladataan varmenteita…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s tarkistaa varmennetta…"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 60b86bc..7dbd53e 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Vous pouvez installer des certificats à partir d\'un fichier PKCS#12 doté d\'une extension %1$s ou %2$s conservée dans la mémoire de stockage externe."</string>
     <string name="allow_button" msgid="3030990695030371561">"Sélectionner"</string>
     <string name="deny_button" msgid="3766539809121892584">"Refuser"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Chargement des certificats en cours…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s recherche un certificat…"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 44ec4d0..0dadaf9 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Vous pouvez installer des certificats à partir d\'un fichier PKCS#12 doté d\'une extension %1$s ou %2$s conservée dans la mémoire de stockage externe."</string>
     <string name="allow_button" msgid="3030990695030371561">"Choisir"</string>
     <string name="deny_button" msgid="3766539809121892584">"Refuser"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Chargement des certificats…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s recherche un certificat…"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index ebb2610..f8823be 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Podes instalar certificados dun ficheiro PKCS#12 cunha extensión %1$s ou %2$s situado no almacenamento externo."</string>
     <string name="allow_button" msgid="3030990695030371561">"Seleccionar"</string>
     <string name="deny_button" msgid="3766539809121892584">"Rexeitar"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Cargando certificados…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s está cargando un certificado…"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 7b8ff8e..777b944 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"તમે બાહ્ય સ્ટોરેજ પર સ્થિત %1$s અથવા %2$s એક્સ્ટેન્શન સાથે PKCS#12 ફાઇલમાંથી પ્રમાણપત્રો ઇન્સ્ટૉલ કરી શકો છો."</string>
     <string name="allow_button" msgid="3030990695030371561">"પસંદ કરો"</string>
     <string name="deny_button" msgid="3766539809121892584">"નકારો"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"પ્રમાણપત્રો લોડ કરી રહ્યાં છીએ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s પ્રમાણપત્ર ચેક કરી રહ્યાં છીએ…"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index e9a72d4..1313191 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"आप बाहरी मेमोरी में मौजूद %1$s या %2$s एक्‍सटेंशन वाली PKCS#12 फ़ाइल से प्रमाणपत्र इंस्‍टॉल कर सकते हैं."</string>
     <string name="allow_button" msgid="3030990695030371561">"चुनें"</string>
     <string name="deny_button" msgid="3766539809121892584">"नामंज़ूर करें"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"प्रमाणपत्र लोड हो रहे हैं..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s प्रमाणपत्र की जांच की जा रही है…"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index f9827b0..42bd92c 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Možete instalirati certifikate iz datoteke PKCS#12 s proširenjem %1$s ili %2$s locirane u vanjskom spremištu."</string>
     <string name="allow_button" msgid="3030990695030371561">"Odaberi"</string>
     <string name="deny_button" msgid="3766539809121892584">"Odbij"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Učitavanje certifikata..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s provjerava certifikat…"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 3c34808..aec30e8 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"PKCS#12 fájlból külső tárolóban található %1$s vagy %2$s bővítmény segítségével telepíthet tanúsítványokat."</string>
     <string name="allow_button" msgid="3030990695030371561">"Választás"</string>
     <string name="deny_button" msgid="3766539809121892584">"Elutasítás"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Tanúsítványok betöltése…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"A(z) %s tanúsítványt keres…"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index a96c9de..177836f 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Կարող եք հավաստագրեր տեղադրել  %1$s կամ  %2$s  ընդլայնումով PKCS#12 ֆայլից, որը գտնվում է արտաքին կրիչի վրա։"</string>
     <string name="allow_button" msgid="3030990695030371561">"Ընտրել"</string>
     <string name="deny_button" msgid="3766539809121892584">"Մերժել"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Հավաստագրերի բեռնում..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s հավելվածը ստուգում է հավաստագրի առկայությունը…"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c53ae0f..f399f2a 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Anda dapat menginstal sertifikat dari file PKCS#12 dengan ekstensi %1$s atau %2$s yang terletak di penyimpanan eksternal."</string>
     <string name="allow_button" msgid="3030990695030371561">"Pilih"</string>
     <string name="deny_button" msgid="3766539809121892584">"Tolak"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Memuat sertifikat..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s sedang memeriksa sertifikat…"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 7188bbc..c67c47f 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Þú getur sett upp vottorð úr PKCS#12-skrá með endinguna %1$s eða %2$s sem finna má í ytri geymslunni."</string>
     <string name="allow_button" msgid="3030990695030371561">"Velja"</string>
     <string name="deny_button" msgid="3766539809121892584">"Hafna"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Hleður vottorð..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s leitar að vottorði…"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index ebc6971..cd8599b 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Puoi installare certificati da un file PKCS#12 con un\'estensione %1$s o %2$s che si trova nella memoria esterna."</string>
     <string name="allow_button" msgid="3030990695030371561">"Seleziona"</string>
     <string name="deny_button" msgid="3766539809121892584">"Rifiuta"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Caricamento dei certificati…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s sta controllando se è presente un certificato…"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index ea04f37..ec38208 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"‏ניתן להתקין אישורים מקובץ PKCS#12 עם סיומת %1$s או %2$s הממוקם באחסון חיצוני."</string>
     <string name="allow_button" msgid="3030990695030371561">"בחירה"</string>
     <string name="deny_button" msgid="3766539809121892584">"דחייה"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"טעינת האישורים מתבצעת…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"‏מתבצע חיפוש אישורים על ידי %s…"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 2106384..c880836 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"外部ストレージにある、拡張子が%1$sまたは%2$sのPKCS#12ファイルから証明書をインストールできます。"</string>
     <string name="allow_button" msgid="3030990695030371561">"選択"</string>
     <string name="deny_button" msgid="3766539809121892584">"拒否"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"証明書を読み込んでいます..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s が証明書を確認しています…"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 88d7352..aa0e7e4 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"თქვენ შეგიძლიათ დააყენოთ სერტიფიკატი %1$s ან  %2$s გაფართოების ფაილიდან - PKCS#12,   რომელიც გარე მეხსიერებაში მდებარეობს."</string>
     <string name="allow_button" msgid="3030990695030371561">"არჩევა"</string>
     <string name="deny_button" msgid="3766539809121892584">"უარყოფა"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"მიმდინარეობს სერტიფიკატების ჩატვირთვა..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ამოწმებს სერტიფიკატის არსებობას…"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 01aa8e4..180c912 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Сертификаттарды сыртқы жадта орналасқан %1$s немесе %2$s жалғаулы PKCS#12 файлынан орнатуға болады."</string>
     <string name="allow_button" msgid="3030990695030371561">"Таңдау"</string>
     <string name="deny_button" msgid="3766539809121892584">"Тыйым салу"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Сертификаттар жүктеп алынуда..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s сертификатты тексеруде…"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 1e842d6..b80652d 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"អ្នក​អាច​ដំឡើង​វិញ្ញាបនបត្រ​ពី​ឯកសារ PKCS#12 ជា​មួយ​ផ្នែក​បន្ថែម %1$s ឬ %2$s ដែល​ស្ថិត​ក្នុង​ឧបករណ៍​ផ្ទុក​ខាង​ក្រៅ។"</string>
     <string name="allow_button" msgid="3030990695030371561">"ជ្រើស"</string>
     <string name="deny_button" msgid="3766539809121892584">"បដិសេធ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"កំពុងផ្ទុក​វិញ្ញាបនបត្រ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s កំពុងពិនិត្យមើលវិញ្ញាបនបត្រ…"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index b10decf..8d6b506 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆಯಲ್ಲಿ ಸಂಗ್ರಹವಾಗಿರುವ %1$s ಅಥವಾ %2$s ವಿಸ್ತರಣೆಯೊಂದಿಗೆ PKCS#12 ಫೈಲ್‌ನಿಂದ ನೀವು ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಬಹುದು."</string>
     <string name="allow_button" msgid="3030990695030371561">"ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ನಿರಾಕರಿಸಿ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"ಪ್ರಮಾಣಪತ್ರವನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ಪ್ರಮಾಣಪತ್ರ ಪರಿಶೀಲಿಸುತ್ತಿದೆ…"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 0c49e92..01f1e55 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"PKCS#12 파일에서 외부 저장소에 있는 확장 프로그램 %1$s 또는 %2$s을(를) 사용하여 인증서를 설치할 수 있습니다."</string>
     <string name="allow_button" msgid="3030990695030371561">"선택"</string>
     <string name="deny_button" msgid="3766539809121892584">"거부"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"인증서 로드 중..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s에서 인증서 확인 중…"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 058c450..3328e4c 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"PKCS#12 файлындагы тастыктамаларды тышкы сактагычта жайгашкан %1$s же %2$s кеңейтмеси менен орнотсоңуз болот."</string>
     <string name="allow_button" msgid="3030990695030371561">"Тандоо"</string>
     <string name="deny_button" msgid="3766539809121892584">"Баш тартуу"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Тастыктамалар жүктөлүүдө..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s тастыктаманы текшерүүдө…"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index bec8b2f..d3ec800 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"ທ່ານສາມາດຕິດຕັ້ງໃບຮັບຮອງຈາກໄຟລ໌ PKCS#12 ທີ່ມີນາມສະກຸນ %1$s ຫຼື %2$s ຢູ່ໃນພື້ນທີ່ຈັດເກັບຂໍ້ມູນພາຍນອກໄດ້."</string>
     <string name="allow_button" msgid="3030990695030371561">"ເລືອກ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ປະ​ຕິ​ເສດ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"ກຳລັງໂຫຼດໃບຮັບຮອງ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ກຳລັງກວດສອບໃບຮັບຮອງຢູ່…"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index d21776f..697c777 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Galite įdiegti sertifikatus iš failo PKCS#12 naudodami išorinėje saugykloje esantį plėtinį %1$s arba %2$s."</string>
     <string name="allow_button" msgid="3030990695030371561">"Pasirinkti"</string>
     <string name="deny_button" msgid="3766539809121892584">"Atmesti"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Įkeliami sertifikatai..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"Tikrinama, ar yra „%s“ sertifikatas…"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 8734df8..676ae33 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Jūs varat instalēt sertifikātus no PKCS#12 faila ar %1$s vai %2$s paplašinājumu, kas atrodas ārējā atmiņā."</string>
     <string name="allow_button" msgid="3030990695030371561">"Atlasīt"</string>
     <string name="deny_button" msgid="3766539809121892584">"Noraidīt"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Notiek sertifikātu ielāde..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s pārbauda, vai ir sertifikāts…"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index e3599be..88608ad 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Може да инсталирате сертификати од датотеката PKCS#12 со наставка %1$s или %2$s што се наоѓа на надворешната меморија."</string>
     <string name="allow_button" msgid="3030990695030371561">"Избери"</string>
     <string name="deny_button" msgid="3766539809121892584">"Одбиј"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Се вчитуваат сертификати…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s врши проверка за сертификат…"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 3d1ce38..6488515 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"ബാഹ്യസംഭരണത്തിലുള്ള %1$s അല്ലെങ്കിൽ %2$s എക്‌സ്റ്റൻഷൻ ഉപയോഗിച്ച് നിങ്ങൾക്ക് ഒരു PKCS#12 ഫയലിൽ സർട്ടിഫിക്കറ്റുകൾ ഇൻസ്റ്റാളുചെയ്യാനാകും."</string>
     <string name="allow_button" msgid="3030990695030371561">"തിരഞ്ഞെടുക്കുക"</string>
     <string name="deny_button" msgid="3766539809121892584">"നിരസിക്കുക"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"സർട്ടിഫിക്കറ്റുകൾ ലോഡ് ചെയ്യുന്നു..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s സർട്ടിഫിക്കറ്റ് പരിശോധിക്കുകയാണ്…"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index f250ab0..98315e9 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Та %1$s эсхүл %2$s өргөтгэлтэй гадаад хадгалах санд байрласан PKCS#12 файлаас сертификатуудыг суулгаж болно."</string>
     <string name="allow_button" msgid="3030990695030371561">"Сонгох"</string>
     <string name="deny_button" msgid="3766539809121892584">"Татгалзах"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Сертификатыг ачаалж байна..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s сертификатыг шалгаж байна…"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 61413cc..e036d40 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"तुम्ही अतिरिक्त स्टोरेजमध्ये स्थित %1$s किंवा %2$s विस्तारासह एका PKCS#12 फाइलवरुन प्रमाणपत्र इंस्टॉल करु शकता."</string>
     <string name="allow_button" msgid="3030990695030371561">"निवडा"</string>
     <string name="deny_button" msgid="3766539809121892584">"नकार द्या"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"प्रमाणपत्र लोड करत आहे…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s प्रमाणपत्र तपासात आहे…"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 0619ee8..bf7b1c6 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Anda boleh memasang sijil daripada fail PKCS#12 dengan sambungan %1$s atau %2$s yang terletak dalam storan luaran."</string>
     <string name="allow_button" msgid="3030990695030371561">"Pilih"</string>
     <string name="deny_button" msgid="3766539809121892584">"Nafikan"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Memuatkan sijil..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s sedang menyemak sijil…"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index a7eef5a..4e9eab6 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"အသိအမှတ်ပြုလက်မှတ်များကို ပြင်ပ သိုလှောင်ရာများမှာရှိသော %1$s သို့မဟုတ် %2$s ဖိုင်နောက်ဆက်တွဲပါသော  PKCS#12 ဖိုင် များမှ ထည့်သွင်းနိုင်သည်။"</string>
     <string name="allow_button" msgid="3030990695030371561">"ရွေးရန်"</string>
     <string name="deny_button" msgid="3766539809121892584">"ငြင်းပယ်ရန်"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"အသိ‌အမှတ်ပြုလက်မှတ်များကို ဖွင့်နေသည်..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s က အသိအမှတ်ပြုလက်မှတ်ကို ရှာဖွေနေသည်…"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index a7ba7a9..d48538c 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Du kan installere sertifikater fra en PKCS#12-fil med %1$s eller %2$s-utvidelse lagret på ekstern lagring."</string>
     <string name="allow_button" msgid="3030990695030371561">"Velg"</string>
     <string name="deny_button" msgid="3766539809121892584">"Avslå"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Laster inn sertifikater …"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ser etter et sertifikat …"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 505528c..5608497 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"तपाइँले प्रमाणपत्रहरू बाह्य भण्डारणमा अवस्थित %1$s वा एक %2$s विस्तारका साथ PKCS#12 फाइलबाट स्थापना गर्न सक्नुहुन्छ।"</string>
     <string name="allow_button" msgid="3030990695030371561">"चयन गर्नुहोस्"</string>
     <string name="deny_button" msgid="3766539809121892584">"अस्वीकार गर्नुहोस्"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"प्रमाणपत्रहरू लोड गर्दै..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ले प्रमाणपत्र छ कि छैन भन्ने कुरा जाँच्दै छ…"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 1499c5a..6a18d46 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Je kunt certificaten installeren uit een PKCS#12-bestand met de extensie %1$s of %2$s dat zich bevindt in de externe opslag."</string>
     <string name="allow_button" msgid="3030990695030371561">"Selecteren"</string>
     <string name="deny_button" msgid="3766539809121892584">"Weigeren"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Certificaten laden..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s checkt op een certificaat…"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 46a9e88..ce63034 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"ଏକ୍ସଟର୍ନଲ୍‌ ଷ୍ଟୋରେଜ୍‌ରେ ଥିବା ଏକ PKCS#12 ଫାଇଲ୍‌ରୁ ଆପଣ ସର୍ଟିଫିକେଟ୍‌ଗୁଡ଼ିକ ଇନ୍‌ଷ୍ଟଲ୍‌ କରିପାରିବେ, ଯାହାର ଏକ୍ସଟେନ୍ସନ୍‌ ହେଉଛି %1$s କିମ୍ବା ଏକ %2$s।"</string>
     <string name="allow_button" msgid="3030990695030371561">"ଚୟନ କରନ୍ତୁ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ଅଗ୍ରାହ୍ୟ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"ସାର୍ଟିଫିକେଟ୍‌ଗୁଡ଼ିକ ଲୋଡ୍ ହେଉଛି..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ଏକ ସାର୍ଟିଫିକେଟ୍ ପାଇଁ ଯାଞ୍ଚ କରୁଛି…"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index f71a630..5eace6a 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"ਤੁਸੀਂ ਬਾਹਰੀ ਸਟੋਰੇਜ ਵਿੱਚ ਸਥਿਤ ਇੱਕ %1$s ਜਾਂ ਇੱਕ %2$s ਐਕਸਟੈਂਸ਼ਨ ਨਾਲ ਇੱਕ PKCS#12 ਫ਼ਾਈਲ ਵਿੱਚੋਂ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।"</string>
     <string name="allow_button" msgid="3030990695030371561">"ਚੁਣੋ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"ਪ੍ਰਮਾਣ-ਪੱਤਰਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s ਵੱਲੋਂ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਜਾਂਚ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index d1f86c0..218ffb7 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Możesz zainstalować certyfikaty z pliku PKCS#12 z rozszerzeniem %1$s lub %2$s, który znajduje się w pamięci zewnętrznej."</string>
     <string name="allow_button" msgid="3030990695030371561">"Wybierz"</string>
     <string name="deny_button" msgid="3766539809121892584">"Odmów"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Wczytuję certyfikaty…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"Aplikacja %s sprawdza dostępność certifikatu…"</string>
 </resources>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index 65a5d8a..4d6576e 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Você pode instalar certificados a partir de um arquivo PKCS#12 com a extensão %1$s ou %2$s armazenado externamente."</string>
     <string name="allow_button" msgid="3030990695030371561">"Selecionar"</string>
     <string name="deny_button" msgid="3766539809121892584">"Negar"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Carregando certificados…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s está procurando um certificado…"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 8e78f58..d368795 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Pode instalar certificados a partir de um ficheiro PKCS#12 com uma extensão %1$s ou %2$s localizada no armazenamento externo."</string>
     <string name="allow_button" msgid="3030990695030371561">"Selecionar"</string>
     <string name="deny_button" msgid="3766539809121892584">"Recusar"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"A carregar os certificados…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s está a verificar a existência de um certificado…"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 65a5d8a..4d6576e 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Você pode instalar certificados a partir de um arquivo PKCS#12 com a extensão %1$s ou %2$s armazenado externamente."</string>
     <string name="allow_button" msgid="3030990695030371561">"Selecionar"</string>
     <string name="deny_button" msgid="3766539809121892584">"Negar"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Carregando certificados…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s está procurando um certificado…"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 9c280e7..56f6f22 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Puteți instala certificate dintr-un fișier PKCS#12 cu o extensie %1$s sau %2$s care se află pe stocarea externă."</string>
     <string name="allow_button" msgid="3030990695030371561">"Selectați"</string>
     <string name="deny_button" msgid="3766539809121892584">"Refuzați"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Se încarcă certificatele..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s caută certificatul…"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b163f46..3899c78 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Можно установить сертификаты из файла PKCS#12 с расширением %1$s или %2$s, который находится во внешнем хранилище."</string>
     <string name="allow_button" msgid="3030990695030371561">"Выбрать"</string>
     <string name="deny_button" msgid="3766539809121892584">"Отклонить"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Загрузка сертификатов…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s проверяет наличие сертификата…"</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index d4e66e9..5ebcacd 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"ඔබට බාහිර ආචයනයේ පිහිටුවා ඇති %1$s හෝ %2$s දිගුවක් සහිත PKCS#12 ගොනුවකින් සහතික ස්ථාපනය කළ හැක."</string>
     <string name="allow_button" msgid="3030990695030371561">"තෝරන්න"</string>
     <string name="deny_button" msgid="3766539809121892584">"තහනම්"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"සහතික පූරණය කරමින්…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s සහතිකයක් සඳහා පරීක්ෂා කරමින්…"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 2b2693e..40eb90f 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Certifikáty môžete inštalovať zo súboru PKCS#12 s príponou %1$s alebo %2$s, umiestneného v externom úložisku."</string>
     <string name="allow_button" msgid="3030990695030371561">"Vybrať"</string>
     <string name="deny_button" msgid="3766539809121892584">"Zamietnuť"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Načítavajú sa certifikáty…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s kontroluje certifikát…"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 9f67f7c..c5c19b7 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Namestite lahko potrdila iz datoteke PKCS#12 s pripono %1$s ali %2$s, ki je v zunanji shrambi."</string>
     <string name="allow_button" msgid="3030990695030371561">"Izberi"</string>
     <string name="deny_button" msgid="3766539809121892584">"Zavrni"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Nalaganje potrdil …"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s išče potrdilo …"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index e6e2d05..1e2644c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Mund të instalosh certifikata nga skedari PKCS#12 që gjendet në hapësirën e jashtme ruajtëse, vetëm nëse ato bartin prapashtesat %1$s ose %2$s."</string>
     <string name="allow_button" msgid="3030990695030371561">"Zgjidh"</string>
     <string name="deny_button" msgid="3766539809121892584">"Refuzo"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Certifikatat po ngarkohen..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s po kontrollon për një certifikatë…"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index e037cbb..5aa2769 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Можете да инсталирате сертификате из PKCS#12 датотеке са екстензијом %1$s или %2$s који се налази у спољној меморији."</string>
     <string name="allow_button" msgid="3030990695030371561">"Изабери"</string>
     <string name="deny_button" msgid="3766539809121892584">"Одбиј"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Учитавају се сертификати…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s проверава да ли постоји сертификат…"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index cabca96..15475b6 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Du kan installera certifikat från en PKCS#12-fil med filändelsen %1$s eller %2$s på en extern lagringsenhet."</string>
     <string name="allow_button" msgid="3030990695030371561">"Välj"</string>
     <string name="deny_button" msgid="3766539809121892584">"Neka"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Läser in certifikat …"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s söker efter ett certifikat …"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index f236a3c..553ced2 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Unaweza kusakinisha vyeti kutoka katika faili PKCS#12 kwa kirefusho %1$s au %2$s kilicho katika hifadhi ya nje."</string>
     <string name="allow_button" msgid="3030990695030371561">"Chagua"</string>
     <string name="deny_button" msgid="3766539809121892584">"Kataa"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Inapakia vyeti..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s inatafuta cheti…"</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 687af73..4d79148 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"வெளிப்புறச் சேமிப்பிடத்தில் உள்ள %1$s அல்லது %2$s நீட்டிப்புடன் PKCS#12 கோப்பிலிருந்து நீங்கள் சான்றிதழ்களை நிறுவலாம்."</string>
     <string name="allow_button" msgid="3030990695030371561">"தேர்ந்தெடு"</string>
     <string name="deny_button" msgid="3766539809121892584">"நிராகரி"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"சான்றிதழ்களைக் காட்டுகிறது..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s சான்றிதழைத் தேடுகிறது…"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index afa25e7..d7e73c1 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -20,8 +20,8 @@
     <string name="title_select_cert" msgid="3588447616418041699">"ప్రమాణపత్రాన్ని ఎంచుకోండి"</string>
     <string name="requesting_application" msgid="1589142627467598421">"యాప్ %s ప్రమాణపత్రాన్ని అభ్యర్థించింది. ప్రమాణపత్రాన్ని ఎంచుకోవడం వలన ఇప్పుడు మరియు భవిష్యత్తులో సర్వర్‌లతో ఈ గుర్తింపును ఉపయోగించడానికి యాప్ అనుమతించబడుతుంది."</string>
     <string name="requesting_server" msgid="5832565605998634370">"యాప్ అభ్యర్థిస్తున్న సర్వర్‌‌ను %sగా గుర్తించింది, కానీ మీరు యాప్‌ను విశ్వసిస్తే మాత్రమే సర్టిఫికెట్ కోసం యాప్‌నకు యాక్సెస్‌ను అందించాలి."</string>
-    <string name="install_new_cert_message" msgid="4451971501142085495">"మీరు బాహ్య నిల్వలో ఉండే %1$s లేదా %2$s ఎక్స్‌టెన్షన్‌తో PKCS#12 ఫైల్ నుండి ప్రమాణపత్రాలను ఇన్‌స్టాల్ చేయవచ్చు."</string>
+    <string name="install_new_cert_message" msgid="4451971501142085495">"మీరు బాహ్య నిల్వలో ఉండే %1$s లేదా %2$s పొడిగింపుతో PKCS#12 ఫైల్ నుండి ప్రమాణపత్రాలను ఇన్‌స్టాల్ చేయవచ్చు."</string>
     <string name="allow_button" msgid="3030990695030371561">"ఎంచుకోండి"</string>
     <string name="deny_button" msgid="3766539809121892584">"తిరస్కరించు"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"సర్టిఫికెట్‌లు లోడ్ అవుతున్నాయి..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s సర్టిఫికెట్ కోసం చెక్ చేస్తున్నారు…"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 13c8558..d05adeb 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"คุณสามารถติดตั้งใบรับรองจากไฟล์ PKCS#12 ที่มีนามสกุล %1$s หรือ %2$s ที่อยู่ในที่จัดเก็บข้อมูลภายนอก"</string>
     <string name="allow_button" msgid="3030990695030371561">"เลือก"</string>
     <string name="deny_button" msgid="3766539809121892584">"ปฏิเสธ"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"กำลังโหลดใบรับรอง..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s กำลังตรวจสอบใบรับรอง…"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 3fa0f46..06fae2f 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Maaari kang mag-install ng mga certificate mula sa isang PKCS#12 file na may %1$s o %2$s na extension na matatagpuan sa external storage."</string>
     <string name="allow_button" msgid="3030990695030371561">"Piliin"</string>
     <string name="deny_button" msgid="3766539809121892584">"Tanggihan"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Nilo-load ang mga certificate..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"Tumitingin ang %s ng certificate…"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3c745b2..6bc2b43 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Sertifikaları harici depolama biriminde bulunan %1$s veya %2$s uzantılı bir PKCS#12 dosyasından yükleyebilirsiniz."</string>
     <string name="allow_button" msgid="3030990695030371561">"Seç"</string>
     <string name="deny_button" msgid="3766539809121892584">"Reddet"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Sertifikalar yükleniyor…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s, sertifika olup olmadığına bakıyor…"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 1fa1b50..0226ccc 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Можна встановлювати сертифікати з файлу PKCS#12 із розширенням %1$s або %2$s, розміщеного в зовнішній пам’яті."</string>
     <string name="allow_button" msgid="3030990695030371561">"Вибрати"</string>
     <string name="deny_button" msgid="3766539809121892584">"Заборонити"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Завантаження сертифікатів…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s завантажує сертифікати…"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index e548886..ae63ea4 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"‏آپ بیرونی اسٹوریج میں موجود ایک ‎%1$s یا ‎%2$s ایکسٹنشن کے ساتھ ایک PKCS#12 فائل سے سرٹیفکیٹس انسٹال کر سکتے ہیں۔"</string>
     <string name="allow_button" msgid="3030990695030371561">"منتخب کریں"</string>
     <string name="deny_button" msgid="3766539809121892584">"مسترد کریں"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"سرٹیفیکیٹس لوڈ ہو رہے ہیں..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"‏‎%s سرٹیفیکیٹ کی جانچ کر رہی ہے…"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 605bf1a..a8c1deb 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Sertifikatlarni tashqi xotirada joylashgan %1$s yoki %2$s kengaytmali PKCS#12 faylidan o‘rnatishingiz mumkin."</string>
     <string name="allow_button" msgid="3030990695030371561">"Tanlash"</string>
     <string name="deny_button" msgid="3766539809121892584">"Rad etish"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Sertifikatlar yuklanmoqda..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s sertifikatni tekshirmoqda…"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 17a86f7..d219f62 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Bạn có thể cài đặt chứng chỉ từ tệp PKCS#12 với tiện ích mở rộng %1$s hoặc %2$s nằm trong bộ nhớ ngoài."</string>
     <string name="allow_button" msgid="3030990695030371561">"Chọn"</string>
     <string name="deny_button" msgid="3766539809121892584">"Từ chối"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Đang tải chứng chỉ..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s đang tìm chứng chỉ…"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7274056..485608e 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"您可以安装PKCS#12文件(位于外部存储设备中且扩展名为%1$s或%2$s)中的证书。"</string>
     <string name="allow_button" msgid="3030990695030371561">"选择"</string>
     <string name="deny_button" msgid="3766539809121892584">"拒绝"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"正在加载证书…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s正在检查证书…"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 988715a..890ff10 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"您可以從外部儲存裝置中副檔名為 %1$s 或 %2$s 的 PKCS#12 檔案安裝憑證。"</string>
     <string name="allow_button" msgid="3030990695030371561">"選取"</string>
     <string name="deny_button" msgid="3766539809121892584">"拒絕"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"正在載入憑證…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"%s 正在檢查憑證…"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 57aae21..93c151f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"你可以從外部儲存裝置中含有 %1$s 或 %2$s 擴充功能的 PKCS#12 檔案安裝憑證。"</string>
     <string name="allow_button" msgid="3030990695030371561">"選取"</string>
     <string name="deny_button" msgid="3766539809121892584">"拒絕"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"正在載入憑證…"</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"「%s」正在確認憑證…"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 2fd34d3..1a1148f 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -23,5 +23,5 @@
     <string name="install_new_cert_message" msgid="4451971501142085495">"Ungafaka izitifiketi kusuka efayeleni le-PKCS#12 ngesandiso se-%1$s noma se-%2$s esibekwe esilondolozini sangaphandle."</string>
     <string name="allow_button" msgid="3030990695030371561">"Khetha"</string>
     <string name="deny_button" msgid="3766539809121892584">"Nqaba"</string>
-    <string name="loading_certs_message" msgid="814752048905775439">"Ilayisha izitifiketi..."</string>
+    <string name="loading_certs_message" msgid="2152223341043639547">"I-%s ihlola isitifiketi…"</string>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2da7102..7cc4b11 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -35,5 +35,5 @@
     <string name="deny_button">Deny</string>
 
     <!-- Text to show while the KeyChain activity is loading certificates. -->
-    <string name="loading_certs_message">Loading certificates...</string>
+    <string name="loading_certs_message">%s is checking for a certificate&#8230;</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2e1081b..6135255 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,9 +18,10 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="KeyChainTransparent" parent="@android:style/Theme.DeviceDefault.DayNight">
+    <style name="KeyChainTransparent" parent="@style/Theme.AppCompat.DayNight.NoActionBar">
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowIsFloating">true</item>
+        <item name="android:backgroundDimEnabled">false</item>
     </style>
 </resources>
diff --git a/robotests/Android.bp b/robotests/Android.bp
index b6a6f4e..8ddbfc5 100644
--- a/robotests/Android.bp
+++ b/robotests/Android.bp
@@ -2,6 +2,10 @@
 // KeyChain Robolectric test target.                         #
 //############################################################
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_robolectric_test {
     name: "KeyChainRoboTests",
 
diff --git a/robotests/src/com/android/keychain/AliasLoaderTest.java b/robotests/src/com/android/keychain/AliasLoaderTest.java
index 78c6c85..130edde 100644
--- a/robotests/src/com/android/keychain/AliasLoaderTest.java
+++ b/robotests/src/com/android/keychain/AliasLoaderTest.java
@@ -19,16 +19,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.security.Credentials;
-import android.security.KeyStore;
 import android.util.Base64;
+
 import com.android.keychain.internal.KeyInfoProvider;
-import java.util.ArrayList;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.security.auth.x500.X500Principal;
+
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -38,6 +34,22 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowApplication;
 
+import java.io.ByteArrayInputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.security.auth.x500.X500Principal;
+
 @RunWith(RobolectricTestRunner.class)
 public final class AliasLoaderTest {
     // Generated using:
@@ -125,9 +137,22 @@
     private byte[] mECCertOne;
     private byte[] mECCertTwo;
     private ArrayList<byte[]> mIssuers;
+    private KeyStoreSpi mKeyStoreSpi;
+    private KeyStore mKeyStore;
+    private KeyInfoProvider mInfoProvider;
+
+    private Certificate toCertificate(byte[] bytes) throws CertificateException {
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        return cf.generateCertificate(new ByteArrayInputStream(bytes));
+    }
+
+    private Certificate[] toCertificateChain(byte[] bytes) throws CertificateException {
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        return new Certificate[]{cf.generateCertificate(new ByteArrayInputStream(bytes))};
+    }
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         mRSACertOne = Base64.decode(SELF_SIGNED_RSA_CERT_1_B64, Base64.DEFAULT);
         mRSACertTwo = Base64.decode(SELF_SIGNED_RSA_CERT_2_B64, Base64.DEFAULT);
         mECCertOne = Base64.decode(SELF_SIGNED_EC_CERT_1_B64, Base64.DEFAULT);
@@ -143,18 +168,22 @@
 
         mDummyChecker = mock(KeyChainActivity.CertificateParametersFilter.class);
         when(mDummyChecker.shouldPresentCertificate(Mockito.anyString())).thenReturn(true);
+
+        mKeyStoreSpi = mock(KeyStoreSpi.class);
+        mKeyStore = new KeyStore(mKeyStoreSpi, null, "test") {};
+        mKeyStore.load(null);
+        mInfoProvider = mock(KeyInfoProvider.class);
     }
 
     @Test
     public void testAliasLoader_loadsAllAliases()
             throws InterruptedException, ExecutionException, CancellationException,
-                    TimeoutException {
-        KeyStore keyStore = mock(KeyStore.class);
-        when(keyStore.list(Credentials.USER_PRIVATE_KEY)).thenReturn(new String[] {"b", "c", "a"});
+            TimeoutException {
+        prepareKeyStoreWithAliases(ImmutableList.of("b", "c", "a"));
 
         KeyChainActivity.AliasLoader loader =
                 new KeyChainActivity.AliasLoader(
-                        keyStore,
+                        mKeyStore,
                         RuntimeEnvironment.application,
                         mDummyInfoProvider,
                         mDummyChecker);
@@ -173,12 +202,11 @@
     public void testAliasLoader_copesWithNoAliases()
             throws InterruptedException, ExecutionException, CancellationException,
                     TimeoutException {
-        KeyStore keyStore = mock(KeyStore.class);
-        when(keyStore.list(Credentials.USER_PRIVATE_KEY)).thenReturn(null);
+        when(mKeyStoreSpi.engineAliases()).thenReturn(Collections.enumeration(ImmutableList.of()));
 
         KeyChainActivity.AliasLoader loader =
                 new KeyChainActivity.AliasLoader(
-                        keyStore,
+                        mKeyStore,
                         RuntimeEnvironment.application,
                         mDummyInfoProvider,
                         mDummyChecker);
@@ -194,17 +222,14 @@
     public void testAliasLoader_filtersNonUserSelectableAliases()
             throws InterruptedException, ExecutionException, CancellationException,
                     TimeoutException {
-        KeyStore keyStore = mock(KeyStore.class);
-        when(keyStore.list(Credentials.USER_PRIVATE_KEY)).thenReturn(new String[] {"a", "b", "c"});
-
-        KeyInfoProvider infoProvider = mock(KeyInfoProvider.class);
-        when(infoProvider.isUserSelectable("a")).thenReturn(false);
-        when(infoProvider.isUserSelectable("b")).thenReturn(true);
-        when(infoProvider.isUserSelectable("c")).thenReturn(false);
+        prepareKeyStoreWithAliases(ImmutableList.of("b", "c", "a"));
+        when(mInfoProvider.isUserSelectable("a")).thenReturn(false);
+        when(mInfoProvider.isUserSelectable("b")).thenReturn(true);
+        when(mInfoProvider.isUserSelectable("c")).thenReturn(false);
 
         KeyChainActivity.AliasLoader loader =
                 new KeyChainActivity.AliasLoader(
-                        keyStore, RuntimeEnvironment.application, infoProvider, mDummyChecker);
+                        mKeyStore, RuntimeEnvironment.application, mInfoProvider, mDummyChecker);
         loader.execute();
 
         ShadowApplication.runBackgroundTasks();
@@ -217,16 +242,12 @@
     @Test
     public void testAliasLoader_filtersAliasesWithNonConformingParameters()
             throws InterruptedException, ExecutionException, CancellationException,
-                    TimeoutException {
-        KeyStore keyStore = mock(KeyStore.class);
-        when(keyStore.list(Credentials.USER_PRIVATE_KEY))
-                .thenReturn(new String[] {"a", "b", "c", "d"});
-
-        KeyInfoProvider infoProvider = mock(KeyInfoProvider.class);
-        when(infoProvider.isUserSelectable("a")).thenReturn(true);
-        when(infoProvider.isUserSelectable("b")).thenReturn(true);
-        when(infoProvider.isUserSelectable("c")).thenReturn(false);
-        when(infoProvider.isUserSelectable("d")).thenReturn(false);
+                    TimeoutException, KeyStoreException {
+        prepareKeyStoreWithAliases(ImmutableList.of("a", "b", "c", "d"));
+        when(mInfoProvider.isUserSelectable("a")).thenReturn(true);
+        when(mInfoProvider.isUserSelectable("b")).thenReturn(true);
+        when(mInfoProvider.isUserSelectable("c")).thenReturn(false);
+        when(mInfoProvider.isUserSelectable("d")).thenReturn(false);
 
         KeyChainActivity.CertificateParametersFilter checker =
                 mock(KeyChainActivity.CertificateParametersFilter.class);
@@ -241,7 +262,7 @@
 
         KeyChainActivity.AliasLoader loader =
                 new KeyChainActivity.AliasLoader(
-                        keyStore, RuntimeEnvironment.application, infoProvider, checker);
+                        mKeyStore, RuntimeEnvironment.application, mInfoProvider, checker);
         loader.execute();
 
         ShadowApplication.runBackgroundTasks();
@@ -251,40 +272,55 @@
         Assert.assertEquals("a", result.getItem(0));
     }
 
-    private KeyStore prepareKeyStoreWithCertificates() {
-        KeyStore keyStore = mock(KeyStore.class);
-        when(keyStore.get(Credentials.USER_CERTIFICATE + "rsa1")).thenReturn(mRSACertOne);
-        when(keyStore.get(Credentials.USER_CERTIFICATE + "ec1")).thenReturn(mECCertOne);
-        when(keyStore.get(Credentials.USER_CERTIFICATE + "rsa2")).thenReturn(mRSACertTwo);
-        when(keyStore.get(Credentials.USER_CERTIFICATE + "ec2")).thenReturn(mECCertTwo);
+    private void prepareKeyStoreWithAliases(ImmutableList<String> aliases) {
+        when(mKeyStoreSpi.engineAliases()).thenReturn(Collections.enumeration(aliases));
+        for (int i = 0; i < aliases.size(); i++) {
+            when(mKeyStoreSpi.engineIsKeyEntry(aliases.get(i))).thenReturn(true);
+        }
+    }
 
-        return keyStore;
+    private void prepareKeyStoreWithCertificates() throws CertificateException {
+        when(mKeyStoreSpi.engineGetCertificate("rsa1")).thenReturn(toCertificate(mRSACertOne));
+        when(mKeyStoreSpi.engineGetCertificate("ec1")).thenReturn(toCertificate(mECCertOne));
+        when(mKeyStoreSpi.engineGetCertificate("rsa2")).thenReturn(toCertificate(mRSACertTwo));
+        when(mKeyStoreSpi.engineGetCertificate("ec2")).thenReturn(toCertificate(mECCertTwo));
+
+        when(mKeyStoreSpi.engineGetCertificateChain("rsa1"))
+                .thenReturn(toCertificateChain(mRSACertOne));
+        when(mKeyStoreSpi.engineGetCertificateChain("ec1"))
+                .thenReturn(toCertificateChain(mECCertOne));
+        when(mKeyStoreSpi.engineGetCertificateChain("rsa2"))
+                .thenReturn(toCertificateChain(mRSACertTwo));
+        when(mKeyStoreSpi.engineGetCertificateChain("ec2"))
+                .thenReturn(toCertificateChain(mECCertTwo));
     }
 
     @Test
-    public void testCertificateParametersFilter_filtersByKey() throws CancellationException {
-        KeyStore keyStore = prepareKeyStoreWithCertificates();
+    public void testCertificateParametersFilter_filtersByKey()
+            throws CancellationException, CertificateException {
+        prepareKeyStoreWithCertificates();
 
         KeyChainActivity.CertificateParametersFilter ec_checker =
                 new KeyChainActivity.CertificateParametersFilter(
-                        keyStore, new String[] {"EC"}, new ArrayList<byte[]>());
+                        mKeyStore, new String[] {"EC"}, new ArrayList<byte[]>());
         Assert.assertFalse(ec_checker.shouldPresentCertificate("rsa1"));
         Assert.assertTrue(ec_checker.shouldPresentCertificate("ec1"));
 
         KeyChainActivity.CertificateParametersFilter rsa_and_ec_checker =
                 new KeyChainActivity.CertificateParametersFilter(
-                        keyStore, new String[] {"EC", "RSA"}, new ArrayList<byte[]>());
+                        mKeyStore, new String[] {"EC", "RSA"}, new ArrayList<byte[]>());
         Assert.assertTrue(rsa_and_ec_checker.shouldPresentCertificate("rsa1"));
         Assert.assertTrue(rsa_and_ec_checker.shouldPresentCertificate("ec1"));
     }
 
     @Test
-    public void testCertificateParametersFilter_filtersByIssuer() throws CancellationException {
-        KeyStore keyStore = prepareKeyStoreWithCertificates();
+    public void testCertificateParametersFilter_filtersByIssuer()
+            throws CancellationException, CertificateException {
+        prepareKeyStoreWithCertificates();
 
         KeyChainActivity.CertificateParametersFilter issuer_checker =
                 new KeyChainActivity.CertificateParametersFilter(
-                        keyStore, new String[] {}, mIssuers);
+                        mKeyStore, new String[] {}, mIssuers);
         Assert.assertTrue(issuer_checker.shouldPresentCertificate("rsa1"));
         Assert.assertTrue(issuer_checker.shouldPresentCertificate("ec1"));
         Assert.assertFalse(issuer_checker.shouldPresentCertificate("rsa2"));
@@ -293,13 +329,12 @@
 
     @Test
     public void testCertificateParametersFilter_filtersByIssuerAndKey()
-            throws InterruptedException, ExecutionException, CancellationException,
-                    TimeoutException {
-        KeyStore keyStore = prepareKeyStoreWithCertificates();
+            throws CancellationException, CertificateException {
+        prepareKeyStoreWithCertificates();
 
         KeyChainActivity.CertificateParametersFilter issuer_checker =
                 new KeyChainActivity.CertificateParametersFilter(
-                        keyStore, new String[] {"EC"}, mIssuers);
+                        mKeyStore, new String[] {"EC"}, mIssuers);
         Assert.assertFalse(issuer_checker.shouldPresentCertificate("rsa1"));
         Assert.assertTrue(issuer_checker.shouldPresentCertificate("ec1"));
         Assert.assertFalse(issuer_checker.shouldPresentCertificate("rsa2"));
diff --git a/robotests/src/com/android/keychain/KeyChainServiceRoboTest.java b/robotests/src/com/android/keychain/KeyChainServiceRoboTest.java
index 8594e56..6392af4 100644
--- a/robotests/src/com/android/keychain/KeyChainServiceRoboTest.java
+++ b/robotests/src/com/android/keychain/KeyChainServiceRoboTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -33,10 +34,11 @@
 import android.app.admin.SecurityLog;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.security.AppUriAuthenticationPolicy;
 import android.security.IKeyChainService;
 
 import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.keychain.ShadowKeyStore;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,6 +54,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.security.KeyStore;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
@@ -61,9 +64,11 @@
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {
     ShadowTrustedCertificateStore.class,
-    ShadowKeyStore.class
 })
 public final class KeyChainServiceRoboTest {
+
+    private static final String DEFAULT_KEYSTORE_TYPE = "BKS";
+
     private IKeyChainService.Stub mKeyChain;
 
     @Mock
@@ -100,6 +105,15 @@
 
     private static final String NON_EXISTING_ALIAS = "alias-does-not-exist-1";
 
+    private static final String TEST_PACKAGE_NAME_1 = "com.android.test";
+    private static final Uri TEST_URI_1 = Uri.parse("test.com");
+    private static final String TEST_ALIAS_1 = "testAlias";
+    private static final String CREDENTIAL_MANAGER_PACKAGE = "com.android.cred.mng.pkg";
+    private static final AppUriAuthenticationPolicy AUTHENTICATION_POLICY =
+            new AppUriAuthenticationPolicy.Builder()
+                    .addAppAndUriMapping(TEST_PACKAGE_NAME_1, TEST_URI_1, TEST_ALIAS_1)
+                    .build();
+
     private X509Certificate mCert;
     private String mSubject;
     private ShadowPackageManager mShadowPackageManager;
@@ -116,9 +130,13 @@
         mShadowPackageManager = shadowOf(packageManager);
 
         final ServiceController<KeyChainService> serviceController =
-                Robolectric.buildService(KeyChainService.class).create().bind();
+                Robolectric.buildService(KeyChainService.class);
         final KeyChainService service = serviceController.get();
         service.setInjector(mockInjector);
+        doReturn(KeyStore.getInstance(DEFAULT_KEYSTORE_TYPE))
+                .when(mockInjector).getKeyStoreInstance();
+        serviceController.create().bind();
+
         final Intent intent = new Intent(IKeyChainService.class.getName());
         mKeyChain = (IKeyChainService.Stub) service.onBind(intent);
     }
@@ -239,6 +257,83 @@
         assertThat(certificate).isNull();
     }
 
+    @Test
+    public void testHasCredentialManagementApp_noManagementApp_returnsFalse() throws Exception {
+        setUpSystemCaller();
+        assertFalse(mKeyChain.hasCredentialManagementApp());
+    }
+
+    @Test
+    public void testGetCredentialManagementAppPackageName_noManagementApp_returnsNull()
+            throws Exception {
+        setUpSystemCaller();
+        assertThat(mKeyChain.getCredentialManagementAppPackageName()).isNull();
+    }
+
+    @Test
+    public void testGetCredentialManagementAppPolicy_noManagementApp_returnsNull()
+            throws Exception {
+        setUpSystemCaller();
+        assertThat(mKeyChain.getCredentialManagementAppPolicy()).isNull();
+    }
+
+    @Test
+    public void testGetPredefinedAliasForPackageAndUri_noManagementApp_returnsNull()
+            throws Exception {
+        setUpSystemCaller();
+        assertThat(mKeyChain.getPredefinedAliasForPackageAndUri(TEST_PACKAGE_NAME_1,
+                TEST_URI_1)).isNull();
+    }
+
+    @Test
+    public void testHasCredentialManagement_hasManagementApp_returnsTrue() throws Exception {
+        setUpSystemCaller();
+        mKeyChain.setCredentialManagementApp(CREDENTIAL_MANAGER_PACKAGE, AUTHENTICATION_POLICY);
+
+        assertTrue(mKeyChain.hasCredentialManagementApp());
+    }
+
+    @Test
+    public void testGetCredentialManagementAppPackageName_hasManagementApp_returnsPackageName()
+            throws Exception {
+        setUpSystemCaller();
+        mKeyChain.setCredentialManagementApp(CREDENTIAL_MANAGER_PACKAGE, AUTHENTICATION_POLICY);
+
+        assertThat(mKeyChain.getCredentialManagementAppPackageName())
+                .isEqualTo(CREDENTIAL_MANAGER_PACKAGE);
+    }
+
+    @Test
+    public void testGetCredentialManagementAppPolicy_hasManagementApp_returnsPolicy()
+            throws Exception {
+        setUpSystemCaller();
+        mKeyChain.setCredentialManagementApp(CREDENTIAL_MANAGER_PACKAGE, AUTHENTICATION_POLICY);
+
+        assertThat(mKeyChain.getCredentialManagementAppPolicy()).isEqualTo(AUTHENTICATION_POLICY);
+    }
+
+    @Test
+    public void testGetPredefinedAliasForPackageAndUri_hasManagementApp_returnsCorrectAlias()
+            throws Exception {
+        setUpSystemCaller();
+        mKeyChain.setCredentialManagementApp(CREDENTIAL_MANAGER_PACKAGE, AUTHENTICATION_POLICY);
+
+        assertThat(mKeyChain.getPredefinedAliasForPackageAndUri(TEST_PACKAGE_NAME_1, TEST_URI_1))
+                .isEqualTo(TEST_ALIAS_1);
+    }
+
+    @Test
+    public void testRemoveCredentialManagementApp_hasManagementApp_removesManagementApp()
+            throws Exception {
+        setUpSystemCaller();
+
+        mKeyChain.removeCredentialManagementApp();
+
+        assertFalse(mKeyChain.hasCredentialManagementApp());
+        assertThat(mKeyChain.getCredentialManagementAppPackageName()).isNull();
+        assertThat(mKeyChain.getCredentialManagementAppPolicy()).isNull();
+    }
+
     private void setUpLoggingAndAccess(boolean loggingEnabled) {
         doReturn(loggingEnabled).when(mockInjector).isSecurityLoggingEnabled();
 
@@ -246,6 +341,10 @@
         setUpCaller(1000, "android.uid.system:1000");
     }
 
+    private void setUpSystemCaller() {
+        setUpCaller(1000, "android.uid.system:1000");
+    }
+
     private void setUpCaller(int uid, String packageName) {
         doReturn(uid).when(mockInjector).getCallingUid();
         mShadowPackageManager.setNameForUid(uid, packageName);
diff --git a/robotests/src/com/android/keychain/ShadowKeyStore.java b/robotests/src/com/android/keychain/ShadowKeyStore.java
deleted file mode 100644
index 1a7a2b3..0000000
--- a/robotests/src/com/android/keychain/ShadowKeyStore.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.keychain;
-
-import android.security.KeyStore;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-import java.util.List;
-
-@Implements(KeyStore.class)
-public class ShadowKeyStore {
-    @Implementation
-    public String[] list(String prefix) {
-        return new String[0];
-    }
-}
diff --git a/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java b/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
index e5212da..4cf552f 100644
--- a/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
+++ b/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
@@ -369,4 +369,18 @@
             Assert.assertTrue(mGrantsDB.isUserSelectable(alias));
         }
     }
+
+    @Test
+    public void testGetGrants_empty() {
+        Assert.assertArrayEquals(new int[0], mGrantsDB.getGrants(DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testGetGrants_nonEmpty() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        mGrantsDB.setGrant(DUMMY_UID + 1, DUMMY_ALIAS, true);
+
+        Assert.assertArrayEquals(
+                new int[]{DUMMY_UID, DUMMY_UID + 1}, mGrantsDB.getGrants(DUMMY_ALIAS));
+    }
 }
diff --git a/src/com/android/keychain/KeyChainActivity.java b/src/com/android/keychain/KeyChainActivity.java
index 078e377..105dc6a 100644
--- a/src/com/android/keychain/KeyChainActivity.java
+++ b/src/com/android/keychain/KeyChainActivity.java
@@ -17,25 +17,29 @@
 package com.android.keychain;
 
 import android.annotation.NonNull;
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.security.Credentials;
+import android.os.UserManager;
 import android.security.IKeyChainAliasCallback;
 import android.security.KeyChain;
-import android.security.KeyStore;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,29 +49,46 @@
 import android.widget.RadioButton;
 import android.widget.TextView;
 
+import androidx.appcompat.app.AppCompatActivity;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keychain.internal.KeyInfoProvider;
-import com.android.org.bouncycastle.asn1.x509.X509Name;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
+import com.google.android.material.snackbar.Snackbar;
+
+import org.bouncycastle.asn1.x509.X509Name;
+
 import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 
 import javax.security.auth.x500.X500Principal;
 
-public class KeyChainActivity extends Activity {
+public class KeyChainActivity extends AppCompatActivity {
     private static final String TAG = "KeyChain";
 
+    // The amount of time to delay showing a snackbar. If the alias is received before the snackbar
+    // is shown, the activity will finish. If the certificate selection dialog is shown before the
+    // snackbar, no snackbar will be shown.
+    private static final long SNACKBAR_DELAY_TIME = 2000;
+    // The minimum amount of time to display a snackbar while loading certificates.
+    private static final long SNACKBAR_MIN_TIME = 1000;
+
     private int mSenderUid;
+    private String mSenderPackageName;
 
     private PendingIntent mSender;
 
@@ -75,11 +96,39 @@
     // get do file I/O in the remote keystore process and while they
     // do not cause StrictMode violations, they logically should not
     // be done on the UI thread.
-    private KeyStore mKeyStore = KeyStore.getInstance();
+    private final KeyStore mKeyStore = getKeyStore();
 
-    // A dialog to show the user while the KeyChain Activity is loading the
-    // certificates.
-    AlertDialog mLoadingDialog;
+    private static KeyStore getKeyStore() {
+        try {
+            final KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
+            keystore.load(null);
+            return keystore;
+        } catch (KeyStoreException | IOException | NoSuchAlgorithmException
+                | CertificateException e) {
+            Log.e(TAG, "Error opening AndroidKeyStore.", e);
+            throw new RuntimeException("Error opening AndroidKeyStore.", e);
+        }
+    }
+
+    // A snackbar to show the user while the KeyChain Activity is loading the certificates.
+    private Snackbar mSnackbar;
+
+    // A remote service may call {@link android.security.KeyChain#choosePrivateKeyAlias} multiple
+    // times, which will result in multiple intents being sent to KeyChainActivity. The time of the
+    // first received intent is recorded in order to ensure the snackbar is displayed for a
+    // minimum amount of time after receiving the first intent.
+    private long mFirstIntentReceivedTimeMillis = 0L;
+
+    private ExecutorService executor = Executors.newSingleThreadExecutor();
+    private Handler handler = new Handler(Looper.getMainLooper());
+    private final Runnable mFinishActivity = KeyChainActivity.this::finish;
+    private final Runnable mShowSnackBar = this::showSnackBar;
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+        setContentView(R.layout.keychain_activity);
+    }
 
     @Override public void onResume() {
         super.onResume();
@@ -91,8 +140,12 @@
             return;
         }
         try {
+            // getTargetPackage guarantees that the returned string is
+            // supplied by the system, so that an application can not
+            // spoof its package.
+            mSenderPackageName = mSender.getIntentSender().getTargetPackage();
             mSenderUid = getPackageManager().getPackageInfo(
-                    mSender.getIntentSender().getTargetPackage(), 0).applicationInfo.uid;
+                    mSenderPackageName, 0).applicationInfo.uid;
         } catch (PackageManager.NameNotFoundException e) {
             // if unable to find the sender package info bail,
             // we need to identify the app to the user securely.
@@ -103,14 +156,27 @@
         chooseCertificate();
     }
 
-    private void showLoadingDialog() {
-        final Context themedContext = new ContextThemeWrapper(
-                this, com.android.internal.R.style.Theme_Translucent_NoTitleBar);
-        mLoadingDialog = new AlertDialog.Builder(themedContext)
-                .setTitle(R.string.app_name)
-                .setMessage(R.string.loading_certs_message)
-                .create();
-        mLoadingDialog.show();
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        handler.removeCallbacks(mFinishActivity);
+    }
+
+    private void showSnackBar() {
+        mFirstIntentReceivedTimeMillis = System.currentTimeMillis();
+        mSnackbar = Snackbar.make(findViewById(R.id.container),
+                String.format(getResources().getString(R.string.loading_certs_message),
+                        getApplicationLabel()), Snackbar.LENGTH_INDEFINITE);
+        mSnackbar.show();
+    }
+
+    private void finishSnackBar() {
+        if (mSnackbar != null) {
+            mSnackbar.dismiss();
+            mSnackbar = null;
+        } else {
+            handler.removeCallbacks(mShowSnackBar);
+        }
     }
 
     private void chooseCertificate() {
@@ -182,38 +248,99 @@
                     finish(null);
                     return;
                 }
-                runOnUiThread(new Runnable() {
-                    @Override public void run() {
-                        if (mLoadingDialog != null) {
-                            mLoadingDialog.dismiss();
-                            mLoadingDialog = null;
-                        }
-                        displayCertChooserDialog(certAdapter);
-                    }
+                runOnUiThread(() -> {
+                    finishSnackBar();
+                    displayCertChooserDialog(certAdapter);
                 });
             }
         };
 
-        // Show a dialog to the user to indicate long-running task.
-        showLoadingDialog();
-        // 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));
-
+        // Show a snackbar to the user to indicate long-running task.
+        if (mSnackbar == null) {
+            handler.postDelayed(mShowSnackBar, SNACKBAR_DELAY_TIME);
+        }
         Uri uri = getIntent().getParcelableExtra(KeyChain.EXTRA_URI);
         String alias = getIntent().getStringExtra(KeyChain.EXTRA_ALIAS);
-        try {
-            devicePolicyManager.choosePrivateKeyAlias(mSenderUid, uri, alias, callback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to request alias from DevicePolicyManager", e);
-            // Proceed without a suggested alias.
+
+        if (isManagedDevice()) {
+            // 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));
             try {
-                callback.alias(null);
-            } catch (RemoteException shouldNeverHappen) {
-                finish(null);
+                devicePolicyManager.choosePrivateKeyAlias(mSenderUid, uri, alias, callback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to request alias from DevicePolicyManager", e);
+                // Proceed without a suggested alias.
+                try {
+                    callback.alias(null);
+                } catch (RemoteException shouldNeverHappen) {
+                    finish(null);
+                }
+            }
+        } else {
+            // If the device is unmanaged, check whether the credential management app has provided
+            // an alias for the given uri and calling package name.
+            getAliasFromCredentialManagementApp(uri, callback);
+        }
+    }
+
+    private boolean isManagedDevice() {
+        DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
+        return devicePolicyManager.getDeviceOwner() != null
+                || devicePolicyManager.getProfileOwner() != null
+                || hasManagedProfile();
+    }
+
+    private boolean hasManagedProfile() {
+        UserManager userManager = getSystemService(UserManager.class);
+        for (final UserInfo userInfo : userManager.getProfiles(getUserId())) {
+            if (userInfo.isManagedProfile()) {
+                return true;
             }
         }
+        return false;
+    }
+
+    private void getAliasFromCredentialManagementApp(Uri uri,
+            IKeyChainAliasCallback.Stub callback) {
+        executor.execute(() -> {
+            try (KeyChain.KeyChainConnection keyChainConnection = KeyChain.bind(this)) {
+                String chosenAlias = null;
+                if (keyChainConnection.getService().hasCredentialManagementApp()) {
+                    Log.i(TAG, "There is a credential management app on the device. "
+                            + "Looking for an alias in the policy.");
+                    chosenAlias = keyChainConnection.getService()
+                            .getPredefinedAliasForPackageAndUri(mSenderPackageName, uri);
+                    if (chosenAlias != null) {
+                        keyChainConnection.getService().setGrant(mSenderUid, chosenAlias, true);
+                        Log.w(TAG, String.format("Selected alias %s from the "
+                                + "credential management app's policy", chosenAlias));
+                        DevicePolicyEventLogger
+                                .createEvent(DevicePolicyEnums
+                                        .CREDENTIAL_MANAGEMENT_APP_CREDENTIAL_FOUND_IN_POLICY)
+                                .write();
+                    } else {
+                        Log.i(TAG, "No alias provided from the credential management app");
+                    }
+                }
+                callback.alias(chosenAlias);
+            } catch (InterruptedException | RemoteException e) {
+                Log.e(TAG, "Unable to request find predefined alias from credential "
+                        + "management app policy");
+                // Proceed without a suggested alias.
+                try {
+                    callback.alias(null);
+                } catch (RemoteException shouldNeverHappen) {
+                    finish(null);
+                } finally {
+                    DevicePolicyEventLogger
+                            .createEvent(DevicePolicyEnums
+                                    .CREDENTIAL_MANAGEMENT_APP_POLICY_LOOKUP_FAILED)
+                            .write();
+                }
+            }
+        });
     }
 
     @VisibleForTesting
@@ -297,8 +424,8 @@
         private final KeyInfoProvider mInfoProvider;
         private final CertificateParametersFilter mCertificateFilter;
 
-        public AliasLoader(KeyStore keyStore, Context context, KeyInfoProvider infoProvider,
-                CertificateParametersFilter certificateFilter) {
+        public AliasLoader(KeyStore keyStore, Context context,
+                KeyInfoProvider infoProvider, CertificateParametersFilter certificateFilter) {
           mKeyStore = keyStore;
           mContext = context;
           mInfoProvider = infoProvider;
@@ -306,10 +433,19 @@
         }
 
         @Override protected CertificateAdapter doInBackground(Void... params) {
-            String[] aliasArray = mKeyStore.list(Credentials.USER_PRIVATE_KEY);
-            List<String> rawAliasList = ((aliasArray == null)
-                                      ? Collections.<String>emptyList()
-                                      : Arrays.asList(aliasArray));
+            final List<String> rawAliasList = new ArrayList<>();
+            try {
+                final Enumeration<String> aliases = mKeyStore.aliases();
+                while (aliases.hasMoreElements()) {
+                    final String alias = aliases.nextElement();
+                    if (mKeyStore.isKeyEntry(alias)) {
+                        rawAliasList.add(alias);
+                    }
+                }
+            } catch (KeyStoreException e) {
+                Log.e(TAG, "Error while loading entries from keystore. "
+                        + "List may be empty or incomplete.");
+            }
 
             return new CertificateAdapter(mKeyStore, mContext,
                     rawAliasList.stream().filter(mInfoProvider::isUserSelectable)
@@ -389,20 +525,8 @@
             }
         });
 
-        // getTargetPackage guarantees that the returned string is
-        // supplied by the system, so that an application can not
-        // spoof its package.
-        String pkg = mSender.getIntentSender().getTargetPackage();
-        PackageManager pm = getPackageManager();
-        CharSequence applicationLabel;
-        try {
-            applicationLabel = pm.getApplicationLabel(pm.getApplicationInfo(pkg, 0)).toString();
-        } catch (PackageManager.NameNotFoundException e) {
-            applicationLabel = pkg;
-        }
-        String appMessage = String.format(res.getString(R.string.requesting_application),
-                                          applicationLabel);
-        String contextMessage = appMessage;
+        String contextMessage = String.format(res.getString(R.string.requesting_application),
+                getApplicationLabel());
         Uri uri = getIntent().getParcelableExtra(KeyChain.EXTRA_URI);
         if (uri != null) {
             String hostMessage = String.format(res.getString(R.string.requesting_server),
@@ -431,6 +555,15 @@
         dialog.show();
     }
 
+    private String getApplicationLabel() {
+        PackageManager pm = getPackageManager();
+        try {
+            return pm.getApplicationLabel(pm.getApplicationInfo(mSenderPackageName, 0)).toString();
+        } catch (PackageManager.NameNotFoundException e) {
+            return mSenderPackageName;
+        }
+    }
+
     @VisibleForTesting
     static class CertificateAdapter extends BaseAdapter {
         private final List<String> mAliases;
@@ -532,10 +665,6 @@
     }
 
     private void finish(String alias, boolean isAliasFromPolicy) {
-        if (mLoadingDialog != null) {
-            mLoadingDialog.dismiss();
-            mLoadingDialog = null;
-        }
         if (alias == null || alias.equals(KeyChain.KEY_ALIAS_SELECTION_DENIED)) {
             alias = null;
             setResult(RESULT_CANCELED);
@@ -551,7 +680,7 @@
             new ResponseSender(keyChainAliasResponse, alias, isAliasFromPolicy).execute();
             return;
         }
-        finish();
+        finishActivity();
     }
 
     private class ResponseSender extends AsyncTask<Void, Void, Void> {
@@ -598,7 +727,20 @@
             return null;
         }
         @Override protected void onPostExecute(Void unused) {
+            finishActivity();
+        }
+    }
+
+    private void finishActivity() {
+        long timeElapsedSinceFirstIntent =
+                System.currentTimeMillis() - mFirstIntentReceivedTimeMillis;
+        if (mFirstIntentReceivedTimeMillis == 0L
+                || timeElapsedSinceFirstIntent > SNACKBAR_MIN_TIME) {
+            finishSnackBar();
             finish();
+        } else {
+            long remainingTimeToShowSnackBar = SNACKBAR_MIN_TIME - timeElapsedSinceFirstIntent;
+            handler.postDelayed(mFinishActivity, remainingTimeToShowSnackBar);
         }
     }
 
@@ -607,33 +749,51 @@
     }
 
     private static X509Certificate loadCertificate(KeyStore keyStore, String alias) {
-        byte[] bytes = keyStore.get(Credentials.USER_CERTIFICATE + alias);
-        if (bytes == null) {
-            Log.i(TAG, String.format("Missing user certificate for key alias %s", alias));
-            return null;
-        }
-        InputStream in = new ByteArrayInputStream(bytes);
+        final Certificate cert;
         try {
-            CertificateFactory cf = CertificateFactory.getInstance("X.509");
-            return (X509Certificate)cf.generateCertificate(in);
-        } catch (CertificateException ignored) {
-            Log.w(TAG, "Error generating certificate", ignored);
+            if (keyStore.isCertificateEntry(alias)) {
+                return null;
+            }
+            cert = keyStore.getCertificate(alias);
+        } catch (KeyStoreException e) {
+            Log.e(TAG, String.format("Error trying to retrieve certificate for \"%s\".", alias), e);
             return null;
         }
+        if (cert != null) {
+            if (cert instanceof X509Certificate) {
+                return (X509Certificate) cert;
+            } else {
+                Log.w(TAG, String.format("Certificate associated with alias \"%s\" is not X509.",
+                        alias));
+            }
+        }
+        return null;
     }
 
-    private static List<X509Certificate> loadCertificateChain(KeyStore keyStore, String alias) {
-        byte[] chainBytes = keyStore.get(Credentials.CA_CERTIFICATE + alias);
-        if (chainBytes == null) {
-            Log.i(TAG, String.format("Missing certificate chain for key alias %s", alias));
-            return Collections.emptyList();
-        }
-
+    private static List<X509Certificate> loadCertificateChain(KeyStore keyStore,
+            String alias) {
+        final Certificate[] certs;
+        final boolean isCertificateEntry;
         try {
-            return Credentials.convertFromPem(chainBytes);
-        } catch (IOException | CertificateException e) {
-            Log.w(TAG, String.format("Error parsing certificate chain for alias %s", alias), e);
+            isCertificateEntry = keyStore.isCertificateEntry(alias);
+            certs = keyStore.getCertificateChain(alias);
+        } catch (KeyStoreException e) {
+            Log.e(TAG, String.format("Error trying to retrieve certificate chain for \"%s\".",
+                    alias), e);
             return Collections.emptyList();
         }
+        final List<X509Certificate> result = new ArrayList<>();
+        // If this is a certificate entry we return the single certificate. Otherwise we trim the
+        // leaf and return only the rest of the chain.
+        for (int i = isCertificateEntry ? 0 : 1; i < certs.length; ++i) {
+            if (certs[i] instanceof X509Certificate) {
+                result.add((X509Certificate) certs[i]);
+            } else {
+                Log.w(TAG,"A certificate in the chain of alias \""
+                        + alias + "\" is not X509.");
+                return Collections.emptyList();
+            }
+        }
+        return result;
     }
 }
diff --git a/src/com/android/keychain/KeyChainService.java b/src/com/android/keychain/KeyChainService.java
index 6c03fa1..6686542 100644
--- a/src/com/android/keychain/KeyChainService.java
+++ b/src/com/android/keychain/KeyChainService.java
@@ -18,58 +18,82 @@
 
 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED;
 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED;
+import static android.security.KeyStore.UID_SELF;
 
+import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.IntentService;
 import android.app.admin.SecurityLog;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.StringParceledListSlice;
+import android.hardware.security.keymint.ErrorCode;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.UserHandle;
-import android.security.Credentials;
+import android.security.AppUriAuthenticationPolicy;
+import android.security.CredentialManagementApp;
 import android.security.IKeyChainService;
 import android.security.KeyChain;
-import android.security.KeyStore;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keystore.AttestationUtils;
-import android.security.keystore.DeviceIdAttestationException;
+import android.security.KeyStore2;
 import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.security.keystore.StrongBoxUnavailableException;
+import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyPermission;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keychain.internal.ExistingKeysProvider;
 import com.android.keychain.internal.GrantsDatabase;
 import com.android.org.conscrypt.TrustedCertificateStore;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.security.InvalidAlgorithmParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
@@ -79,21 +103,68 @@
     private static final String TAG = "KeyChain";
     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
     private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet(
-            new HashSet(Arrays.asList(KeyStore.UID_SELF, Process.WIFI_UID)));
+            new HashSet(Arrays.asList(UID_SELF, Process.WIFI_UID)));
+
+    private static final String MSG_NOT_SYSTEM = "Not system package";
+    private static final String MSG_NOT_SYSTEM_OR_CERT_INSTALLER =
+            "Not system or cert installer package";
+    private static final String MSG_NOT_SYSTEM_OR_CRED_MNG_APP =
+            "Not system or credential management app package";
 
     /** created in onCreate(), closed in onDestroy() */
     private GrantsDatabase mGrantsDb;
     private Injector mInjector;
-    private final KeyStore mKeyStore = KeyStore.getInstance();
+    private KeyStore mKeyStore;
+    private KeyChainStateStorage mStateStorage;
+
+    private Object mCredentialManagementAppLock = new Object();
+    @Nullable
+    @GuardedBy("mCredentialManagementAppLock")
+    private CredentialManagementApp mCredentialManagementApp;
 
     public KeyChainService() {
         super(KeyChainService.class.getSimpleName());
         mInjector = new Injector();
     }
 
+    private KeyStore getKeyStore() {
+        try {
+            final KeyStore keystore = mInjector.getKeyStoreInstance();
+            keystore.load(null);
+            return keystore;
+        } catch (KeyStoreException | IOException | NoSuchAlgorithmException
+                | CertificateException e) {
+            Log.e(TAG, "Error opening AndroidKeyStore.", e);
+            throw new RuntimeException("Error opening AndroidKeyStore.", e);
+        }
+    }
+
+    private KeyStore getKeyStore(boolean useWifiNamespace) {
+        if (!useWifiNamespace) {
+            return mKeyStore;
+        }
+        try {
+            final KeyStore keystore = mInjector.getKeyStoreInstance();
+            keystore.load(
+                    new AndroidKeyStoreLoadStoreParameter(
+                            KeyProperties.NAMESPACE_WIFI));
+            return keystore;
+        } catch (IOException | CertificateException | KeyStoreException
+                | NoSuchAlgorithmException e) {
+            Log.e(TAG, "Failed to open AndroidKeyStore for WI-FI namespace.", e);
+            return null;
+        }
+    }
+
     @Override public void onCreate() {
         super.onCreate();
+        mKeyStore = getKeyStore();
         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
+        mStateStorage = new KeyChainStateStorage(getDataDir());
+
+        synchronized (mCredentialManagementAppLock) {
+            mCredentialManagementApp = mStateStorage.loadCredentialManagementApp();
+        }
     }
 
     @Override
@@ -112,23 +183,35 @@
 
         @Override
         public List<String> getExistingKeyAliases() {
-            List<String> aliases = new ArrayList<String>();
-            String[] keyStoreAliases = mKeyStore.list(Credentials.USER_PRIVATE_KEY);
-            if (keyStoreAliases == null) {
-                return aliases;
+            final List<String> keyStoreAliases = new ArrayList<>();
+            try {
+                final Enumeration<String> aliases = mKeyStore.aliases();
+                while (aliases.hasMoreElements()) {
+                    final String alias = aliases.nextElement();
+                    if (!alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
+                        if (mKeyStore.isKeyEntry(alias)) {
+                            keyStoreAliases.add(alias);
+                        }
+                    }
+                }
+            } catch (KeyStoreException e) {
+                Log.e(TAG, "Error while loading entries from keystore. "
+                        + "List may be empty or incomplete.");
             }
 
-            for (String alias: keyStoreAliases) {
-                Log.w(TAG, "Got Alias from KeyStore: " + alias);
-                String unPrefixedAlias = alias.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "");
-                if (!unPrefixedAlias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
-                    aliases.add(unPrefixedAlias);
-                }
-            }
-            return aliases;
+            return keyStoreAliases;
         }
     }
 
+    private KeyDescriptor makeKeyDescriptor(String alias) {
+        final KeyDescriptor key = new KeyDescriptor();
+        key.domain = Domain.APP;
+        key.nspace = KeyProperties.NAMESPACE_APPLICATION;
+        key.alias = alias;
+        key.blob = null;
+        return key;
+    }
+
     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
         private final TrustedCertificateStore mTrustedCertificateStore
                 = new TrustedCertificateStore();
@@ -136,28 +219,83 @@
 
         @Override
         public String requestPrivateKey(String alias) {
-            if (!hasGrant(alias)) {
+            final CallerIdentity caller = getCaller();
+            if (!hasGrant(alias, caller)) {
                 return null;
             }
 
-            final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
-            final int uid = mInjector.getCallingUid();
-            Log.i(TAG, String.format("UID %d will be granted access to %s", uid, keystoreAlias));
-            return mKeyStore.grant(keystoreAlias, uid);
+            final int granteeUid = caller.mUid;
+
+            try {
+                final KeyStore2 keyStore2 = KeyStore2.getInstance();
+                KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias), granteeUid,
+                        KeyPermission.USE | KeyPermission.GET_INFO);
+                return KeyChain.getGrantString(grant);
+            } catch (android.security.KeyStoreException e) {
+                Log.e(TAG, "Failed to grant " + alias + " to uid: " + granteeUid, e);
+                return null;
+            }
+        }
+
+        @Override
+        public String getWifiKeyGrantAsUser(String alias) {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+
+            if (!hasGrant(alias, Process.WIFI_UID)) {
+                return null;
+            }
+
+            KeyStore2 keyStore2 = KeyStore2.getInstance();
+            try {
+                KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias),
+                        Process.WIFI_UID, KeyPermission.USE | KeyPermission.GET_INFO);
+                return KeyStore2.makeKeystoreEngineGrantString(grant.nspace);
+            } catch (android.security.KeyStoreException e) {
+                Log.e(TAG, "Failed to grant " + alias + " to uid: " + Process.WIFI_UID, e);
+                return null;
+            }
         }
 
         @Override public byte[] getCertificate(String alias) {
-            if (!hasGrant(alias)) {
+            final CallerIdentity caller = getCaller();
+            if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
                 return null;
             }
-            return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+            try {
+                if (!mKeyStore.isCertificateEntry(alias)) {
+                    final Certificate cert = mKeyStore.getCertificate(alias);
+                    if (cert == null) return null;
+                    return cert.getEncoded();
+                } else {
+                    return null;
+                }
+            } catch (KeyStoreException | CertificateEncodingException e) {
+                Log.e(TAG, "Failed to retrieve certificate.", e);
+                return null;
+            }
         }
 
         @Override public byte[] getCaCertificates(String alias) {
-            if (!hasGrant(alias)) {
+            final CallerIdentity caller = getCaller();
+            if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
                 return null;
             }
-            return mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+            try {
+                if (mKeyStore.isCertificateEntry(alias)) {
+                    return mKeyStore.getCertificate(alias).getEncoded();
+                } else {
+                    final Certificate[] certs = mKeyStore.getCertificateChain(alias);
+                    if (certs == null || certs.length <= 1) return null;
+                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+                    for (int i = 1; i < certs.length; ++i) {
+                        outputStream.write(certs[i].getEncoded());
+                    }
+                    return outputStream.toByteArray();
+                }
+            } catch (KeyStoreException | CertificateEncodingException | IOException e) {
+                Log.e(TAG, "Failed to retrieve certificate(s) from AndroidKeyStore.", e);
+                return null;
+            }
         }
 
         @Override public boolean isUserSelectable(String alias) {
@@ -167,7 +305,7 @@
 
         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
             validateAlias(alias);
-            checkSystemCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
             Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias,
                     isUserSelectable));
             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
@@ -175,7 +313,7 @@
 
         @Override public int generateKeyPair(
                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
-            checkSystemCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
             final String alias = spec.getKeystoreAlias();
 
@@ -188,23 +326,11 @@
             }
             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
-            if (TextUtils.isEmpty(alias) || spec.getUid() != KeyStore.UID_SELF) {
+            if (TextUtils.isEmpty(alias) || spec.getUid() != UID_SELF) {
                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
                 return KeyChain.KEY_GEN_MISSING_ALIAS;
             }
 
-            if (spec.getAttestationChallenge() != null) {
-                Log.e(TAG, "Key generation request should not include an Attestation challenge.");
-                return KeyChain.KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE;
-            }
-
-            if (!removeKeyPair(alias)) {
-                Log.e(TAG, "Failed to remove previously-installed alias " + alias);
-                //TODO: Introduce a different error code in R to distinguish the failure to remove
-                // old keys from other failures.
-                return KeyChain.KEY_GEN_FAILURE;
-            }
-
             try {
                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
                         algorithm, "AndroidKeyStore");
@@ -230,98 +356,58 @@
             } catch (StrongBoxUnavailableException e) {
                 Log.e(TAG, "StrongBox unavailable.", e);
                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
-            }
-        }
-
-        @Override public int attestKey(
-                String alias, byte[] attestationChallenge,
-                int[] idAttestationFlags,
-                KeymasterCertificateChain attestationChain) {
-            checkSystemCaller();
-            validateAlias(alias);
-
-            if (attestationChallenge == null) {
-                Log.e(TAG, String.format("Missing attestation challenge for alias %s", alias));
-                return KeyChain.KEY_ATTESTATION_MISSING_CHALLENGE;
-            }
-
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, String.format("About to attest key alias %s, challenge %s, flags %s",
-                        alias, Base64.encodeToString(attestationChallenge, Base64.NO_WRAP),
-                        Arrays.toString(idAttestationFlags)));
-            }
-
-            KeymasterArguments attestArgs;
-            try {
-                attestArgs = AttestationUtils.prepareAttestationArguments(
-                        mContext, idAttestationFlags, attestationChallenge);
-            } catch (DeviceIdAttestationException e) {
-                Log.e(TAG, "Failed collecting attestation data", e);
-                return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
-            }
-            int errorCode = checkKeyChainStatus(alias, attestationChain, attestArgs);
-            if (errorCode == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
-                // b/69471841: id attestation might fail due to incorrect provisioning of device
-                try {
-                    attestArgs =
-                            AttestationUtils.prepareAttestationArgumentsIfMisprovisioned(
-                            mContext, idAttestationFlags, attestationChallenge);
-                    if (attestArgs == null) {
-                        return errorCode;
+            } catch (ProviderException e) {
+                Throwable t = e.getCause();
+                if (t instanceof android.security.KeyStoreException) {
+                    if (((android.security.KeyStoreException) t).getErrorCode()
+                            == ErrorCode.CANNOT_ATTEST_IDS) {
+                        return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
                     }
-                } catch (DeviceIdAttestationException e) {
-                    Log.e(TAG, "Failed collecting attestation data "
-                            + "during second attempt on misprovisioned device", e);
-                    return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
                 }
+                Log.e(TAG, "KeyStore error.", e);
+                return KeyChain.KEY_GEN_FAILURE;
             }
-
-            return checkKeyChainStatus(alias, attestationChain, attestArgs);
-        }
-
-        private int checkKeyChainStatus(
-                String alias,
-                KeymasterCertificateChain attestationChain,
-                KeymasterArguments attestArgs) {
-
-            final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
-            final int errorCode = mKeyStore.attestKey(keystoreAlias, attestArgs, attestationChain);
-            if (errorCode != KeyStore.NO_ERROR) {
-                Log.e(TAG, String.format("Failure attesting for key %s: %d", alias, errorCode));
-                if (errorCode == KeyStore.CANNOT_ATTEST_IDS) {
-                    return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
-                } else {
-                    // General failure, cannot discern which.
-                    return KeyChain.KEY_ATTESTATION_FAILURE;
-                }
-            }
-
-            return KeyChain.KEY_ATTESTATION_SUCCESS;
         }
 
         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
                 byte[] userCertificateChain) {
-            checkSystemCaller();
-            if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
-                        KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
-                Log.e(TAG, "Failed to import user certificate " + userCertificate);
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+
+            final PrivateKey privateKey;
+            try {
+                final Key key = mKeyStore.getKey(alias, null);
+                if (! (key instanceof PrivateKey)) {
+                    return false;
+                }
+                privateKey = (PrivateKey) key;
+            } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
+                Log.e(TAG, "Failed to get private key entry.", e);
                 return false;
             }
 
-            if (userCertificateChain != null && userCertificateChain.length > 0) {
-                if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain,
-                            KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
-                    Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
-                    if (!mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)) {
-                        Log.e(TAG, "Failed to clean up key chain after certificate chain"
-                                + " importing failed");
-                    }
-                    return false;
+            final ArrayList<Certificate> certs = new ArrayList<>();
+            try {
+                if (userCertificate != null) {
+                    certs.add(parseCertificate(userCertificate));
                 }
-            } else {
-                if (!mKeyStore.delete(Credentials.CA_CERTIFICATE + alias)) {
-                    Log.e(TAG, "Failed to remove CA certificate chain for alias " + alias);
+                if (userCertificateChain != null) {
+                    certs.addAll(parseCertificates(userCertificateChain));
                 }
+            } catch (CertificateException e) {
+                Log.e(TAG, "Failed to parse user certificate.", e);
+                return false;
+            }
+
+            final Certificate[] certsArray = certs.toArray(new Certificate[0]);
+
+            try {
+                // setKeyEntry with a private key loaded from AndroidKeyStore replaces
+                // the certificate components without touching the private key if
+                // the alias is the same as that of the private key.
+                mKeyStore.setKeyEntry(alias, privateKey, null, certsArray);
+            } catch (KeyStoreException e) {
+                Log.e(TAG, "Failed update key certificates.", e);
+                return false;
             }
 
             if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -340,14 +426,17 @@
             }
         }
 
-        private boolean hasGrant(String alias) {
+        private boolean hasGrant(String alias, CallerIdentity caller) {
+            return hasGrant(alias, caller.mUid);
+        }
+
+        private boolean hasGrant(String alias, int targetUid) {
             validateAlias(alias);
 
-            final int callingUid = mInjector.getCallingUid();
-            if (!mGrantsDb.hasGrant(callingUid, alias)) {
+            if (!mGrantsDb.hasGrant(targetUid, alias)) {
                 Log.w(TAG, String.format(
                         "uid %d doesn't have permission to access the requested alias %s",
-                        callingUid, alias));
+                        targetUid, alias));
                 return false;
             }
 
@@ -355,12 +444,15 @@
         }
 
         @Override public String installCaCertificate(byte[] caCertificate) {
-            checkCertInstallerOrSystemCaller();
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
+                    MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
             final String alias;
             String subject = null;
             final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
+            final X509Certificate cert;
             try {
-                final X509Certificate cert = parseCertificate(caCertificate);
+                cert = parseCertificate(caCertificate);
 
                 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG);
                 subject = cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
@@ -392,14 +484,14 @@
             // anchors. Ultimately, the user should explicitly choose to install the VPN trust
             // anchor separately and independently of CA certificates, at which point this code
             // should be removed.
-            if (CERT_INSTALLER_PACKAGE.equals(callingPackage())) {
-                final boolean result = mKeyStore.put(
-                        String.format("%s%s %s", Credentials.CA_CERTIFICATE, subject, alias),
-                        caCertificate, Process.SYSTEM_UID,
-                        KeyStore.FLAG_NONE);
-                Log.d(TAG, String.format(
-                        "Attempted installing %s (subject: %s) to KeyStore. Result: %b", alias,
-                        subject, result));
+            if (CERT_INSTALLER_PACKAGE.equals(caller.mPackageName)) {
+                try {
+                    mKeyStore.setCertificateEntry(String.format("%s %s", subject, alias), cert);
+                } catch(KeyStoreException e) {
+                    Log.e(TAG, String.format(
+                            "Attempted installing %s (subject: %s) to KeyStore. Failed", alias,
+                            subject), e);
+                }
             }
 
             broadcastLegacyStorageChange();
@@ -415,17 +507,19 @@
          * @param userCertificateChain The rest of the chain for the client certificate
          * @param alias The alias under which the key pair is installed. It is invalid to pass
          *              {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}.
-         * @param uid Can be only one of two values: Either {@code KeyStore.UID_SELF} to indicate
-         *            installation into the current user's system Keystore instance, or
-         *            {@code Process.WIFI_UID} to indicate installation into the main user's
-         *            WiFi Keystore instance. It is only valid to pass {@code Process.WIFI_UID} to
-         *            the KeyChain service on user 0.
+         * @param uid Can be only one of two values: Either
+         *            {@code android.security.KeyStore.UID_SELF} to indicate installation into the
+         *            current user's system Keystore instance, or {@code Process.WIFI_UID} to
+         *            indicate installation into the main user's WiFi Keystore instance. It is only
+         *            valid to pass {@code Process.WIFI_UID} to the KeyChain service on user 0.
          * @return Whether the operation succeeded or not.
          */
         @Override public boolean installKeyPair(@Nullable byte[] privateKey,
                 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain,
                 String alias, int uid) {
-            checkCertInstallerOrSystemCaller();
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
+                    MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
                 throw new IllegalArgumentException("The alias specified for the key denotes "
                         + "a reserved value and cannot be used to name a key");
@@ -456,42 +550,78 @@
                                 emptyOrBase64Encoded(userCertificateChain)));
             }
 
-            if (!removeKeyPair(alias)) {
-                return false;
-            }
-            if (privateKey != null && !mKeyStore.importKey(
-                    Credentials.USER_PRIVATE_KEY + alias, privateKey, uid, KeyStore.FLAG_NONE)) {
-                Log.e(TAG, "Failed to import private key " + alias);
-                return false;
-            }
-            if (userCertificate != null &&
-                    !mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
-                        uid, KeyStore.FLAG_NONE)) {
-                Log.e(TAG, "Failed to import user certificate " + userCertificate);
-                if (!mKeyStore.delete(Credentials.USER_PRIVATE_KEY + alias)) {
-                    Log.e(TAG, "Failed to delete private key after certificate importing failed");
+            final ArrayList<Certificate> certs = new ArrayList<>();
+            try {
+                if (userCertificate != null) {
+                    certs.add(parseCertificate(userCertificate));
                 }
+                if (userCertificateChain != null) {
+                    certs.addAll(parseCertificates(userCertificateChain));
+                }
+            } catch (CertificateException e) {
+                Log.e(TAG, "Failed to parse user certificate.", e);
                 return false;
             }
-            if (userCertificateChain != null && userCertificateChain.length > 0) {
-                if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain, uid,
-                        KeyStore.FLAG_NONE)) {
-                    Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
-                    if (!removeKeyPair(alias)) {
-                        Log.e(TAG, "Failed to clean up key chain after certificate chain"
-                                + " importing failed");
+
+            if (certs.isEmpty()) {
+                Log.e(TAG, "Cannot install private key without public certificate.");
+                return false;
+            }
+
+            final Certificate[] certificates = certs.toArray(new Certificate[0]);
+
+            final PrivateKey privateKey1;
+            try {
+                if (privateKey != null) {
+                    final KeyFactory keyFactory =
+                            KeyFactory.getInstance(certificates[0].getPublicKey().getAlgorithm());
+                    privateKey1 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
+                } else {
+                    privateKey1 = null;
+                }
+            } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                Log.e(TAG, "Failed to parse private key.", e);
+                return false;
+            }
+
+            KeyStore keystore = getKeyStore(uid == Process.WIFI_UID);
+            if (keystore == null) {
+                return false;
+            }
+
+            try {
+                if (privateKey != null) {
+                    keystore.setKeyEntry(alias, privateKey1, null, certificates);
+                } else {
+                    if (certificates.length > 1) {
+                        Log.e(TAG,
+                                "Cannot install key certificate chain without private key.");
+                        return false;
                     }
-                    return false;
+                    keystore.setCertificateEntry(alias, certificates[0]);
                 }
+            } catch (KeyStoreException e) {
+                Log.e(TAG, "Failed to install key pair.", e);
             }
+
             broadcastKeychainChange();
             broadcastLegacyStorageChange();
             return true;
         }
 
         @Override public boolean removeKeyPair(String alias) {
-            checkCertInstallerOrSystemCaller();
-            if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
+                    MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
+            return removeKeyPairInternal(alias);
+        }
+
+        private boolean removeKeyPairInternal(String alias) {
+            try {
+                mKeyStore.deleteEntry(alias);
+            } catch (KeyStoreException e) {
+                Log.e(TAG, String.format(
+                        "Failed not remove keystore entry with alias %s", alias));
                 return false;
             }
             Log.w(TAG, String.format(
@@ -502,14 +632,31 @@
             return true;
         }
 
+        @Override public boolean containsKeyPair(String alias) {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+            try {
+                final Key key = mKeyStore.getKey(alias, null);
+                return key instanceof PrivateKey;
+            } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) {
+                Log.w("Error while trying to check for key presence.", e);
+                return false;
+            }
+        }
+
         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
             CertificateFactory cf = CertificateFactory.getInstance("X.509");
             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
         }
+        private Collection<X509Certificate> parseCertificates(byte[] bytes)
+                throws CertificateException {
+            final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            return (Collection<X509Certificate>)
+                    cf.generateCertificates(new ByteArrayInputStream(bytes));
+        }
 
         @Override public boolean reset() {
             // only Settings should be able to reset
-            checkSystemCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
             mGrantsDb.removeAllAliasesInformation();
             boolean ok = true;
             synchronized (mTrustedCertificateStore) {
@@ -530,7 +677,7 @@
 
         @Override public boolean deleteCaCertificate(String alias) {
             // only Settings should be able to delete
-            checkSystemCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
             boolean ok = true;
             Log.i(TAG, String.format("Deleting CA certificate %s", alias));
             synchronized (mTrustedCertificateStore) {
@@ -570,37 +717,58 @@
             }
         }
 
-        private void checkCertInstallerOrSystemCaller() {
-            final String caller = callingPackage();
-            if (!isCallerWithSystemUid() && !CERT_INSTALLER_PACKAGE.equals(caller)) {
-                throw new SecurityException("Not system or cert installer package: " + caller);
+        private boolean hasManageCredentialManagementAppPermission(CallerIdentity caller) {
+            return mContext.checkPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
+                    caller.mPid, caller.mUid) == PackageManager.PERMISSION_GRANTED;
+        }
+
+        private boolean isCertInstaller(CallerIdentity caller) {
+            return caller.mPackageName != null
+                    && CERT_INSTALLER_PACKAGE.equals(caller.mPackageName);
+        }
+
+        private boolean isCredentialManagementApp(CallerIdentity caller) {
+            synchronized (mCredentialManagementAppLock) {
+                return mCredentialManagementApp != null && caller.mPackageName != null
+                        && caller.mPackageName.equals(mCredentialManagementApp.getPackageName());
             }
         }
 
-        private void checkSystemCaller() {
-            if (!isCallerWithSystemUid()) {
-                throw new SecurityException("Not system package: " + callingPackage());
-            }
-        }
-
-        private boolean isCallerWithSystemUid() {
-            return UserHandle.isSameApp(mInjector.getCallingUid(), Process.SYSTEM_UID);
-        }
-
-        private String callingPackage() {
-            return getPackageManager().getNameForUid(mInjector.getCallingUid());
+        private boolean isSystemUid(CallerIdentity caller) {
+            return UserHandle.isSameApp(caller.mUid, Process.SYSTEM_UID);
         }
 
         @Override public boolean hasGrant(int uid, String alias) {
-            checkSystemCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
             return mGrantsDb.hasGrant(uid, alias);
         }
 
-        @Override public void setGrant(int uid, String alias, boolean value) {
-            checkSystemCaller();
-            mGrantsDb.setGrant(uid, alias, value);
-            broadcastPermissionChange(uid, alias, value);
+        @Override public boolean setGrant(int uid, String alias, boolean granted) {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+            mGrantsDb.setGrant(uid, alias, granted);
+            if (!granted) {
+                try {
+                    KeyStore2.getInstance().ungrant(makeKeyDescriptor(alias), uid);
+                } catch (android.security.KeyStoreException e) {
+                    Log.e(TAG, "Failed to ungrant " + alias + " to uid: " + uid, e);
+                    return false;
+                }
+            }
+            broadcastPermissionChange(uid, alias, granted);
             broadcastLegacyStorageChange();
+            return true;
+        }
+
+        @Override public int[] getGrants(String alias) {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+            try {
+                if (mKeyStore.isKeyEntry(alias)) {
+                    return mGrantsDb.getGrants(alias);
+                }
+            } catch (KeyStoreException e) {
+                Log.w(TAG, "Error while checking if key exists.", e);
+            }
+            throw new IllegalArgumentException("Alias not found: " + alias);
         }
 
         @Override
@@ -667,6 +835,149 @@
                 }
             }
         }
+
+        @Override
+        public void setCredentialManagementApp(@NonNull String packageName,
+                @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller)
+                    || hasManageCredentialManagementAppPermission(caller), MSG_NOT_SYSTEM);
+            checkValidAuthenticationPolicy(authenticationPolicy);
+
+            synchronized (mCredentialManagementAppLock) {
+                if (mCredentialManagementApp != null) {
+                    final String existingPackage = mCredentialManagementApp.getPackageName();
+                    if (existingPackage.equals(packageName)) {
+                        // Update existing credential management app's policy
+                        removeOrphanedKeyPairs(authenticationPolicy);
+                    } else {
+                        // Replace existing credential management app
+                        removeOrphanedKeyPairs(null);
+                        setManageCredentialsAppOps(existingPackage, false);
+                    }
+                }
+                setManageCredentialsAppOps(packageName, true);
+                mCredentialManagementApp = new CredentialManagementApp(packageName,
+                        authenticationPolicy);
+                mStateStorage.saveCredentialManagementApp(mCredentialManagementApp);
+            }
+        }
+
+        private void setManageCredentialsAppOps(String packageName, boolean allowed) {
+            try {
+                int mode = allowed ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_DEFAULT;
+                ApplicationInfo appInfo = getPackageManager().getApplicationInfo(packageName, 0);
+                getSystemService(AppOpsManager.class).setMode(AppOpsManager.OP_MANAGE_CREDENTIALS,
+                        appInfo.uid, packageName, mode);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Unable to find info for package: " + packageName);
+            }
+        }
+
+        private void removeOrphanedKeyPairs(
+                @Nullable AppUriAuthenticationPolicy newPolicy) {
+            Set<String> existingAliases = mCredentialManagementApp.getAuthenticationPolicy()
+                    .getAliases();
+            Set<String> newAliases = newPolicy != null ? newPolicy.getAliases() : new HashSet<>();
+
+            // Uninstall all certificates that are no longer included in the new
+            // authentication policy
+            for (String existingAlias : existingAliases) {
+                if (!newAliases.contains(existingAlias)) {
+                    removeKeyPairInternal(existingAlias);
+                }
+            }
+        }
+
+        private void checkValidAuthenticationPolicy(
+                @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
+            if (authenticationPolicy == null
+                    || authenticationPolicy.getAppAndUriMappings().isEmpty()) {
+                throw new IllegalArgumentException("The authentication policy is null or empty");
+            }
+            // Check whether any of the aliases in the policy already exist
+            for (String alias : authenticationPolicy.getAliases()) {
+                if (requestPrivateKey(alias) != null) {
+                    throw new IllegalArgumentException(String.format("The authentication policy "
+                            + "contains an installed alias: %s", alias));
+                }
+            }
+        }
+
+        @Override
+        public boolean hasCredentialManagementApp() {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+            synchronized (mCredentialManagementAppLock) {
+                return mCredentialManagementApp != null;
+            }
+        }
+
+        @Nullable
+        @Override
+        public String getCredentialManagementAppPackageName() {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+            synchronized (mCredentialManagementAppLock) {
+                return mCredentialManagementApp != null
+                        ? mCredentialManagementApp.getPackageName()
+                        : null;
+            }
+        }
+
+        @Nullable
+        @Override
+        public AppUriAuthenticationPolicy getCredentialManagementAppPolicy() {
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller)
+                            || isCredentialManagementApp(caller), MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
+            synchronized (mCredentialManagementAppLock) {
+                return mCredentialManagementApp != null
+                        ? mCredentialManagementApp.getAuthenticationPolicy()
+                        : null;
+            }
+        }
+
+        @Nullable
+        @Override
+        public String getPredefinedAliasForPackageAndUri(@NonNull String packageName,
+                @Nullable Uri uri) {
+            Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
+            synchronized (mCredentialManagementAppLock) {
+                if (mCredentialManagementApp == null || uri == null) {
+                    return null;
+                }
+                Map<Uri, String> urisToAliases = mCredentialManagementApp.getAuthenticationPolicy()
+                        .getAppAndUriMappings().get(packageName);
+                return urisToAliases != null ? urisToAliases.get(uri) : null;
+            }
+        }
+
+        @Override
+        public void removeCredentialManagementApp() {
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller)
+                            || isCredentialManagementApp(caller)
+                            || hasManageCredentialManagementAppPermission(caller),
+                    MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
+            synchronized (mCredentialManagementAppLock) {
+                if (mCredentialManagementApp != null) {
+                    // Remove all certificates
+                    removeOrphanedKeyPairs(null);
+                    setManageCredentialsAppOps(mCredentialManagementApp.getPackageName(), false);
+                }
+                mCredentialManagementApp = null;
+                mStateStorage.saveCredentialManagementApp(mCredentialManagementApp);
+            }
+        }
+
+        @Override
+        public boolean isCredentialManagementApp(@NonNull String packageName) {
+            final CallerIdentity caller = getCaller();
+            Preconditions.checkCallAuthorization(isSystemUid(caller)
+                    || isCredentialManagementApp(caller), MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
+            synchronized (mCredentialManagementAppLock) {
+                return packageName.equals(mCredentialManagementApp.getPackageName());
+            }
+        }
     };
 
     @Override public IBinder onBind(Intent intent) {
@@ -723,6 +1034,23 @@
         return Base64.encodeToString(cert, Base64.NO_WRAP);
     }
 
+    private final class CallerIdentity {
+
+        final int mUid;
+        final int mPid;
+        final String mPackageName;
+
+        CallerIdentity() {
+            mUid = mInjector.getCallingUid();
+            mPid = Binder.getCallingPid();
+            mPackageName = getPackageManager().getNameForUid(mUid);
+        }
+    }
+
+    private CallerIdentity getCaller() {
+        return new CallerIdentity();
+    }
+
     @VisibleForTesting
     void setInjector(Injector injector) {
         mInjector = injector;
@@ -744,5 +1072,9 @@
         public int getCallingUid() {
             return Binder.getCallingUid();
         }
+
+        public KeyStore getKeyStoreInstance() throws KeyStoreException {
+            return KeyStore.getInstance("AndroidKeyStore");
+        }
     }
 }
diff --git a/src/com/android/keychain/KeyChainStateStorage.java b/src/com/android/keychain/KeyChainStateStorage.java
new file mode 100644
index 0000000..5a791c1
--- /dev/null
+++ b/src/com/android/keychain/KeyChainStateStorage.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.keychain;
+
+import android.annotation.Nullable;
+import android.security.CredentialManagementApp;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class KeyChainStateStorage {
+
+    private static final String TAG = "KeyChain";
+    private static final String TAG_CREDENTIAL_MANAGEMENT_APP = "credential-management-app";
+
+    private final File mDirectory;
+
+    public KeyChainStateStorage(File directory) {
+        mDirectory = directory;
+    }
+
+    @Nullable
+    public CredentialManagementApp loadCredentialManagementApp() {
+        CredentialManagementApp credentialManagementApp = null;
+        AtomicFile file = getCredentialManagementFile();
+        FileInputStream stream = null;
+        try {
+            stream = file.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+            }
+            String tag = parser.getName();
+            if (TAG_CREDENTIAL_MANAGEMENT_APP.equals(tag)) {
+                credentialManagementApp = CredentialManagementApp.readFromXml(parser);
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Log.e(TAG, "Failed to load state", e);
+        } finally {
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException e) {
+            }
+        }
+        return credentialManagementApp;
+    }
+
+    public void saveCredentialManagementApp(
+            @Nullable CredentialManagementApp credentialManagementApp) {
+        AtomicFile file = getCredentialManagementFile();
+        FileOutputStream stream;
+        try {
+            stream = file.startWrite();
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to write state " + e);
+            return;
+        }
+        try {
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+
+            if (credentialManagementApp != null) {
+                out.startTag(null, TAG_CREDENTIAL_MANAGEMENT_APP);
+                credentialManagementApp.writeToXml(out);
+                out.endTag(null, TAG_CREDENTIAL_MANAGEMENT_APP);
+            }
+            out.endDocument();
+            file.finishWrite(stream);
+            stream.close();
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to store state");
+            file.failWrite(stream);
+        }
+    }
+
+    private AtomicFile getCredentialManagementFile() {
+        File file = new File(mDirectory, "credential-management-app.xml");
+        if (!file.exists()) {
+            try {
+                file.createNewFile();
+            } catch (IOException e) {
+            }
+        }
+        return new AtomicFile(file);
+    }
+}
diff --git a/src/com/android/keychain/internal/GrantsDatabase.java b/src/com/android/keychain/internal/GrantsDatabase.java
index 7c14a44..789ce6f 100644
--- a/src/com/android/keychain/internal/GrantsDatabase.java
+++ b/src/com/android/keychain/internal/GrantsDatabase.java
@@ -25,6 +25,9 @@
 import android.database.sqlite.SQLiteOpenHelper;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class GrantsDatabase {
     private static final String TAG = "KeyChain";
 
@@ -43,6 +46,13 @@
                     + GRANTS_ALIAS
                     + "=?";
 
+    private static final String SELECTION_GRANTEE_UIDS_FOR_ALIAS =
+            "SELECT " + GRANTS_GRANTEE_UID + " FROM "
+                    + TABLE_GRANTS
+                    + " WHERE "
+                    + GRANTS_ALIAS
+                    + "=?";
+
     private static final String SELECT_GRANTS_BY_UID_AND_ALIAS =
             GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
 
@@ -185,6 +195,25 @@
         }
     }
 
+    public int[] getGrants(String alias) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        try (Cursor cursor =
+                     db.query(
+                             TABLE_GRANTS,
+                             new String[] {GRANTS_GRANTEE_UID},
+                             SELECTION_GRANTS_BY_ALIAS,
+                             new String[] {alias},
+                             null /* group by */,
+                             null /* having */,
+                             null /* order by */)) {
+            final List<Integer> result = new ArrayList<>();
+            while (cursor.moveToNext()) {
+                result.add(cursor.getInt(0));
+            }
+            return result.stream().mapToInt(Integer::intValue).toArray();
+        }
+    }
+
     public void removeAliasInformation(String alias) {
         final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
         db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_ALIAS, new String[] {alias});
diff --git a/support/Android.bp b/support/Android.bp
index 426799c..3859d9b 100644
--- a/support/Android.bp
+++ b/support/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 java_library {
     name: "com.android.keychain.tests.support",
     srcs: ["src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl"],
@@ -26,7 +30,4 @@
         "junit",
     ],
     certificate: "platform",
-    test_suites: [
-        "general-tests",
-    ]
 }
diff --git a/support/AndroidManifest.xml b/support/AndroidManifest.xml
index 934660a..11e7f55 100644
--- a/support/AndroidManifest.xml
+++ b/support/AndroidManifest.xml
@@ -18,7 +18,8 @@
           package="com.android.keychain.tests.support"
 	  android:sharedUserId="android.uid.system">
     <application android:process="system">
-        <service android:name="com.android.keychain.tests.support.KeyChainServiceTestSupport">
+        <service android:name="com.android.keychain.tests.support.KeyChainServiceTestSupport"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.android.keychain.tests.support.IKeyChainServiceTestSupport"/>
             </intent-filter>
diff --git a/support/src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl b/support/src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl
index c62c971..c8ecc76 100644
--- a/support/src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl
+++ b/support/src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl
@@ -31,16 +31,11 @@
  * @hide
  */
 interface IKeyChainServiceTestSupport {
-    boolean keystoreReset();
-    boolean keystoreSetPassword(String password);
-    boolean keystorePut(String key, in byte[] value);
-    boolean keystoreImportKey(String key, in byte[] value);
     void revokeAppPermission(int uid, String alias);
     void grantAppPermission(int uid, String alias);
     boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
     boolean removeKeyPair(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
     int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
-    int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags);
     boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
 }
diff --git a/support/src/com/android/keychain/tests/support/KeyChainServiceTestSupport.java b/support/src/com/android/keychain/tests/support/KeyChainServiceTestSupport.java
index 5f688f8..1e99770 100644
--- a/support/src/com/android/keychain/tests/support/KeyChainServiceTestSupport.java
+++ b/support/src/com/android/keychain/tests/support/KeyChainServiceTestSupport.java
@@ -23,39 +23,14 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyStore;
-import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.util.Log;
 
 public class KeyChainServiceTestSupport extends Service {
     private static final String TAG = "KeyChainServiceTest";
 
-    private final KeyStore mKeyStore = KeyStore.getInstance();
-
     private final IKeyChainServiceTestSupport.Stub mIKeyChainServiceTestSupport
             = new IKeyChainServiceTestSupport.Stub() {
-        @Override public boolean keystoreReset() {
-            Log.d(TAG, "keystoreReset");
-            for (String key : mKeyStore.list("")) {
-                if (!mKeyStore.delete(key, KeyStore.UID_SELF)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-        @Override public boolean keystoreSetPassword(String password) {
-            Log.d(TAG, "keystoreSetPassword");
-            return mKeyStore.onUserPasswordChanged(password);
-        }
-        @Override public boolean keystorePut(String key, byte[] value) {
-            Log.d(TAG, "keystorePut");
-            return mKeyStore.put(key, value, KeyStore.UID_SELF, KeyStore.FLAG_NONE);
-        }
-        @Override public boolean keystoreImportKey(String key, byte[] value) {
-            Log.d(TAG, "keystoreImport");
-            return mKeyStore.importKey(key, value, KeyStore.UID_SELF, KeyStore.FLAG_NONE);
-        }
-
         @Override public void revokeAppPermission(final int uid, final String alias)
                 throws RemoteException {
             Log.d(TAG, "revokeAppPermission");
@@ -102,16 +77,6 @@
             });
         }
 
-        @Override public int attestKey(
-                String alias, byte[] attestationChallenge,
-                int[] idAttestationFlags) throws RemoteException {
-            KeymasterCertificateChain attestationChain = new KeymasterCertificateChain();
-            return performBlockingKeyChainCall(keyChainService -> {
-                return keyChainService.attestKey(alias, attestationChallenge, idAttestationFlags,
-                        attestationChain);
-            });
-        }
-
         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
                 byte[] userCertificateChain) throws RemoteException {
             return performBlockingKeyChainCall(keyChainService -> {
diff --git a/tests/Android.bp b/tests/Android.bp
index 823d7a1..8d2415a 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_test {
     name: "KeyChainTests",
     srcs: ["src/**/*.java"],
@@ -32,9 +36,9 @@
     libs: [
         "android.test.base",
     ],
-    test_suites: ["general-tests"],
-    required: [
-        "KeyChainTestsSupport",
+    test_suites: ["device-tests"],
+    data: [
+        ":KeyChainTestsSupport",
     ],
 
     instrumentation_for: "KeyChain",
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index c1f5021..754b76b 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -40,12 +40,14 @@
     -->
     <application>
         <uses-library android:name="android.test.runner" />
-        <activity android:name="com.android.keychain.tests.KeyChainTestActivity">
+        <activity android:name="com.android.keychain.tests.KeyChainTestActivity"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.android.keychain.tests.KeyChainTestActivity"/>
             </intent-filter>
         </activity>
-        <activity android:name="com.android.keychain.tests.KeyChainSocketTestActivity">
+        <activity android:name="com.android.keychain.tests.KeyChainSocketTestActivity"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.android.keychain.tests.KeyChainSocketTestActivity"/>
             </intent-filter>
diff --git a/tests/src/com/android/keychain/tests/BasicKeyChainServiceTest.java b/tests/src/com/android/keychain/tests/BasicKeyChainServiceTest.java
index 9d69482..f3f20db 100644
--- a/tests/src/com/android/keychain/tests/BasicKeyChainServiceTest.java
+++ b/tests/src/com/android/keychain/tests/BasicKeyChainServiceTest.java
@@ -18,6 +18,7 @@
 import static android.os.Process.WIFI_UID;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.testng.Assert.assertThrows;
 
@@ -36,13 +37,16 @@
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.util.Base64;
 import android.util.Log;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 import com.android.keychain.tests.support.IKeyChainServiceTestSupport;
 import java.io.IOException;
+import java.security.KeyStore;
 import java.security.KeyStore.PrivateKeyEntry;
 import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import libcore.java.security.TestKeyStore;
 import org.junit.After;
@@ -135,8 +139,8 @@
     public void testCanAccessKeyAfterGettingGrant()
             throws RemoteException, IOException, CertificateException {
         Log.d(TAG, "Testing access to imported key after getting grant.");
-        assertThat(mTestSupportService.keystoreReset()).isTrue();
-        installFirstKey();
+        removeKeyPair(ALIAS_1);
+        generateRsaKey(ALIAS_1);
         assertThat(mKeyChainService.requestPrivateKey(ALIAS_1)).isNull();
         mTestSupportService.grantAppPermission(Process.myUid(), ALIAS_1);
         assertThat(mKeyChainService.requestPrivateKey(ALIAS_1)).isNotNull();
@@ -146,7 +150,7 @@
     public void testInstallAndRemoveKeyPair()
             throws RemoteException, IOException, CertificateException {
         Log.d(TAG, "Testing importing key.");
-        assertThat(mTestSupportService.keystoreReset()).isTrue();
+        removeKeyPair(ALIAS_IMPORTED);
         // No key installed, all should fail.
         assertThat(mKeyChainService.requestPrivateKey(ALIAS_IMPORTED)).isNull();
         assertThat(mKeyChainService.getCertificate(ALIAS_IMPORTED)).isNull();
@@ -159,10 +163,11 @@
                     Credentials.convertToPem(privateKeyEntry.getCertificateChain()),
                     ALIAS_IMPORTED)).isTrue();
 
-        // No grant, all should still fail.
+        // No grant, Private key access should still fail.
         assertThat(mKeyChainService.requestPrivateKey(ALIAS_IMPORTED)).isNull();
-        assertThat(mKeyChainService.getCertificate(ALIAS_IMPORTED)).isNull();
-        assertThat(mKeyChainService.getCaCertificates(ALIAS_IMPORTED)).isNull();
+        // Certificate access succeeds because this test runs as AID_SYSTEM.
+        assertThat(mKeyChainService.getCertificate(ALIAS_IMPORTED)).isNotNull();
+        assertThat(mKeyChainService.getCaCertificates(ALIAS_IMPORTED)).isNotNull();
         // Grant access
         mTestSupportService.grantAppPermission(Process.myUid(), ALIAS_IMPORTED);
         // Has grant, all should succeed.
@@ -176,7 +181,7 @@
     @Test
     public void testUserSelectability() throws RemoteException, IOException, CertificateException {
         Log.d(TAG, "Testing user-selectability of a key.");
-        assertThat(mTestSupportService.keystoreReset()).isTrue();
+        removeKeyPair(ALIAS_IMPORTED);
         PrivateKeyEntry privateKeyEntry =
                 TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
         assertThat(mTestSupportService.installKeyPair(privateKeyEntry.getPrivateKey().getEncoded(),
@@ -207,18 +212,6 @@
     }
 
     @Test
-    public void testGenerateKeyPairErrorsOnSuperflousAttestationChallenge() throws RemoteException {
-        KeyGenParameterSpec specWithChallenge =
-                new KeyGenParameterSpec.Builder(buildRsaKeySpec(ALIAS_GENERATED))
-                        .setAttestationChallenge(DUMMY_CHALLENGE)
-                        .build();
-        ParcelableKeyGenParameterSpec parcelableSpec =
-                new ParcelableKeyGenParameterSpec(specWithChallenge);
-        assertThat(mTestSupportService.generateKeyPair("RSA", parcelableSpec)).isEqualTo(
-                KeyChain.KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE);
-    }
-
-    @Test
     public void testGenerateKeyPairErrorsOnInvalidAlgorithm() throws RemoteException {
         ParcelableKeyGenParameterSpec parcelableSpec = new ParcelableKeyGenParameterSpec(
                 buildRsaKeySpec(ALIAS_GENERATED));
@@ -247,31 +240,69 @@
         assertThat(mKeyChainService.requestPrivateKey(ALIAS_GENERATED)).isNotNull();
     }
 
-    @Test
-    public void testAttestKeyFailsOnMissingChallenge() throws RemoteException {
-        generateRsaKey(ALIAS_GENERATED);
-        assertThat(mTestSupportService.attestKey(ALIAS_GENERATED, null, new int[]{}
-                )).isEqualTo(KeyChain.KEY_ATTESTATION_MISSING_CHALLENGE);
-    }
+    private static final String USER_CERT1 =
+            "MIIC+DCCAeACAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMxEzARBgNV" +
+            "BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0" +
+            "ZDAeFw0yMTAyMTAwMDEyMDNaFw00ODA2MjgwMDEyMDNaMD8xCzAJBgNVBAYTAlVT" +
+            "MQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTWVkaW9jcmV0b24xDTALBgNVBAoMBENv" +
+            "Q28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2LieaIDILjZPtNvZI" +
+            "I6VSvKIPIdY5STadoWjmt9TqjyvszJ+oEceBPqwevOdtXpJCiHhP91B314XBT33z" +
+            "8re1b0A5k31YKcy0PU+3OMh4XG6O3/Z5/9GfsekfQZK3jagbn3uqJ2emyj0JK+HY" +
+            "ipD6iwyO21DerUYavPpC0uo8PKAxc9l6XjILg9qoi68yCi8P3tkLLAFWCsc7GUkA" +
+            "v97zUjGq8iz/gIrwmzJBB3O//7e1nuHO5AswgJiwWa9aY6uHKWm97xP0Kw4pShFI" +
+            "eEsdNKj82FURpWIvrWsztYPaEM/+nG8cNiJ1XRT3DfnlKzXVVQi7pE82HkUpFNLQ" +
+            "4wCbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAJwq05ykEDK5etwC5jDYsX7hyiYI" +
+            "TAephAfhMqT7CtwScVlurSWt4ivv3IBx2huibQqdwq9zyCG9UzQKACMrEAaINerr" +
+            "VPwq+MpfgOUJRlZOCchr1KaN+quoOstStsGqemHZSTyUow6OrG2a+DIGQZy84yza" +
+            "y4+NZYtTjbVKFAmQkji344cVd6qFjCG5Dgo+1u4+YOWV4QTVWwbjgjCGiDNyZKeO" +
+            "Y8n8pdsv4ygat4VCotch22bqiUJLgY/abiprqKz3jfkko9urHkWsrdqzc3mS29AC" +
+            "tWVhomOQq/51A0wfQRbSw2MY+j5LaF22Sz5rnPk0u6Vcm2eLUI5gSJ/HZOU=";
 
-    @Test
-    public void testAttestKeyFailsOnNonExistentKey() throws RemoteException {
-        assertThat(mTestSupportService.attestKey(ALIAS_NON_EXISTING, DUMMY_CHALLENGE, new int[]{}
-                )).isEqualTo(KeyChain.KEY_ATTESTATION_FAILURE);
-    }
+    private static final String USER_CERT2 =
+            "MIIDETCCAfkCFDEBBiFSwkBDAZMyhMjrr7P4wHklMA0GCSqGSIb3DQEBCwUAMEUx" +
+            "CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl" +
+            "cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjEwMjIyMTgwNzE2WhcNNDgwNzEwMTgw" +
+            "NzE2WjBFMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE" +
+            "CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC" +
+            "AQ8AMIIBCgKCAQEA8XLlpvOvG22fJ4ZNSEdc/QIzkbjmtP5rmqX8dgfZdZ7xdUNO" +
+            "24Coyv9f+GiXJdDlL2+KIK3ZTpcuVdd5oO+GdrxB1Dnvhqt2YgOb/+VyeMOdsEre" +
+            "Bqw4Y+v8A92zpWlYlh9xyjVE53ismdQg2RT8kIxQt2ydMyZRx8KzNmZRsXouiVSi" +
+            "ngWlXdtkpXNiVt5CkSBwscfVNMkF4EfcKqYGLJzFTXcSsRVZoknGNtgJZIsHaOJh" +
+            "etmrfxATcbvNYdDhm9xs2ud/WwaRDqbMme9KVOrk9g5NBZIn7SpWDUyBk36W2CgQ" +
+            "U/3OPnDOUS6oW+YKE1xvJ3i2FhLD2ufJyNh4WwIDAQABMA0GCSqGSIb3DQEBCwUA" +
+            "A4IBAQDkl+8ZlYgKwSHjqxHjJsoBXBlwWUw0FQDaFz0tlPv/f5w80NsTXImVgRcP" +
+            "MBWxLrVa/JKCm7GPZgIotvYsRKL0/DRZDUuva86C2hi9C/E7OtgFkDO0d/t9QIQM" +
+            "gSNgdEgda/+lixc7XrsjJKAufFlYhzk/q5uyiD3SbqFzdlADumWtY9Xu6fqU2JB5" +
+            "TFOTxqpU5c2b+sXL6uc+dA2pSP94wL+7g+uKVhdJGsimbXIq1jl9r+C2ykrUjuz+" +
+            "b7uBJ5Qzq81tdmNCZ4pLtqmatTAMj2LYsKiXUe9Fh0nd/2aZek6I1YHaIBGg7jns" +
+            "ZYEWPj4Rd0KiE3L/ymeQ4VQx6SW2";
 
-    @Test
-    public void testAttestKeySucceedsOnGeneratedKey() throws RemoteException {
-        generateRsaKey(ALIAS_GENERATED);
-        assertThat(mTestSupportService.attestKey(ALIAS_GENERATED, DUMMY_CHALLENGE,
-                null)).isEqualTo(KeyChain.KEY_ATTESTATION_SUCCESS);
-    }
+    private static final String CA_CERT =
+            "MIIDazCCAlOgAwIBAgIUVA2nyBJMc/OcO0C/yPt/E1TwREIwDQYJKoZIhvcNAQEL" +
+            "BQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM" +
+            "GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAyMDkyMzAzMzdaFw00ODA2" +
+            "MjcyMzAzMzdaMEUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw" +
+            "HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB" +
+            "AQUAA4IBDwAwggEKAoIBAQDxcuWm868bbZ8nhk1IR1z9AjORuOa0/muapfx2B9l1" +
+            "nvF1Q07bgKjK/1/4aJcl0OUvb4ogrdlOly5V13mg74Z2vEHUOe+Gq3ZiA5v/5XJ4" +
+            "w52wSt4GrDhj6/wD3bOlaViWH3HKNUTneKyZ1CDZFPyQjFC3bJ0zJlHHwrM2ZlGx" +
+            "ei6JVKKeBaVd22Slc2JW3kKRIHCxx9U0yQXgR9wqpgYsnMVNdxKxFVmiScY22Alk" +
+            "iwdo4mF62at/EBNxu81h0OGb3Gza539bBpEOpsyZ70pU6uT2Dk0FkiftKlYNTIGT" +
+            "fpbYKBBT/c4+cM5RLqhb5goTXG8neLYWEsPa58nI2HhbAgMBAAGjUzBRMB0GA1Ud" +
+            "DgQWBBSsg7KE2+ypUJrrk9pBIVYpgjZM3TAfBgNVHSMEGDAWgBSsg7KE2+ypUJrr" +
+            "k9pBIVYpgjZM3TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBx" +
+            "pUWwTL8vsrq3hNEDOui1Y6kYPiaAs2Fd//tBjPhgc9tlKS8KLkGU0yW8kEyPovvK" +
+            "M6/IUDZhrHzli8Y9iJxiZ43o/fni1DOLHLU+CCOaKGrmBDBhgDlz1m2nXsCOb8uF" +
+            "ohz4Yp1SLMy1YpQPmltiaILXNTw33B8xFa0d9ChZbBudyiiNs0vijdFMMYetqAGM" +
+            "aXCjPXdM6hGjHxc6vf2ECJOrfVg3D22VOJ0WzrrJDM9pRxJgJ/IFG0eirKvE7G8s" +
+            "zk1Og79JAFr3qMKEnUS7nuym7J69HSQlFHc7JEMzeS78YR7EHOlOlK/bEX/8cHF9" +
+            "ZlGCOS1Ds0rmVe3CoQIp";
 
     @Test
     public void testSetKeyPairCertificate() throws RemoteException {
         generateRsaKey(ALIAS_GENERATED);
-        final byte[] userCert = new byte[] {'a', 'b', 'c'};
-        final byte[] certChain = new byte[] {'d', 'e', 'f'};
+        final byte[] userCert = Base64.decode(USER_CERT1, Base64.DEFAULT);
+        final byte[] certChain = Base64.decode(CA_CERT, Base64.DEFAULT);
 
         assertThat(mTestSupportService.setKeyPairCertificate(ALIAS_GENERATED, userCert,
                 certChain)).isTrue();
@@ -280,7 +311,8 @@
         assertThat(mKeyChainService.getCertificate(ALIAS_GENERATED)).isEqualTo(userCert);
         assertThat(mKeyChainService.getCaCertificates(ALIAS_GENERATED)).isEqualTo(certChain);
 
-        final byte[] newUserCert = new byte[] {'x', 'y', 'z'};
+        final byte[] newUserCert = Base64.decode(USER_CERT2, Base64.DEFAULT);
+
         assertThat(mTestSupportService.setKeyPairCertificate(ALIAS_GENERATED, newUserCert,
                 null)).isTrue();
         assertThat(mKeyChainService.getCertificate(ALIAS_GENERATED)).isEqualTo(newUserCert);
@@ -364,35 +396,54 @@
         }
     }
 
-    void installFirstKey() throws RemoteException, IOException, CertificateException {
-        String intermediate = "-intermediate";
-        String root = "-root";
+    @Test
+    public void testContainsKeyPair_NonExisting() throws RemoteException {
+        assertThat(mKeyChainService.containsKeyPair(ALIAS_NON_EXISTING)).isFalse();
+    }
 
-        String alias1PrivateKey = Credentials.USER_PRIVATE_KEY + ALIAS_1;
-        String alias1ClientCert = Credentials.USER_CERTIFICATE + ALIAS_1;
-        String alias1IntermediateCert = (Credentials.CA_CERTIFICATE + ALIAS_1 + intermediate);
-        String alias1RootCert = (Credentials.CA_CERTIFICATE + ALIAS_1 + root);
-        PrivateKeyEntry privateKeyEntry =
+    @Test
+    public void testContainsKeyPair_ImportedKey() throws Exception {
+        installTestRsaKey();
+        assertThat(mKeyChainService.containsKeyPair(ALIAS_IMPORTED)).isTrue();
+    }
+
+    @Test
+    public void testContainsKeyPair_RemovedKey() throws Exception {
+        installTestRsaKey();
+        mKeyChainService.removeKeyPair(ALIAS_IMPORTED);
+        assertThat(mKeyChainService.containsKeyPair(ALIAS_IMPORTED)).isFalse();
+    }
+
+    @Test
+    public void testGetGrants_NonExisting() {
+        assertThrows(IllegalArgumentException.class,
+                () -> mKeyChainService.getGrants(ALIAS_NON_EXISTING));
+    }
+
+    @Test
+    public void testGetGrants_Empty() throws Exception {
+        installTestRsaKey();
+
+        assertThat(mKeyChainService.getGrants(ALIAS_IMPORTED)).isEmpty();
+    }
+
+    @Test
+    public void testGetGrants_NonEmpty() throws Exception {
+        final int uid = Process.myUid();
+
+        installTestRsaKey();
+        mTestSupportService.grantAppPermission(uid, ALIAS_IMPORTED);
+
+        assertThat(mKeyChainService.getGrants(ALIAS_IMPORTED)).isEqualTo(new int[] {uid});
+    }
+
+    private void installTestRsaKey() throws Exception {
+        final PrivateKeyEntry privateKeyEntry =
                 TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
-        Certificate intermediate1 = privateKeyEntry.getCertificateChain()[1];
-        Certificate root1 = TestKeyStore.getClientCertificate().getRootCertificate("RSA");
-
-        assertThat(
-                mTestSupportService.keystoreImportKey(
-                    alias1PrivateKey, privateKeyEntry.getPrivateKey().getEncoded()))
-            .isTrue();
-        assertThat(
-                mTestSupportService.keystorePut(
-                    alias1ClientCert,
-                    Credentials.convertToPem(privateKeyEntry.getCertificate())))
-            .isTrue();
-        assertThat(
-                mTestSupportService.keystorePut(
-                    alias1IntermediateCert, Credentials.convertToPem(intermediate1)))
-            .isTrue();
-        assertThat(
-                mTestSupportService.keystorePut(alias1RootCert, Credentials.convertToPem(root1)))
-            .isTrue();
+        mTestSupportService.installKeyPair(privateKeyEntry.getPrivateKey().getEncoded(),
+                privateKeyEntry.getCertificate().getEncoded(),
+                Credentials.convertToPem(privateKeyEntry.getCertificateChain()),
+                ALIAS_IMPORTED);
     }
 
     void waitForSupportService() {
diff --git a/tests/src/com/android/keychain/tests/KeyChainActivityTest.java b/tests/src/com/android/keychain/tests/KeyChainActivityTest.java
index 73c3867..9acb6de 100644
--- a/tests/src/com/android/keychain/tests/KeyChainActivityTest.java
+++ b/tests/src/com/android/keychain/tests/KeyChainActivityTest.java
@@ -17,40 +17,41 @@
 package com.android.keychain.tests;
 
 import static com.android.keychain.KeyChainActivity.CertificateParametersFilter;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.LargeTest;
-import android.security.Credentials;
-import android.security.KeyStore;
 import android.util.Base64;
+
 import androidx.test.runner.AndroidJUnit4;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-import javax.security.auth.x500.X500Principal;
-import org.bouncycastle.util.io.pem.PemObject;
-import org.bouncycastle.util.io.pem.PemWriter;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CancellationException;
+
+import javax.security.auth.x500.X500Principal;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class KeyChainActivityTest {
@@ -58,22 +59,25 @@
     // openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 3650
     //   -out root_ca_certificate.pem
     private static final String ROOT_CA_CERT_RSA =
-            "MIIDazCCAlOgAwIBAgIUWQjj+9olDNtdjcSLzK2RpxI9j6UwDQYJKoZIhvcNAQELBQAwRTELMAkG" +
-            "A1UEBhMCVUsxCzAJBgNVBAgMAk5BMQ8wDQYDVQQHDAZMb25kb24xGDAWBgNVBAoMD0FuZHJvaWQg" +
-            "VGVzdCBDQTAeFw0yMDAzMTIxMTUxNDJaFw0zMDAzMTAxMTUxNDJaMEUxCzAJBgNVBAYTAlVLMQsw" +
-            "CQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMRgwFgYDVQQKDA9BbmRyb2lkIFRlc3QgQ0EwggEi" +
-            "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGbsQO+LqPMr5Nr4Lq0B7C0th93phohSY6hb2w" +
-            "MmZs3MRwamlw8FS64KEgmszX5lnyLNqRs91FOFNuq4f2A+TYQhawi9D2bHB7z2ishDM3SxNAqwQl" +
-            "LzVNBJw7DAtimajy3VvXoprescFbsOZx8wPGGb2xMKqAXg4Yw9F6te4Y4BSIiwCWtammiSR8Ev0B" +
-            "lcnMBrWmSZ4yYF+UgNgNiD/TVrTtRmzQlRhBo5n4F61SGeAxb5p0NRRGmAXKtx358HiLANzZSCiM" +
-            "UE5IrgDvW8AKPn5InuYS1G1K2wG5ar1eanQahimtaIEugQxhqG0+/OiKKq2LGRiBpwV1OomXHNFr" +
-            "AgMBAAGjUzBRMB0GA1UdDgQWBBRrxYWzKZpCHDi/NK4keXIU5iGukzAfBgNVHSMEGDAWgBRrxYWz" +
-            "KZpCHDi/NK4keXIU5iGukzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBJTjUY" +
-            "rfi3adJFpF2IFkSnavPRxi+NX6wxfKgQvcScO7sV18gAMG7r2NhjsVeScZ48mxsNkj99lHaVoNm6" +
-            "c+sWmcb3LO7WCqmAgfJcHeQ5VuluwNoJWo+SGuKbh6/yRejNeQFf++uaEwXP3yNydwKJyQDyDwoG" +
-            "vx0jvy8glkVl3fr6u0lGQqmubGU5Q1X6QyA0zJ/sSWBVorLCgk6KvPABQJhjoij+g/GOB1h4g6fb" +
-            "bQ3xnek6TGwjQ1bB7rQlqBF7iP/9iUtuuDf0cR8LwMr1Z2OUEMDjRHQCZQJ3APc0kW1ewJ8nqQ+m" +
-            "NsUKFRuThYtE/OFsV/TfXwMXbc2rMug+";
+            "MIIDazCCAlOgAwIBAgIUKNVj8g/LG+6tqsK8HZqT6EuktpkwDQYJKoZIhvcNAQEL" +
+            "BQAwRTELMAkGA1UEBhMCVUsxCzAJBgNVBAgMAk5BMQ8wDQYDVQQHDAZMb25kb24x" +
+            "GDAWBgNVBAoMD0FuZHJvaWQgVGVzdCBDQTAeFw0yMTAyMjMwMDU0MzdaFw0zMTAy" +
+            "MjEwMDU0MzdaMEUxCzAJBgNVBAYTAlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwG" +
+            "TG9uZG9uMRgwFgYDVQQKDA9BbmRyb2lkIFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEB" +
+            "AQUAA4IBDwAwggEKAoIBAQDLpcaoJijYhS3QUDgG8kVGrwTxaVTS0TE156fJa5za" +
+            "s3RI7TYKHzYwn1KMJJUwoc+cOkv+rFC7j5MQ+6SMK2GpDoCoGn4FV9dDPVnxIgjj" +
+            "/66kuf+we1ur3gz7m/8tFFdZhLFoFMRzcNg+F35jSur8y0dnc8O83gMwuf+91pU7" +
+            "HyNahHzDyMM5sR7u1K91R1MKiOnVqJNTHWVK+rl3G0m0rbDTz7/xbq3/FPBvw764" +
+            "QUJgkSEG15i5CFNn2ww5IYnF30Wbke3kUfdd/Q32MOyfkcp3El/TPJj/3mevGHed" +
+            "vTi0j6ovIXMrDnoJvmeI40p97EMIlaZ3x39i+krE02RfAgMBAAGjUzBRMB0GA1Ud" +
+            "DgQWBBRDpNR2iik8NGDmY5IvgiUy6dMRMjAfBgNVHSMEGDAWgBRDpNR2iik8NGDm" +
+            "Y5IvgiUy6dMRMjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDG" +
+            "OIZdoopwX2PlkAnWMR6PIl4GJ9eiPg1cbQHHcqsB8oNYiiiZydJ8TniZYFptEIjQ" +
+            "LIuerSr/a+c353JNOOK76w0vciJld8uQDCTnMChUahJsQjJJgDCyhjkQqV9Srstu" +
+            "IClm5IP/LEYVSzfR+LqhwX5JYUU8zsDnbiAH3CIENhBw5ApXgfDHP54bHOBw5eHE" +
+            "ETATnib8uPZVHsyHQXXULDqMITY9pmjc9/9x6CqcMGEiSVvhDSujerVlnw3I17Te" +
+            "HWT8bf8mJyQFR8kr59NPQMNN4oUIfFzj2VgzjL21mKGR+hBGqoIMJ1ZdPGCJsw0W" +
+            "5WYM4TtQtL7yNVDtJzOL";
 
     // Generated with:
     // openssl genrsa  -out intermediate_key.pem 2048
@@ -81,42 +85,74 @@
     // openssl x509 -req -days 3650 -in intermediate_ca.csr -CA root_ca_certificate.pem
     //   -CAkey key.pem -CAcreateserial -out intermediate_ca.pem
     private static final String INTERMEDIATE_CA_CERT_RSA =
-            "MIIDHjCCAgYCFHVrA0CEKcoc/C8BqKap3a1m0x8CMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYT" +
-            "AlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMRgwFgYDVQQKDA9BbmRyb2lkIFRlc3Qg" +
-            "Q0EwHhcNMjAwMzEyMTE1NzUwWhcNMzAwMzEwMTE1NzUwWjBSMQswCQYDVQQGEwJVSzELMAkGA1UE" +
-            "CAwCTkExDzANBgNVBAcMBkxvbmRvbjElMCMGA1UECgwcQW5kcm9pZCBJbnRlcm1lZGlhdGUgVGVz" +
-            "dCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPlEIzApeTyKzQWTvv25z/KVEbsr" +
-            "alrcto7mX56HV1VQ53cGqi4I7dso3cvpg0CYfcZ+mZh6Evkd+njkcc7Dh/nI0KBJIzGZuo2LB+0r" +
-            "qT0RfvI/Xv7CqLO9KOjWJ3+HK3EhSXGvnLvSTsQD1LnE9HXKVdhdOUgLFjbcZrzH62mvTRAO6nhg" +
-            "agWTzprTXOX8okaMJtJl9QGMG63Z/m5DONPPrgASW6X6wksGjyorEaakQTUGuPimapP5mk+Y31Se" +
-            "pLDDumqRavLT2CpfjHfFq0iDmnnJjG5nz6oKlirhg9JjxHwuKm5jIdsO4dIgi1fJ8Goz2ODG6R6E" +
-            "6CjitsQjoMMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdud6PahLSmDLOTr9t0Jqq1HKpeRsjSqn" +
-            "JTpd/GkNUrxcctKLTzpAruMq6en8OfcSEa2s3HUMJ1LVMoO9pp4aQaadH7626Q4uqHbNGHWngVNX" +
-            "lfBioxrH2QJ2wuKBjUipEGWaM3LY0wqNuBFd5qAVuBwQtZ1x/XH7/Y4l38Y5EGGEi4jSw8eCqiiQ" +
-            "2UKItmK8byl3/T5SVgAMbFYz1WJN37EgETEcEgPlosSQ4pha6fVB3Oz6mSfzjGXqHKpHBPUn/N2d" +
-            "Z2kxJG8IuwhhhyemhqJdCfOxT2WpemgLQQCCgqtM9O89peWL8AJUVT9cF9KySOvh/P9lTtSf5bJf" +
-            "sAzfSg==";
+            "MIIDHjCCAgYCFHgZBbZMuJTvvm1wlBBoPE7peS4jMA0GCSqGSIb3DQEBCwUAMEUx" +
+            "CzAJBgNVBAYTAlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMRgwFgYD" +
+            "VQQKDA9BbmRyb2lkIFRlc3QgQ0EwHhcNMjEwMjIzMDA1ODQ5WhcNMzEwMjIxMDA1" +
+            "ODQ5WjBSMQswCQYDVQQGEwJVSzELMAkGA1UECAwCTkExDzANBgNVBAcMBkxvbmRv" +
+            "bjElMCMGA1UECgwcQW5kcm9pZCBJbnRlcm1lZGlhdGUgVGVzdCBDQTCCASIwDQYJ" +
+            "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZ5tLvS8qiZGK8fBUPzZYWRG2kmf3Hi" +
+            "o6db8qjTvzkJ0l43zmI92v0pEy8a4/XlC7wGSaJuX/TqpAR43+a9kYqGWczkASpX" +
+            "7w1W5tjjDlLANZdRP2R7Wb3XWTTMxzDWOqsMGTHw+H92oFm4bGO9+PaBRRzifLmQ" +
+            "OI/Whw6/kHZlXyI7J68EwRbyaZXNJ6iqk8dF7B4iwZ9yNgW1H0m8uxdDAykEA2WX" +
+            "wbzITwD1zdMsQV7/eLe9RIMFN5VMeHtIFUi2AcioG0i4ZLZNFnVrFQKu7u3XQyxk" +
+            "u7ZzgeGtpluM71sDxnqhZv9NaZIq3mV3JrKHPsw6+uJjN4U5AVfzIYUCAwEAATAN" +
+            "BgkqhkiG9w0BAQsFAAOCAQEAXrRQUFlLyS3QlmwkGocLQISY9B8fF8LTH7sl6HFA" +
+            "VSVuhPDuNsmqVhsMH1981MY2rSVfM3fkUMz1WEH7ZbhooYPirax/AlW+oRdRB/xX" +
+            "WEAJRGgybK98PXogI4tqEvicVn2kfcyNzmfMn8yRClxD5GuZ0oOA50lpUwUmeJjo" +
+            "jb3DY8NF+bcA0lW5h7p86ezqjhB836XZRL47jZJj+jgKoiSsdcex7rzikW6bzdlV" +
+            "f9DCuBJMpM1y31AP15Gvg5Jhh9Wc4y8LLipTn7wGdJvvhclUe1U3roerhAEB+rU/" +
+            "Eu7h+Nqjogg3IzmfHlhDe4N8o/XLdc2FnhCZGZklDDDMvA==";
 
     // Generated with:
     // openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr
     // openssl x509 -req -days 3650 -in server.csr -CA intermediate_ca.pem
     //   -CAkey intermediate_key.pem -CAcreateserial -out server.pem
     private static final String LEAF_SERVER_CERT_RSA =
-            "MIIDIjCCAgoCFGNstHCN7uzPtFlxnbu2FQ3+sc7rMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYT" +
-            "AlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMSUwIwYDVQQKDBxBbmRyb2lkIEludGVy" +
-            "bWVkaWF0ZSBUZXN0IENBMB4XDTIwMDMxMjEyMTQwMloXDTMwMDMxMDEyMTQwMlowSTELMAkGA1UE" +
-            "BhMCVUsxCzAJBgNVBAgMAk5BMQ8wDQYDVQQHDAZMb25kb24xHDAaBgNVBAoME0FuZHJvaWQgU2Vy" +
-            "dmVyIFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCBdt0uV01nG2ULMThfnEx" +
-            "CDoVvOSJJLcqKSMsoRUcEYvvqA70x7O2BR6+FZLilr09qAyK1ygyTh+Q6NcBxiB137khrygd/R3S" +
-            "U9nNWI6Xj882EospCiPaewR3j49F6F45PvviAacx7v0mZpU1dVMP7ZvhJWb1AB675/379EFztYyU" +
-            "ghM5ub8zuAgBuvH/O/dJpnmKUmO43n/qSHDM+Q+oDmLAkWD4gd3SOBZKcLgTyO/pD0pghT8m2t0t" +
-            "9X/3KX+tQhif1pAemCOqvxr/HHjlLWxY+QWmcRIAMzTg6+h7sNSKAwwfulMomzMwzkV5sJAYUJOm" +
-            "suhhyAXCNX5rWuqXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACdFD/uV4iT4jg3/rEtszPHkyP4b" +
-            "zHaYJYpExoWNbJIz7djhycptdM7wjSoesWQMfpJz95aNWoVHrW85DSdGT+7HwZEsW1zUx3KkXURA" +
-            "CdlbVBn1CS4vm0Xk7Rr3LayfhqdFALQVItvBr+LkJPiG/R1jQySp1qaw+NrwukFoepukeZxHH1bF" +
-            "9zjGCLwOcfuRB9g8Gm45wRgoSUTDKbD1SMSLuPyllKeHLKE+chhYjm51Evy2xR0DLm0yaFmeyPPM" +
-            "KQaZJmtkeAzk2uYYb1HsVDlnEoDoXpTKKN1j39qckpCHxF0X0KqbN7D0grDWIDIee5mnSrg3+Xq5" +
-            "aCEDTUn6uU4=";
+            "MIIDIjCCAgoCFDV+Rg1WxBy4ThLxvu1lRJl1YUzwMA0GCSqGSIb3DQEBCwUAMFIx" +
+            "CzAJBgNVBAYTAlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMSUwIwYD" +
+            "VQQKDBxBbmRyb2lkIEludGVybWVkaWF0ZSBUZXN0IENBMB4XDTIxMDIyMzAxMDEw" +
+            "OFoXDTMxMDIyMTAxMDEwOFowSTELMAkGA1UEBhMCVUsxCzAJBgNVBAgMAk5BMQ8w" +
+            "DQYDVQQHDAZMb25kb24xHDAaBgNVBAoME0FuZHJvaWQgU2VydmVyIFRlc3QwggEi" +
+            "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7267U6iCagBJFiYMMUkteQliO" +
+            "ljOfnuSZJTG1xxNXBggLDdHmchjOPQgEICINpxz7Hhg+PLME3DEqrwUHo9k/bR3e" +
+            "Tglt1o6qxGirIEKROtdzNyi3medRex6FATXq1g4W/1U0tl8EbMv9kJPZP52Uj0Rq" +
+            "XYZ9Y27IGtWDcudpyJij/nBbV/kfufti2pFNHhnXytyBrQXz6AziZjHnNt316z64" +
+            "Tfqchr0jxqIF3Sup9AVKnGooGymwT8ez5C6VO7WoRZnp40pH78GzSALnRJC9w26V" +
+            "1IVlqjYPWvFwJ0ENb0Nuc8Jr3tW/0gf1UzTsuRsMU9vl+tMjweXS8HI8M4uRAgMB" +
+            "AAEwDQYJKoZIhvcNAQELBQADggEBAIcOLAdu9HU6+Bk/SkQW2qW2mY5+WFSYYRG5" +
+            "KfDiMQr0kUeddBJYk+bLN3Qqi6KUAZ+ITxyarVrjZjxaTr1JV6m9fVxdZ8elAx+7" +
+            "ci9ghBkiUPKFtVz3Y1F31Em4tLRr0LHF49Mjvr62+mQuZlAXZ3TuMdxrwc9AePhN" +
+            "btY8YwUsPPJ2vrIQB14NJ6EIaMaIyTowBPX9eo5K47ISbfnCUKhFYAEK4v5s4Hkt" +
+            "Us/209E0FNdFKOZDKDwclhZSFbyA1tknBRXsP7QCFuj/hPFxcRaj9R8CoHQAqEFr" +
+            "mV8JscA9dV40m+kRkj5TZeHjIw2xI39btv5aeRW2fBNB0MEoNPQ=";
+
+    private static final String PRIVATE_SERVER_KEY =
+            "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC7267U6iCagBJF" +
+            "iYMMUkteQliOljOfnuSZJTG1xxNXBggLDdHmchjOPQgEICINpxz7Hhg+PLME3DEq" +
+            "rwUHo9k/bR3eTglt1o6qxGirIEKROtdzNyi3medRex6FATXq1g4W/1U0tl8EbMv9" +
+            "kJPZP52Uj0RqXYZ9Y27IGtWDcudpyJij/nBbV/kfufti2pFNHhnXytyBrQXz6Azi" +
+            "ZjHnNt316z64Tfqchr0jxqIF3Sup9AVKnGooGymwT8ez5C6VO7WoRZnp40pH78Gz" +
+            "SALnRJC9w26V1IVlqjYPWvFwJ0ENb0Nuc8Jr3tW/0gf1UzTsuRsMU9vl+tMjweXS" +
+            "8HI8M4uRAgMBAAECggEARWNUhYJhPpAVr6emRxPSkONysGAce1YGW+bYIKuCoj8x" +
+            "E1wsbrEwJmV2o4d27JIQa1TnYX2sJhxq8Lgq5HKJ2Rql0KoEY5S/p6Xaf3LwA5K3" +
+            "Z/A00vQ+8+LFGB2lW7NrCuWPBGRkXk8NXgBcC/+qZegxPhSDi6cBkVoQCXiUr4Zs" +
+            "4wacrmpQbl/FekvpuWQxnVAm95knZhJ87r7izwO6e3VP1VZseG7ld6PbxkLnuTP7" +
+            "Y9Q+viAPwkk1SedvYy6RtIRbyyOKzTVbh9SXirPsLM6N+a8k3J7LY+8HnZhte0O4" +
+            "BFoZwwhXt/kPHilro6gt69Bh2bas+lpP62c8e7Q3HQKBgQDwY6XJzLXV1UQNbwLL" +
+            "FHTDBdf3Afe6W5jcKpsOUaNP/424JeLpc5a2ccaytCPs2p3RCTZ3SHaxe+LNrKbU" +
+            "iKdloGO/XTqcAHbvw/uy5Zzsv6XqUjREmCvCatF7UU7PH9Pztjhb91IXT7taB1Ee" +
+            "yYyPPJU9ioZDEV45/PIUpGwjnwKBgQDIDrtHx5fBH0od8XWSBzuQ3QjLl3WZcEAv" +
+            "Bf2GcdYUV7migWOXm28k51l2VbQQRCVOulJjf45TZgZgzKInZZJ+rZ69FLrOx7q3" +
+            "7X+8rTyWKa2u/IlZ/EBtNIRuqH7A+OuY5qroFngtYa/qsaX58/eUAe64I6HslenG" +
+            "ktvmwdSCzwKBgAtQ1YQLU9/t+xcay6ndm6V2h/UDrbKjDy4F/2iMJUDlybkKZ4UP" +
+            "wN9zuaO94RcML3OgmGTDD3tJVqLR5sSIbkDVbPycGd8wEmk084s3TczDNL80AWvd" +
+            "Meoj9xpz+F69o8+MG1kQ6ldYlHwnbgUh/bDcbDYKaEmN7r6SDp80IjcHAoGAOrqQ" +
+            "Yf8G3qu3z1h94jN7Wgh5N4MsA7I/NU614Uzzwp8KINmJCg2YMCY2ThXUuV238gei" +
+            "fhEJEBSIVMxd4eDgg42mZu159ZAOkUYIVLQqcA6mLRN3otH5e9WJ9w5Bv5aTWxyE" +
+            "GYPXHcNqqCQkjF8BVBLJKIdVVqWfriqYoYJPR2MCgYAWWIrS8XN1c2iLParb+xOJ" +
+            "q1WKe8q5wFucqJCVFbJXjtpGgZFxZLAFlT8VpaiBwDLQBPC+CYhzLVwAvzW2h46W" +
+            "KONqO7zoxiuwOEj466iH4YrgviNw6lGtgSPB6wx91c7se/1lcZhviBMB5rUjtxxj" +
+            "qKIC9Y+gz77w1M3pwMlhDA==";
 
     private static final X500Principal LEAF_SUBJECT =
             new X500Principal("O=Android Server Test, L=London, ST=NA, C=UK");
@@ -127,44 +163,63 @@
     private static final X500Principal ROOT_SUBJECT =
             new X500Principal("O=Android Test CA, L=London, ST=NA, C=UK");
 
+    private byte[] mPrivateKey;
     private byte[] mLeafRsaCertificate;
     private byte[] mIntermediateRsaCertificate;
     private byte[] mRootRsaCertificate;
 
     @Before
     public void setUp() {
+        mPrivateKey = Base64.decode(PRIVATE_SERVER_KEY, Base64.DEFAULT);
         mLeafRsaCertificate = Base64.decode(LEAF_SERVER_CERT_RSA, Base64.DEFAULT);
         mIntermediateRsaCertificate = Base64.decode(INTERMEDIATE_CA_CERT_RSA, Base64.DEFAULT);
         mRootRsaCertificate = Base64.decode(ROOT_CA_CERT_RSA, Base64.DEFAULT);
     }
 
+    @After
+    public void tearDown() {
+        KeyStore keyStore = null;
+        try {
+            keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            keyStore.deleteEntry("testCertificateParametersFilter_client");
+        } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException
+                | IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Test
     public void testCertificateParametersFilter_filtersByIntermediateIssuer()
-            throws InterruptedException, ExecutionException, CancellationException,
-            TimeoutException, IOException, CertificateEncodingException {
+            throws CancellationException, IOException, CertificateException,
+            KeyStoreException, NoSuchAlgorithmException {
         KeyStore keyStore = prepareKeyStoreWithLongChainCertificates();
 
         assertThat(createCheckerForIssuer(keyStore, ROOT_SUBJECT)
-                .shouldPresentCertificate("client")).isTrue();
+                .shouldPresentCertificate("testCertificateParametersFilter_client")).isTrue();
 
         assertThat(createCheckerForIssuer(keyStore, INTERMEDIATE_SUBJECT)
-                .shouldPresentCertificate("client")).isTrue();
+                .shouldPresentCertificate("testCertificateParametersFilter_client")).isTrue();
 
         assertThat(createCheckerForIssuer(keyStore, LEAF_SUBJECT)
-                .shouldPresentCertificate("client")).isFalse();
+                .shouldPresentCertificate("testCertificateParametersFilter_client")).isFalse();
     }
 
     // Return a KeyStore instance that has both a client certificate as well as a certificate
     // chain associated with it.
     private KeyStore prepareKeyStoreWithLongChainCertificates()
-            throws IOException, CertificateEncodingException {
-        KeyStore keyStore = mock(KeyStore.class);
-        when(keyStore.get(Credentials.USER_CERTIFICATE + "client")).thenReturn(mLeafRsaCertificate);
-        Certificate[] intermediates = new Certificate[] {
-            parseCertificate(mRootRsaCertificate), parseCertificate(mIntermediateRsaCertificate)};
-        byte[] intermediatesPem = convertToPem(intermediates);
-        assertThat(intermediatesPem).isNotNull();
-        when(keyStore.get(Credentials.CA_CERTIFICATE + "client")).thenReturn(intermediatesPem);
+            throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
+
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+
+        Certificate[] certs = new Certificate[3];
+        certs[0] = parseCertificate(mLeafRsaCertificate);
+        certs[1] = parseCertificate(mIntermediateRsaCertificate);
+        certs[2] = parseCertificate(mRootRsaCertificate);
+
+        keyStore.setKeyEntry("testCertificateParametersFilter_client", parseKey(mPrivateKey),
+                null, certs);
 
         return keyStore;
     }
@@ -189,16 +244,13 @@
         }
     }
 
-    // Copied from android.security.Credentials, as that is a framework class.
-    public static byte[] convertToPem(Certificate... objects)
-            throws IOException, CertificateEncodingException {
-        ByteArrayOutputStream bao = new ByteArrayOutputStream();
-        Writer writer = new OutputStreamWriter(bao, StandardCharsets.US_ASCII);
-        PemWriter pw = new PemWriter(writer);
-        for (Certificate o : objects) {
-            pw.writeObject(new PemObject("CERTIFICATE", o.getEncoded()));
+    private static PrivateKey parseKey(byte[] key) {
+        try {
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return kf.generatePrivate(new PKCS8EncodedKeySpec(key));
+        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+            fail(String.format("Could not parse private key: %s", e));
+            return null;
         }
-        pw.close();
-        return bao.toByteArray();
     }
 }