Merge remote-tracking branch 'goog/stage-aosp-master' into HEAD
am: be06f9efe5

Change-Id: I207bd3b4148621fde1ea345da7f92a4d3527c3f6
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index ae51f98..841f1bd 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -22,7 +22,7 @@
     <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_button_label" msgid="903474285774077171">"শংসাপত্র ইনস্টল করুন"</string>
+    <string name="install_new_cert_button_label" msgid="903474285774077171">"সার্টিফিকেট ইনস্টল করুন"</string>
     <string name="allow_button" msgid="3030990695030371561">"বেছে নিন"</string>
     <string name="deny_button" msgid="3766539809121892584">"আস্বীকার করুন"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..36b09a7
--- /dev/null
+++ b/res/values-en-rCA/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2011 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="170210454004696382">"Key Chain"</string>
+    <string name="title_no_certs" msgid="8350009443064722873">"No certificates found"</string>
+    <string name="title_select_cert" msgid="3588447616418041699">"Choose certificate"</string>
+    <string name="requesting_application" msgid="1589142627467598421">"The %s app has requested a certificate. Choosing a certificate will let the app use this identity with servers now and in the future."</string>
+    <string name="requesting_server" msgid="5832565605998634370">"The app has identified the requesting server as %s, but you should only give the app access to the certificate if you trust the app."</string>
+    <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="install_new_cert_button_label" msgid="903474285774077171">"Install certificate"</string>
+    <string name="allow_button" msgid="3030990695030371561">"Select"</string>
+    <string name="deny_button" msgid="3766539809121892584">"Deny"</string>
+</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..b75e2e9
--- /dev/null
+++ b/res/values-en-rXC/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2011 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="170210454004696382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎Key Chain‎‏‎‎‏‎"</string>
+    <string name="title_no_certs" msgid="8350009443064722873">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎No certificates found‎‏‎‎‏‎"</string>
+    <string name="title_select_cert" msgid="3588447616418041699">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎Choose certificate‎‏‎‎‏‎"</string>
+    <string name="requesting_application" msgid="1589142627467598421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‎The app %s has requested a certificate. Choosing a certificate will let the app use this identity with servers now and in the future.‎‏‎‎‏‎"</string>
+    <string name="requesting_server" msgid="5832565605998634370">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎The app has identified the requesting server as %s, but you should only give the app access to the certificate if you trust the app.‎‏‎‎‏‎"</string>
+    <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="install_new_cert_button_label" msgid="903474285774077171">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎Install certificate‎‏‎‎‏‎"</string>
+    <string name="allow_button" msgid="3030990695030371561">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎Select‎‏‎‎‏‎"</string>
+    <string name="deny_button" msgid="3766539809121892584">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎Deny‎‏‎‎‏‎"</string>
+</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b61cbe7..d7bfde4 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,8 +19,8 @@
     <string name="app_name" msgid="170210454004696382">"की चेन"</string>
     <string name="title_no_certs" msgid="8350009443064722873">"कोई प्रमाणपत्र नहीं मिला"</string>
     <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="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_button_label" msgid="903474285774077171">"प्रमाणपत्र इंस्‍टॉल करें"</string>
     <string name="allow_button" msgid="3030990695030371561">"चुनें"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index e0eb18b..42aad08 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -22,7 +22,7 @@
     <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_button_label" msgid="903474285774077171">"ಪ್ರಮಾಣಪತ್ರವನ್ನು ಸ್ಥಾಪಿಸು"</string>
+    <string name="install_new_cert_button_label" msgid="903474285774077171">"ಪ್ರಮಾಣಪತ್ರವನ್ನು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
     <string name="allow_button" msgid="3030990695030371561">"ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ನಿರಾಕರಿಸಿ"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 27de709..ac91bef 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -20,9 +20,9 @@
     <string name="title_no_certs" msgid="8350009443064722873">"कोणतीही प्रमाणपत्रे आढळली नाहीत"</string>
     <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_button_label" msgid="903474285774077171">"प्रमाणपत्र स्थापित करा"</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_button_label" msgid="903474285774077171">"प्रमाणपत्र इंस्टॉल करा"</string>
     <string name="allow_button" msgid="3030990695030371561">"निवडा"</string>
     <string name="deny_button" msgid="3766539809121892584">"नकार द्या"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index ca2309f..3b26efd 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -18,10 +18,10 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="170210454004696382">"ਕੁੰਜੀ ਚੇਨ"</string>
     <string name="title_no_certs" msgid="8350009443064722873">"ਕੋਈ ਸਰਟੀਫਿਕੇਟ ਨਹੀਂ ਮਿਲੇ"</string>
-    <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="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_button_label" msgid="903474285774077171">"ਪ੍ਰਮਾਣ-ਪੱਤਰ ਸਥਾਪਤ ਕਰੋ"</string>
     <string name="allow_button" msgid="3030990695030371561">"ਚੁਣੋ"</string>
     <string name="deny_button" msgid="3766539809121892584">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index aadaa04..02b9b51 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -19,8 +19,8 @@
     <string name="app_name" msgid="170210454004696382">"కీ చెయిన్"</string>
     <string name="title_no_certs" msgid="8350009443064722873">"ప్రమాణపత్రాలు కనుగొనబడలేదు"</string>
     <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="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_button_label" msgid="903474285774077171">"ప్రమాణపత్రాన్ని ఇన్‌స్టాల్ చేయి"</string>
     <string name="allow_button" msgid="3030990695030371561">"ఎంచుకోండి"</string>
diff --git a/robotests/Android.mk b/robotests/Android.mk
new file mode 100644
index 0000000..e0a4f86
--- /dev/null
+++ b/robotests/Android.mk
@@ -0,0 +1,45 @@
+#############################################
+#     KeyChain Robolectric test target.     #
+#############################################
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Include the testing libraries (JUnit4 + Robolectric libs).
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    mockito-robolectric-prebuilt \
+    platform-robolectric-android-all-stubs \
+    truth-prebuilt
+
+LOCAL_JAVA_LIBRARIES := \
+    junit \
+    platform-robolectric-3.4.2-prebuilt \
+    telephony-common
+
+LOCAL_INSTRUMENTATION_FOR := KeyChain
+LOCAL_MODULE := KeyChainRoboTests
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# Settings runner target to run the previous target. #
+#############################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunKeyChainRoboTests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    KeyChainRoboTests
+
+LOCAL_TEST_PACKAGE := KeyChain
+
+LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
+
+LOCAL_ROBOTEST_TIMEOUT := 36000
+
+include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk
diff --git a/robotests/src/com/android/keychain/AliasLoaderTest.java b/robotests/src/com/android/keychain/AliasLoaderTest.java
new file mode 100644
index 0000000..f2a05c8
--- /dev/null
+++ b/robotests/src/com/android/keychain/AliasLoaderTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.security.Credentials;
+import android.security.KeyStore;
+import com.android.keychain.internal.KeyInfoProvider;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class AliasLoaderTest {
+    private KeyInfoProvider mDummyInfoProvider;
+
+    @Before
+    public void setUp() {
+        mDummyInfoProvider =
+                new KeyInfoProvider() {
+                    public boolean isUserSelectable(String alias) {
+                        return true;
+                    }
+                };
+    }
+
+    @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"});
+
+        KeyChainActivity.AliasLoader loader =
+                new KeyChainActivity.AliasLoader(
+                        keyStore, RuntimeEnvironment.application, mDummyInfoProvider);
+        loader.execute();
+
+        ShadowApplication.runBackgroundTasks();
+        KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+        Assert.assertEquals(3, result.getCount());
+        Assert.assertEquals("a", result.getItem(0));
+        Assert.assertEquals("b", result.getItem(1));
+        Assert.assertEquals("c", result.getItem(2));
+    }
+
+    @Test
+    public void testAliasLoader_copesWithNoAliases()
+            throws InterruptedException, ExecutionException, CancellationException,
+                    TimeoutException {
+        KeyStore keyStore = mock(KeyStore.class);
+        when(keyStore.list(Credentials.USER_PRIVATE_KEY)).thenReturn(null);
+
+        KeyChainActivity.AliasLoader loader =
+                new KeyChainActivity.AliasLoader(
+                        keyStore, RuntimeEnvironment.application, mDummyInfoProvider);
+        loader.execute();
+
+        ShadowApplication.runBackgroundTasks();
+        KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+        Assert.assertEquals(0, result.getCount());
+    }
+
+    @Test
+    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);
+
+        KeyChainActivity.AliasLoader loader =
+                new KeyChainActivity.AliasLoader(
+                        keyStore, RuntimeEnvironment.application, infoProvider);
+        loader.execute();
+
+        ShadowApplication.runBackgroundTasks();
+        KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(result);
+        Assert.assertEquals(1, result.getCount());
+        Assert.assertEquals("b", result.getItem(0));
+    }
+}
diff --git a/robotests/src/com/android/keychain/TestConfig.java b/robotests/src/com/android/keychain/TestConfig.java
new file mode 100644
index 0000000..d31d2e5
--- /dev/null
+++ b/robotests/src/com/android/keychain/TestConfig.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 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;
+
+public class TestConfig {
+    public static final int SDK_VERSION = 26;
+    public static final String MANIFEST_PATH = "packages/apps/KeyChain/AndroidManifest.xml";
+}
diff --git a/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java b/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
new file mode 100644
index 0000000..ecb0889
--- /dev/null
+++ b/robotests/src/com/android/keychain/internal/GrantsDatabaseTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 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.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManager;
+import com.android.keychain.TestConfig;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+/** Unit tests for {@link com.android.keychain.internal.GrantsDatabase}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class GrantsDatabaseTest {
+    private static final String DUMMY_ALIAS = "dummy_alias";
+    private static final String DUMMY_ALIAS2 = "another_dummy_alias";
+    private static final int DUMMY_UID = 1000;
+    private static final int DUMMY_UID2 = 1001;
+
+    private GrantsDatabase mGrantsDB;
+
+    @Before
+    public void setUp() {
+        mGrantsDB = new GrantsDatabase(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void testSetGrant_notMixingUIDs() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testSetGrant_notMixingAliases() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS2));
+    }
+
+    @Test
+    public void testSetGrantTrue() {
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testSetGrantFalse() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, false);
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testSetGrantTrueThenFalse() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, false);
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testRemoveGrantsForAlias() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        mGrantsDB.setGrant(DUMMY_UID2, DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+        mGrantsDB.removeGrantsForAlias(DUMMY_ALIAS);
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testRemoveAllGrants() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        mGrantsDB.setGrant(DUMMY_UID2, DUMMY_ALIAS, true);
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS2, true);
+        mGrantsDB.removeAllGrants();
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS));
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS2));
+    }
+
+    @Test
+    public void testPurgeOldGrantsDoesNotDeleteGrantsForExistingPackages() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        PackageManager pm = mock(PackageManager.class);
+        when(pm.getPackagesForUid(DUMMY_UID)).thenReturn(new String[]{"p"});
+        mGrantsDB.purgeOldGrants(pm);
+        Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testPurgeOldGrantsPurgesAllNonExistingPackages() {
+        mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true);
+        mGrantsDB.setGrant(DUMMY_UID2, DUMMY_ALIAS, true);
+        PackageManager pm = mock(PackageManager.class);
+        when(pm.getPackagesForUid(DUMMY_UID)).thenReturn(null);
+        when(pm.getPackagesForUid(DUMMY_UID2)).thenReturn(null);
+        mGrantsDB.purgeOldGrants(pm);
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS));
+        Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testPurgeOldGrantsWorksOnEmptyDatabase() {
+        // Check that NPE is not thrown.
+        mGrantsDB.purgeOldGrants(null);
+    }
+
+    @Test
+    public void testIsUserSelectable() {
+        Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+    }
+
+    @Test
+    public void testSetUserSelectable() {
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, false);
+        Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+        mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true);
+        Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS));
+    }
+}
diff --git a/src/com/android/keychain/KeyChainActivity.java b/src/com/android/keychain/KeyChainActivity.java
index 3fd4a13..2eb7c89 100644
--- a/src/com/android/keychain/KeyChainActivity.java
+++ b/src/com/android/keychain/KeyChainActivity.java
@@ -46,6 +46,8 @@
 import android.widget.ListView;
 import android.widget.RadioButton;
 import android.widget.TextView;
+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;
@@ -57,6 +59,7 @@
 import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -144,7 +147,24 @@
     private void chooseCertificate() {
         // Start loading the set of certs to choose from now- if device policy doesn't return an
         // alias, having aliases loading already will save some time waiting for UI to start.
-        final AliasLoader loader = new AliasLoader();
+        KeyInfoProvider keyInfoProvider = new KeyInfoProvider() {
+            public boolean isUserSelectable(String alias) {
+                try (KeyChain.KeyChainConnection connection =
+                        KeyChain.bind(KeyChainActivity.this)) {
+                    return connection.getService().isUserSelectable(alias);
+                }
+                catch (InterruptedException ignored) {
+                    Log.e(TAG, "interrupted while checking if key is user-selectable", ignored);
+                    Thread.currentThread().interrupt();
+                    return false;
+                } catch (Exception ignored) {
+                    Log.e(TAG, "error while checking if key is user-selectable", ignored);
+                    return false;
+                }
+            }
+        };
+
+        final AliasLoader loader = new AliasLoader(mKeyStore, this, keyInfoProvider);
         loader.execute();
 
         final IKeyChainAliasCallback.Stub callback = new IKeyChainAliasCallback.Stub() {
@@ -192,14 +212,26 @@
         }
     }
 
-    private class AliasLoader extends AsyncTask<Void, Void, CertificateAdapter> {
+    @VisibleForTesting
+    static class AliasLoader extends AsyncTask<Void, Void, CertificateAdapter> {
+        private final KeyStore mKeyStore;
+        private final Context mContext;
+        private final KeyInfoProvider mInfoProvider;
+
+        public AliasLoader(KeyStore keyStore, Context context, KeyInfoProvider infoProvider) {
+          mKeyStore = keyStore;
+          mContext = context;
+          mInfoProvider = infoProvider;
+        }
+
         @Override protected CertificateAdapter doInBackground(Void... params) {
             String[] aliasArray = mKeyStore.list(Credentials.USER_PRIVATE_KEY);
-            List<String> aliasList = ((aliasArray == null)
+            List<String> rawAliasList = ((aliasArray == null)
                                       ? Collections.<String>emptyList()
                                       : Arrays.asList(aliasArray));
-            Collections.sort(aliasList);
-            return new CertificateAdapter(aliasList);
+            return new CertificateAdapter(mKeyStore, mContext,
+                    rawAliasList.stream().filter(mInfoProvider::isUserSelectable).sorted()
+                    .collect(Collectors.toList()));
         }
     }
 
@@ -323,12 +355,18 @@
         dialog.show();
     }
 
-    private class CertificateAdapter extends BaseAdapter {
+    @VisibleForTesting
+    static class CertificateAdapter extends BaseAdapter {
         private final List<String> mAliases;
         private final List<String> mSubjects = new ArrayList<String>();
-        private CertificateAdapter(List<String> aliases) {
+        private final KeyStore mKeyStore;
+        private final Context mContext;
+
+        private CertificateAdapter(KeyStore keyStore, Context context, List<String> aliases) {
             mAliases = aliases;
             mSubjects.addAll(Collections.nCopies(aliases.size(), (String) null));
+            mKeyStore = keyStore;
+            mContext = context;
         }
         @Override public int getCount() {
             return mAliases.size();
@@ -342,7 +380,7 @@
         @Override public View getView(final int adapterPosition, View view, ViewGroup parent) {
             ViewHolder holder;
             if (view == null) {
-                LayoutInflater inflater = LayoutInflater.from(KeyChainActivity.this);
+                LayoutInflater inflater = LayoutInflater.from(mContext);
                 view = inflater.inflate(R.layout.cert_item, parent, false);
                 holder = new ViewHolder();
                 holder.mAliasTextView = (TextView) view.findViewById(R.id.cert_item_alias);
@@ -457,6 +495,11 @@
                 if (mAlias != null) {
                     KeyChain.KeyChainConnection connection = KeyChain.bind(KeyChainActivity.this);
                     try {
+                        if (!connection.getService().isUserSelectable(mAlias)) {
+                            Log.w(TAG, String.format("Alias %s not user-selectable.", mAlias));
+                            //TODO: Should we invoke the callback with null here to indicate error?
+                            return null;
+                        }
                         connection.getService().setGrant(mSenderUid, mAlias, true);
                     } finally {
                         connection.close();
diff --git a/src/com/android/keychain/KeyChainService.java b/src/com/android/keychain/KeyChainService.java
index f45a1e6..09bdc6c 100644
--- a/src/com/android/keychain/KeyChainService.java
+++ b/src/com/android/keychain/KeyChainService.java
@@ -37,6 +37,7 @@
 import android.security.KeyChain;
 import android.security.KeyStore;
 import android.util.Log;
+import com.android.keychain.internal.GrantsDatabase;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.security.cert.CertificateException;
@@ -54,25 +55,8 @@
 
     private static final String TAG = "KeyChain";
 
-    private static final String DATABASE_NAME = "grants.db";
-    private static final int DATABASE_VERSION = 1;
-    private static final String TABLE_GRANTS = "grants";
-    private static final String GRANTS_ALIAS = "alias";
-    private static final String GRANTS_GRANTEE_UID = "uid";
-
     /** created in onCreate(), closed in onDestroy() */
-    public DatabaseHelper mDatabaseHelper;
-
-    private static final String SELECTION_COUNT_OF_MATCHING_GRANTS =
-            "SELECT COUNT(*) FROM " + TABLE_GRANTS
-                    + " WHERE " + GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
-
-    private static final String SELECT_GRANTS_BY_UID_AND_ALIAS =
-            GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
-
-    private static final String SELECTION_GRANTS_BY_UID = GRANTS_GRANTEE_UID + "=?";
-
-    private static final String SELECTION_GRANTS_BY_ALIAS = GRANTS_ALIAS + "=?";
+    public GrantsDatabase mGrantsDb;
 
     public KeyChainService() {
         super(KeyChainService.class.getSimpleName());
@@ -80,14 +64,14 @@
 
     @Override public void onCreate() {
         super.onCreate();
-        mDatabaseHelper = new DatabaseHelper(this);
+        mGrantsDb = new GrantsDatabase(this);
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mDatabaseHelper.close();
-        mDatabaseHelper = null;
+        mGrantsDb.destroy();
+        mGrantsDb = null;
     }
 
     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
@@ -114,17 +98,36 @@
             return mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
         }
 
-        private void checkArgs(String alias) {
+        @Override public boolean isUserSelectable(String alias) {
+            validateAlias(alias);
+            return mGrantsDb.isUserSelectable(alias);
+        }
+
+        @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
+            validateAlias(alias);
+            checkSystemCaller();
+            mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
+        }
+
+        private void validateAlias(String alias) {
             if (alias == null) {
                 throw new NullPointerException("alias == null");
             }
+        }
+
+        private void validateKeyStoreState() {
             if (!mKeyStore.isUnlocked()) {
                 throw new IllegalStateException("keystore is "
                         + mKeyStore.state().toString());
             }
+        }
+
+        private void checkArgs(String alias) {
+            validateAlias(alias);
+            validateKeyStoreState();
 
             final int callingUid = getCallingUid();
-            if (!hasGrantInternal(mDatabaseHelper.getReadableDatabase(), callingUid, alias)) {
+            if (!mGrantsDb.hasGrant(callingUid, alias)) {
                 throw new IllegalStateException("uid " + callingUid
                         + " doesn't have permission to access the requested alias");
             }
@@ -203,7 +206,7 @@
             if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
                 return false;
             }
-            removeGrantsForAlias(alias);
+            mGrantsDb.removeGrantsForAlias(alias);
             broadcastKeychainChange();
             broadcastLegacyStorageChange();
             return true;
@@ -217,7 +220,7 @@
         @Override public boolean reset() {
             // only Settings should be able to reset
             checkSystemCaller();
-            removeAllGrants(mDatabaseHelper.getWritableDatabase());
+            mGrantsDb.removeAllGrants();
             boolean ok = true;
             synchronized (mTrustedCertificateStore) {
                 // delete user-installed CA certs
@@ -283,12 +286,12 @@
 
         @Override public boolean hasGrant(int uid, String alias) {
             checkSystemCaller();
-            return hasGrantInternal(mDatabaseHelper.getReadableDatabase(), uid, alias);
+            return mGrantsDb.hasGrant(uid, alias);
         }
 
         @Override public void setGrant(int uid, String alias, boolean value) {
             checkSystemCaller();
-            setGrantInternal(mDatabaseHelper.getWritableDatabase(), uid, alias, value);
+            mGrantsDb.setGrant(uid, alias, value);
             broadcastPermissionChange(uid, alias, value);
             broadcastLegacyStorageChange();
         }
@@ -359,60 +362,6 @@
         }
     };
 
-    private boolean hasGrantInternal(final SQLiteDatabase db, final int uid, final String alias) {
-        final long numMatches = DatabaseUtils.longForQuery(db, SELECTION_COUNT_OF_MATCHING_GRANTS,
-                new String[]{String.valueOf(uid), alias});
-        return numMatches > 0;
-    }
-
-    private void setGrantInternal(final SQLiteDatabase db,
-            final int uid, final String alias, final boolean value) {
-        if (value) {
-            if (!hasGrantInternal(db, uid, alias)) {
-                final ContentValues values = new ContentValues();
-                values.put(GRANTS_ALIAS, alias);
-                values.put(GRANTS_GRANTEE_UID, uid);
-                db.insert(TABLE_GRANTS, GRANTS_ALIAS, values);
-            }
-        } else {
-            db.delete(TABLE_GRANTS, SELECT_GRANTS_BY_UID_AND_ALIAS,
-                    new String[]{String.valueOf(uid), alias});
-        }
-    }
-
-    private void removeGrantsForAlias(String alias) {
-        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
-        db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_ALIAS, new String[] {alias});
-    }
-
-    private void removeAllGrants(final SQLiteDatabase db) {
-        db.delete(TABLE_GRANTS, null /* whereClause */, null /* whereArgs */);
-    }
-
-    private class DatabaseHelper extends SQLiteOpenHelper {
-        public DatabaseHelper(Context context) {
-            super(context, DATABASE_NAME, null /* CursorFactory */, DATABASE_VERSION);
-        }
-
-        @Override
-        public void onCreate(final SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
-                    + GRANTS_ALIAS + " STRING NOT NULL,  "
-                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
-                    + "UNIQUE (" + GRANTS_ALIAS + "," + GRANTS_GRANTEE_UID + "))");
-        }
-
-        @Override
-        public void onUpgrade(final SQLiteDatabase db, int oldVersion, final int newVersion) {
-            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
-
-            if (oldVersion == 1) {
-                // the first upgrade step goes here
-                oldVersion++;
-            }
-        }
-    }
-
     @Override public IBinder onBind(Intent intent) {
         if (IKeyChainService.class.getName().equals(intent.getAction())) {
             return mIKeyChainService;
@@ -423,35 +372,7 @@
     @Override
     protected void onHandleIntent(final Intent intent) {
         if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-            purgeOldGrants();
-        }
-    }
-
-    private void purgeOldGrants() {
-        final PackageManager packageManager = getPackageManager();
-        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
-        Cursor cursor = null;
-        db.beginTransaction();
-        try {
-            cursor = db.query(TABLE_GRANTS,
-                    new String[]{GRANTS_GRANTEE_UID}, null, null, GRANTS_GRANTEE_UID, null, null);
-            while (cursor.moveToNext()) {
-                final int uid = cursor.getInt(0);
-                final boolean packageExists = packageManager.getPackagesForUid(uid) != null;
-                if (packageExists) {
-                    continue;
-                }
-                Log.d(TAG, "deleting grants for UID " + uid
-                        + " because its package is no longer installed");
-                db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_UID,
-                        new String[]{Integer.toString(uid)});
-            }
-            db.setTransactionSuccessful();
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-            db.endTransaction();
+            mGrantsDb.purgeOldGrants(getPackageManager());
         }
     }
 
diff --git a/src/com/android/keychain/internal/GrantsDatabase.java b/src/com/android/keychain/internal/GrantsDatabase.java
new file mode 100644
index 0000000..28605b5
--- /dev/null
+++ b/src/com/android/keychain/internal/GrantsDatabase.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 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.internal;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+public class GrantsDatabase {
+    private static final String TAG = "KeyChain";
+
+    private static final String DATABASE_NAME = "grants.db";
+    private static final int DATABASE_VERSION = 1;
+    private static final String TABLE_GRANTS = "grants";
+    private static final String GRANTS_ALIAS = "alias";
+    private static final String GRANTS_GRANTEE_UID = "uid";
+
+    private static final String SELECTION_COUNT_OF_MATCHING_GRANTS =
+            "SELECT COUNT(*) FROM "
+                    + TABLE_GRANTS
+                    + " WHERE "
+                    + GRANTS_GRANTEE_UID
+                    + "=? AND "
+                    + GRANTS_ALIAS
+                    + "=?";
+
+    private static final String SELECT_GRANTS_BY_UID_AND_ALIAS =
+            GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
+
+    private static final String SELECTION_GRANTS_BY_UID = GRANTS_GRANTEE_UID + "=?";
+
+    private static final String SELECTION_GRANTS_BY_ALIAS = GRANTS_ALIAS + "=?";
+
+    private static final String TABLE_SELECTABLE = "userselectable";
+    private static final String SELECTABLE_IS_SELECTABLE = "is_selectable";
+    private static final String COUNT_SELECTABILITY_FOR_ALIAS =
+            "SELECT COUNT(*) FROM " + TABLE_SELECTABLE + " WHERE " + GRANTS_ALIAS + "=?";
+
+    public DatabaseHelper mDatabaseHelper;
+
+    private class DatabaseHelper extends SQLiteOpenHelper {
+        public DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null /* CursorFactory */, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(final SQLiteDatabase db) {
+            db.execSQL(
+                    "CREATE TABLE "
+                            + TABLE_GRANTS
+                            + " (  "
+                            + GRANTS_ALIAS
+                            + " STRING NOT NULL,  "
+                            + GRANTS_GRANTEE_UID
+                            + " INTEGER NOT NULL,  "
+                            + "UNIQUE ("
+                            + GRANTS_ALIAS
+                            + ","
+                            + GRANTS_GRANTEE_UID
+                            + "))");
+
+            db.execSQL(
+                    "CREATE TABLE "
+                            + TABLE_SELECTABLE
+                            + " (  "
+                            + GRANTS_ALIAS
+                            + " STRING NOT NULL,  "
+                            + SELECTABLE_IS_SELECTABLE
+                            + " STRING NOT NULL,  "
+                            + "UNIQUE ("
+                            + GRANTS_ALIAS
+                            + "))");
+        }
+
+        @Override
+        public void onUpgrade(final SQLiteDatabase db, int oldVersion, final int newVersion) {
+            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
+
+            if (oldVersion == 1) {
+                // the first upgrade step goes here
+                oldVersion++;
+            }
+        }
+    }
+
+    public GrantsDatabase(Context context) {
+        mDatabaseHelper = new DatabaseHelper(context);
+    }
+
+    public void destroy() {
+        mDatabaseHelper.close();
+        mDatabaseHelper = null;
+    }
+
+    boolean hasGrantInternal(final SQLiteDatabase db, final int uid, final String alias) {
+        final long numMatches =
+                DatabaseUtils.longForQuery(
+                        db,
+                        SELECTION_COUNT_OF_MATCHING_GRANTS,
+                        new String[] {String.valueOf(uid), alias});
+        return numMatches > 0;
+    }
+
+    public boolean hasGrant(final int uid, final String alias) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        return hasGrantInternal(db, uid, alias);
+    }
+
+    public void setGrant(final int uid, final String alias, final boolean value) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        if (value) {
+            if (!hasGrantInternal(db, uid, alias)) {
+                final ContentValues values = new ContentValues();
+                values.put(GRANTS_ALIAS, alias);
+                values.put(GRANTS_GRANTEE_UID, uid);
+                db.insert(TABLE_GRANTS, GRANTS_ALIAS, values);
+            }
+        } else {
+            db.delete(
+                    TABLE_GRANTS,
+                    SELECT_GRANTS_BY_UID_AND_ALIAS,
+                    new String[] {String.valueOf(uid), alias});
+        }
+    }
+
+    public void removeGrantsForAlias(String alias) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_ALIAS, new String[] {alias});
+    }
+
+    public void removeAllGrants() {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        db.delete(TABLE_GRANTS, null /* whereClause */, null /* whereArgs */);
+    }
+
+    public void purgeOldGrants(PackageManager pm) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        db.beginTransaction();
+        try (Cursor cursor = db.query(
+                TABLE_GRANTS,
+                new String[] {GRANTS_GRANTEE_UID}, null, null, GRANTS_GRANTEE_UID, null, null)) {
+            while ((cursor != null) && (cursor.moveToNext())) {
+                final int uid = cursor.getInt(0);
+                final boolean packageExists = pm.getPackagesForUid(uid) != null;
+                if (packageExists) {
+                    continue;
+                }
+                Log.d(TAG, String.format(
+                        "deleting grants for UID %d because its package is no longer installed",
+                        uid));
+                db.delete(
+                        TABLE_GRANTS,
+                        SELECTION_GRANTS_BY_UID,
+                        new String[] {Integer.toString(uid)});
+            }
+            db.setTransactionSuccessful();
+        }
+
+        db.endTransaction();
+    }
+
+    public void setIsUserSelectable(final String alias, final boolean userSelectable) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(GRANTS_ALIAS, alias);
+        values.put(SELECTABLE_IS_SELECTABLE, Boolean.toString(userSelectable));
+
+        db.replace(TABLE_SELECTABLE, null, values);
+    }
+
+    public boolean isUserSelectable(final String alias) {
+        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+        try (Cursor res =
+                db.query(
+                        TABLE_SELECTABLE,
+                        new String[] {SELECTABLE_IS_SELECTABLE},
+                        SELECTION_GRANTS_BY_ALIAS,
+                        new String[] {alias},
+                        null /* group by */,
+                        null /* having */,
+                        null /* order by */)) {
+            if (res == null || !res.moveToNext()) {
+                return false;
+            }
+
+            boolean isSelectable = Boolean.parseBoolean(res.getString(0));
+            if (!res.isAfterLast()) {
+                // BUG! Should not have more than one result for any given alias.
+                Log.w(TAG, String.format("Have more than one result for alias %s", alias));
+            }
+            return isSelectable;
+        }
+    }
+}
diff --git a/src/com/android/keychain/internal/KeyInfoProvider.java b/src/com/android/keychain/internal/KeyInfoProvider.java
new file mode 100644
index 0000000..c5d1e30
--- /dev/null
+++ b/src/com/android/keychain/internal/KeyInfoProvider.java
@@ -0,0 +1,14 @@
+package com.android.keychain.internal;
+
+/** Interface for classes that provide information about keys in KeyChain.  */
+
+public interface KeyInfoProvider {
+    /**
+     * Indicates whether a key associated with the given alias is allowed
+     * to be selected by users.
+     * @param alias Alias of the key to check.
+     *
+     * @return true if the provided alias is selectable by users.
+     */
+    public boolean isUserSelectable(String alias);
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index dc60913..fa9cd55 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -20,8 +20,19 @@
     <uses-permission android:name="android.permission.INTERNET"/>
 
     <!--
+        Install the activity and disable battery optimization (so the KeyChainServiceTest can be
+        run in the background):
+        adb install out/target/product/${TARGET_PRODUCT}/data/app/KeyChainTests/KeyChainTests.apk
+        Then navigate to Settings -> Battery -> ... -> Battery optimization -> select All Apps
+        Find com.android.keychain.tests and select Do Not Optimize.
+
+        Alternatively, the following command can be used to exclude the test services from
+        the background execution restriction for 2 minutes:
+        adb shell cmd deviceidle tempwhitelist -d 120000 com.android.keychain.tests
+
         To run service:
         adb shell am startservice -n com.android.keychain.tests/.KeyChainServiceTest
+        One has to inspect the ADB log to find out about test failures.
 
         To run activity:
         adb shell am start -n com.android.keychain.tests/com.android.keychain.tests.KeyChainTestActivity
diff --git a/tests/src/com/android/keychain/tests/KeyChainServiceTest.java b/tests/src/com/android/keychain/tests/KeyChainServiceTest.java
index 1f3f7de..7e4008a 100644
--- a/tests/src/com/android/keychain/tests/KeyChainServiceTest.java
+++ b/tests/src/com/android/keychain/tests/KeyChainServiceTest.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.IBinder;
 import android.security.Credentials;
 import android.security.IKeyChainService;
@@ -75,16 +77,36 @@
         }
     };
 
+    private static void addComponentToIntent(PackageManager pm, Intent intent) {
+        ResolveInfo service = pm.resolveService(intent, 0);
+        if (service == null) {
+            Log.w(TAG, String.format("No service found for intent: %s", intent.getAction()));
+        } else {
+            Log.d(TAG, String.format("Found service: %s %s for action %s",
+                        service.serviceInfo.packageName, service.serviceInfo.name,
+                        intent.getAction()));
+            ComponentName comp = new ComponentName(
+                    service.serviceInfo.packageName, service.serviceInfo.name);
+            intent.setComponent(comp);
+        }
+    }
+
     private void bindSupport() {
-        mIsBoundSupport = bindService(new Intent(IKeyChainServiceTestSupport.class.getName()),
+        Intent serviceIntent = new Intent(IKeyChainServiceTestSupport.class.getName());
+        addComponentToIntent(getPackageManager(), serviceIntent);
+        mIsBoundSupport = bindService(serviceIntent,
                                       mSupportConnection,
                                       Context.BIND_AUTO_CREATE);
+        Log.d(TAG, String.format("Finished bindSupport with result: %b", mIsBoundSupport));
     }
 
     private void bindService() {
-        mIsBoundService = bindService(new Intent(IKeyChainService.class.getName()),
+        Intent serviceIntent = new Intent(IKeyChainService.class.getName());
+        addComponentToIntent(getPackageManager(), serviceIntent);
+        mIsBoundService = bindService(serviceIntent,
                                       mServiceConnection,
                                       Context.BIND_AUTO_CREATE);
+        Log.d(TAG, String.format("Finished bindService with result: %b", mIsBoundService));
     }
 
     private void unbindServices() {