1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.security;
17
18import android.annotation.NonNull;
19import android.annotation.Nullable;
20import android.annotation.SdkConstant;
21import android.annotation.WorkerThread;
22import android.annotation.SdkConstant.SdkConstantType;
23import android.app.Activity;
24import android.app.PendingIntent;
25import android.app.Service;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.ServiceConnection;
30import android.net.Uri;
31import android.os.Binder;
32import android.os.Build;
33import android.os.IBinder;
34import android.os.Looper;
35import android.os.Process;
36import android.os.RemoteException;
37import android.os.UserHandle;
38import android.security.keystore.AndroidKeyStoreProvider;
39import android.security.keystore.KeyProperties;
40
41import java.io.ByteArrayInputStream;
42import java.io.Closeable;
43import java.security.Principal;
44import java.security.PrivateKey;
45import java.security.UnrecoverableKeyException;
46import java.security.cert.Certificate;
47import java.security.cert.CertificateException;
48import java.security.cert.CertificateFactory;
49import java.security.cert.X509Certificate;
50import java.util.ArrayList;
51import java.util.Collection;
52import java.util.List;
53import java.util.Locale;
54import java.util.concurrent.BlockingQueue;
55import java.util.concurrent.LinkedBlockingQueue;
56
57import com.android.org.conscrypt.TrustedCertificateStore;
58
59/**
60 * The {@code KeyChain} class provides access to private keys and
61 * their corresponding certificate chains in credential storage.
62 *
63 * <p>Applications accessing the {@code KeyChain} normally go through
64 * these steps:
65 *
66 * <ol>
67 *
68 * <li>Receive a callback from an {@link javax.net.ssl.X509KeyManager
69 * X509KeyManager} that a private key is requested.
70 *
71 * <li>Call {@link #choosePrivateKeyAlias
72 * choosePrivateKeyAlias} to allow the user to select from a
73 * list of currently available private keys and corresponding
74 * certificate chains. The chosen alias will be returned by the
75 * callback {@link KeyChainAliasCallback#alias}, or null if no private
76 * key is available or the user cancels the request.
77 *
78 * <li>Call {@link #getPrivateKey} and {@link #getCertificateChain} to
79 * retrieve the credentials to return to the corresponding {@link
80 * javax.net.ssl.X509KeyManager} callbacks.
81 *
82 * </ol>
83 *
84 * <p>An application may remember the value of a selected alias to
85 * avoid prompting the user with {@link #choosePrivateKeyAlias
86 * choosePrivateKeyAlias} on subsequent connections. If the alias is
87 * no longer valid, null will be returned on lookups using that value
88 *
89 * <p>An application can request the installation of private keys and
90 * certificates via the {@code Intent} provided by {@link
91 * #createInstallIntent}. Private keys installed via this {@code
92 * Intent} will be accessible via {@link #choosePrivateKeyAlias} while
93 * Certificate Authority (CA) certificates will be trusted by all
94 * applications through the default {@code X509TrustManager}.
95 */
96// TODO reference intent for credential installation when public
97public final class KeyChain {
98
99    /**
100     * @hide Also used by KeyChainService implementation
101     */
102    public static final String ACCOUNT_TYPE = "com.android.keychain";
103
104    /**
105     * Package name for KeyChain chooser.
106     */
107    private static final String KEYCHAIN_PACKAGE = "com.android.keychain";
108
109    /**
110     * Action to bring up the KeyChainActivity
111     */
112    private static final String ACTION_CHOOSER = "com.android.keychain.CHOOSER";
113
114    /**
115     * Package name for the Certificate Installer.
116     */
117    private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
118
119    /**
120     * Extra for use with {@link #ACTION_CHOOSER}
121     * @hide Also used by KeyChainActivity implementation
122     */
123    public static final String EXTRA_RESPONSE = "response";
124
125    /**
126     * Extra for use with {@link #ACTION_CHOOSER}
127     * @hide Also used by KeyChainActivity implementation
128     */
129    public static final String EXTRA_URI = "uri";
130
131    /**
132     * Extra for use with {@link #ACTION_CHOOSER}
133     * @hide Also used by KeyChainActivity implementation
134     */
135    public static final String EXTRA_ALIAS = "alias";
136
137    /**
138     * Extra for use with {@link #ACTION_CHOOSER}
139     * @hide Also used by KeyChainActivity implementation
140     */
141    public static final String EXTRA_SENDER = "sender";
142
143    /**
144     * Action to bring up the CertInstaller.
145     */
146    private static final String ACTION_INSTALL = "android.credentials.INSTALL";
147
148    /**
149     * Optional extra to specify a {@code String} credential name on
150     * the {@code Intent} returned by {@link #createInstallIntent}.
151     */
152    // Compatible with old com.android.certinstaller.CredentialHelper.CERT_NAME_KEY
153    public static final String EXTRA_NAME = "name";
154
155    /**
156     * Optional extra to specify an X.509 certificate to install on
157     * the {@code Intent} returned by {@link #createInstallIntent}.
158     * The extra value should be a PEM or ASN.1 DER encoded {@code
159     * byte[]}. An {@link X509Certificate} can be converted to DER
160     * encoded bytes with {@link X509Certificate#getEncoded}.
161     *
162     * <p>{@link #EXTRA_NAME} may be used to provide a default alias
163     * name for the installed certificate.
164     */
165    // Compatible with old android.security.Credentials.CERTIFICATE
166    public static final String EXTRA_CERTIFICATE = "CERT";
167
168    /**
169     * Optional extra for use with the {@code Intent} returned by
170     * {@link #createInstallIntent} to specify a PKCS#12 key store to
171     * install. The extra value should be a {@code byte[]}. The bytes
172     * may come from an external source or be generated with {@link
173     * java.security.KeyStore#store} on a "PKCS12" instance.
174     *
175     * <p>The user will be prompted for the password to load the key store.
176     *
177     * <p>The key store will be scanned for {@link
178     * java.security.KeyStore.PrivateKeyEntry} entries and both the
179     * private key and associated certificate chain will be installed.
180     *
181     * <p>{@link #EXTRA_NAME} may be used to provide a default alias
182     * name for the installed credentials.
183     */
184    // Compatible with old android.security.Credentials.PKCS12
185    public static final String EXTRA_PKCS12 = "PKCS12";
186
187    /**
188     * Broadcast Action: Indicates the trusted storage has changed. Sent when
189     * one of this happens:
190     *
191     * <ul>
192     * <li>a new CA is added,
193     * <li>an existing CA is removed or disabled,
194     * <li>a disabled CA is enabled,
195     * <li>trusted storage is reset (all user certs are cleared),
196     * <li>when permission to access a private key is changed.
197     * </ul>
198     *
199     * @deprecated Use {@link #ACTION_KEYCHAIN_CHANGED}, {@link #ACTION_TRUST_STORE_CHANGED} or
200     * {@link #ACTION_KEY_ACCESS_CHANGED}. Apps that target a version higher than
201     * {@link Build.VERSION_CODES#N_MR1} will only receive this broadcast if they register for it
202     * at runtime.
203     */
204    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
205    public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
206
207    /**
208     * Broadcast Action: Indicates the contents of the keychain has changed. Sent when a KeyChain
209     * entry is added, modified or removed.
210     */
211    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
212    public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED";
213
214    /**
215     * Broadcast Action: Indicates the contents of the trusted certificate store has changed. Sent
216     * when one the following occurs:
217     *
218     * <ul>
219     * <li>A pre-installed CA is disabled or re-enabled</li>
220     * <li>A CA is added or removed from the trust store</li>
221     * </ul>
222     */
223    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
224    public static final String ACTION_TRUST_STORE_CHANGED =
225            "android.security.action.TRUST_STORE_CHANGED";
226
227    /**
228     * Broadcast Action: Indicates that the access permissions for a private key have changed.
229     *
230     */
231    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
232    public static final String ACTION_KEY_ACCESS_CHANGED =
233            "android.security.action.KEY_ACCESS_CHANGED";
234
235    /**
236     * Used as a String extra field in {@link #ACTION_KEY_ACCESS_CHANGED} to supply the alias of
237     * the key.
238     */
239    public static final String EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS";
240
241    /**
242     * Used as a boolean extra field in {@link #ACTION_KEY_ACCESS_CHANGED} to supply if the key is
243     * accessible to the application.
244     */
245    public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
246
247    /**
248     * Returns an {@code Intent} that can be used for credential
249     * installation. The intent may be used without any extras, in
250     * which case the user will be able to install credentials from
251     * their own source.
252     *
253     * <p>Alternatively, {@link #EXTRA_CERTIFICATE} or {@link
254     * #EXTRA_PKCS12} maybe used to specify the bytes of an X.509
255     * certificate or a PKCS#12 key store for installation. These
256     * extras may be combined with {@link #EXTRA_NAME} to provide a
257     * default alias name for credentials being installed.
258     *
259     * <p>When used with {@link Activity#startActivityForResult},
260     * {@link Activity#RESULT_OK} will be returned if a credential was
261     * successfully installed, otherwise {@link
262     * Activity#RESULT_CANCELED} will be returned.
263     */
264    @NonNull
265    public static Intent createInstallIntent() {
266        Intent intent = new Intent(ACTION_INSTALL);
267        intent.setClassName(CERT_INSTALLER_PACKAGE,
268                            "com.android.certinstaller.CertInstallerMain");
269        return intent;
270    }
271
272    /**
273     * Launches an {@code Activity} for the user to select the alias
274     * for a private key and certificate pair for authentication. The
275     * selected alias or null will be returned via the
276     * KeyChainAliasCallback callback.
277     *
278     * <p>The device or profile owner can intercept this before the activity
279     * is shown, to pick a specific private key alias.
280     *
281     * <p>{@code keyTypes} and {@code issuers} may be used to
282     * highlight suggested choices to the user, although to cope with
283     * sometimes erroneous values provided by servers, the user may be
284     * able to override these suggestions.
285     *
286     * <p>{@code host} and {@code port} may be used to give the user
287     * more context about the server requesting the credentials.
288     *
289     * <p>{@code alias} allows the chooser to preselect an existing
290     * alias which will still be subject to user confirmation.
291     *
292     * @param activity The {@link Activity} context to use for
293     *     launching the new sub-Activity to prompt the user to select
294     *     a private key; used only to call startActivity(); must not
295     *     be null.
296     * @param response Callback to invoke when the request completes;
297     *     must not be null
298     * @param keyTypes The acceptable types of asymmetric keys such as
299     *     "RSA" or "DSA", or a null array.
300     * @param issuers The acceptable certificate issuers for the
301     *     certificate matching the private key, or null.
302     * @param host The host name of the server requesting the
303     *     certificate, or null if unavailable.
304     * @param port The port number of the server requesting the
305     *     certificate, or -1 if unavailable.
306     * @param alias The alias to preselect if available, or null if
307     *     unavailable.
308     */
309    public static void choosePrivateKeyAlias(@NonNull Activity activity,
310            @NonNull KeyChainAliasCallback response,
311            @KeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
312            @Nullable String host, int port, @Nullable String alias) {
313        Uri uri = null;
314        if (host != null) {
315            uri = new Uri.Builder()
316                    .authority(host + (port != -1 ? ":" + port : ""))
317                    .build();
318        }
319        choosePrivateKeyAlias(activity, response, keyTypes, issuers, uri, alias);
320    }
321
322    /**
323     * Launches an {@code Activity} for the user to select the alias
324     * for a private key and certificate pair for authentication. The
325     * selected alias or null will be returned via the
326     * KeyChainAliasCallback callback.
327     *
328     * <p>The device or profile owner can intercept this before the activity
329     * is shown, to pick a specific private key alias.</p>
330     *
331     * <p>{@code keyTypes} and {@code issuers} may be used to
332     * highlight suggested choices to the user, although to cope with
333     * sometimes erroneous values provided by servers, the user may be
334     * able to override these suggestions.
335     *
336     * <p>{@code host} and {@code port} may be used to give the user
337     * more context about the server requesting the credentials.
338     *
339     * <p>{@code alias} allows the chooser to preselect an existing
340     * alias which will still be subject to user confirmation.
341     *
342     * @param activity The {@link Activity} context to use for
343     *     launching the new sub-Activity to prompt the user to select
344     *     a private key; used only to call startActivity(); must not
345     *     be null.
346     * @param response Callback to invoke when the request completes;
347     *     must not be null
348     * @param keyTypes The acceptable types of asymmetric keys such as
349     *     "EC" or "RSA", or a null array.
350     * @param issuers The acceptable certificate issuers for the
351     *     certificate matching the private key, or null.
352     * @param uri The full URI the server is requesting the certificate
353     *     for, or null if unavailable.
354     * @param alias The alias to preselect if available, or null if
355     *     unavailable.
356     */
357    public static void choosePrivateKeyAlias(@NonNull Activity activity,
358            @NonNull KeyChainAliasCallback response,
359            @KeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
360            @Nullable Uri uri, @Nullable String alias) {
361        /*
362         * TODO currently keyTypes, issuers are unused. They are meant
363         * to follow the semantics and purpose of X509KeyManager
364         * method arguments.
365         *
366         * keyTypes would allow the list to be filtered and typically
367         * will be set correctly by the server. In practice today,
368         * most all users will want only RSA or EC, and usually
369         * only a small number of certs will be available.
370         *
371         * issuers is typically not useful. Some servers historically
372         * will send the entire list of public CAs known to the
373         * server. Others will send none. If this is used, if there
374         * are no matches after applying the constraint, it should be
375         * ignored.
376         */
377        if (activity == null) {
378            throw new NullPointerException("activity == null");
379        }
380        if (response == null) {
381            throw new NullPointerException("response == null");
382        }
383        Intent intent = new Intent(ACTION_CHOOSER);
384        intent.setPackage(KEYCHAIN_PACKAGE);
385        intent.putExtra(EXTRA_RESPONSE, new AliasResponse(response));
386        intent.putExtra(EXTRA_URI, uri);
387        intent.putExtra(EXTRA_ALIAS, alias);
388        // the PendingIntent is used to get calling package name
389        intent.putExtra(EXTRA_SENDER, PendingIntent.getActivity(activity, 0, new Intent(), 0));
390        activity.startActivity(intent);
391    }
392
393    private static class AliasResponse extends IKeyChainAliasCallback.Stub {
394        private final KeyChainAliasCallback keyChainAliasResponse;
395        private AliasResponse(KeyChainAliasCallback keyChainAliasResponse) {
396            this.keyChainAliasResponse = keyChainAliasResponse;
397        }
398        @Override public void alias(String alias) {
399            keyChainAliasResponse.alias(alias);
400        }
401    }
402
403    /**
404     * Returns the {@code PrivateKey} for the requested alias, or null
405     * if there is no result.
406     *
407     * <p> This method may block while waiting for a connection to another process, and must never
408     * be called from the main thread.
409     * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed
410     * at any time from the main thread, it is safer to rely on a long-lived context such as one
411     * returned from {@link Context#getApplicationContext()}.
412     *
413     * @param alias The alias of the desired private key, typically returned via
414     *              {@link KeyChainAliasCallback#alias}.
415     * @throws KeyChainException if the alias was valid but there was some problem accessing it.
416     * @throws IllegalStateException if called from the main thread.
417     */
418    @Nullable @WorkerThread
419    public static PrivateKey getPrivateKey(@NonNull Context context, @NonNull String alias)
420            throws KeyChainException, InterruptedException {
421        if (alias == null) {
422            throw new NullPointerException("alias == null");
423        }
424        if (context == null) {
425            throw new NullPointerException("context == null");
426        }
427
428        final String keyId;
429        try (KeyChainConnection keyChainConnection = bind(context.getApplicationContext())) {
430            keyId = keyChainConnection.getService().requestPrivateKey(alias);
431        } catch (RemoteException e) {
432            throw new KeyChainException(e);
433        } catch (RuntimeException e) {
434            // only certain RuntimeExceptions can be propagated across the IKeyChainService call
435            throw new KeyChainException(e);
436        }
437
438        if (keyId == null) {
439            return null;
440        } else {
441            try {
442                return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
443                        KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
444            } catch (RuntimeException | UnrecoverableKeyException e) {
445                throw new KeyChainException(e);
446            }
447        }
448    }
449
450    /**
451     * Returns the {@code X509Certificate} chain for the requested
452     * alias, or null if there is no result.
453     * <p>
454     * <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was
455     * installed, this method will return that chain. If only the client certificate was specified
456     * at the installation time, this method will try to build a certificate chain using all
457     * available trust anchors (preinstalled and user-added).
458     *
459     * <p> This method may block while waiting for a connection to another process, and must never
460     * be called from the main thread.
461     * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed
462     * at any time from the main thread, it is safer to rely on a long-lived context such as one
463     * returned from {@link Context#getApplicationContext()}.
464     *
465     * @param alias The alias of the desired certificate chain, typically
466     * returned via {@link KeyChainAliasCallback#alias}.
467     * @throws KeyChainException if the alias was valid but there was some problem accessing it.
468     * @throws IllegalStateException if called from the main thread.
469     */
470    @Nullable @WorkerThread
471    public static X509Certificate[] getCertificateChain(@NonNull Context context,
472            @NonNull String alias) throws KeyChainException, InterruptedException {
473        if (alias == null) {
474            throw new NullPointerException("alias == null");
475        }
476
477        final byte[] certificateBytes;
478        final byte[] certChainBytes;
479        try (KeyChainConnection keyChainConnection = bind(context.getApplicationContext())) {
480            IKeyChainService keyChainService = keyChainConnection.getService();
481            certificateBytes = keyChainService.getCertificate(alias);
482            if (certificateBytes == null) {
483                return null;
484            }
485            certChainBytes = keyChainService.getCaCertificates(alias);
486        } catch (RemoteException e) {
487            throw new KeyChainException(e);
488        } catch (RuntimeException e) {
489            // only certain RuntimeExceptions can be propagated across the IKeyChainService call
490            throw new KeyChainException(e);
491        }
492
493        try {
494            X509Certificate leafCert = toCertificate(certificateBytes);
495            // If the keypair is installed with a certificate chain by either
496            // DevicePolicyManager.installKeyPair or CertInstaller, return that chain.
497            if (certChainBytes != null && certChainBytes.length != 0) {
498                Collection<X509Certificate> chain = toCertificates(certChainBytes);
499                ArrayList<X509Certificate> fullChain = new ArrayList<>(chain.size() + 1);
500                fullChain.add(leafCert);
501                fullChain.addAll(chain);
502                return fullChain.toArray(new X509Certificate[fullChain.size()]);
503            } else {
504                // If there isn't a certificate chain, either due to a pre-existing keypair
505                // installed before N, or no chain is explicitly installed under the new logic,
506                // fall back to old behavior of constructing the chain from trusted credentials.
507                //
508                // This logic exists to maintain old behaviour for already installed keypair, at
509                // the cost of potentially returning extra certificate chain for new clients who
510                // explicitly installed only the client certificate without a chain. The latter
511                // case is actually no different from pre-N behaviour of getCertificateChain(),
512                // in that sense this change introduces no regression. Besides the returned chain
513                // is still valid so the consumer of the chain should have no problem verifying it.
514                TrustedCertificateStore store = new TrustedCertificateStore();
515                List<X509Certificate> chain = store.getCertificateChain(leafCert);
516                return chain.toArray(new X509Certificate[chain.size()]);
517            }
518        } catch (CertificateException | RuntimeException e) {
519            throw new KeyChainException(e);
520        }
521    }
522
523    /**
524     * Returns {@code true} if the current device's {@code KeyChain} supports a
525     * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
526     * "RSA").
527     */
528    public static boolean isKeyAlgorithmSupported(
529            @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
530        final String algUpper = algorithm.toUpperCase(Locale.US);
531        return KeyProperties.KEY_ALGORITHM_EC.equals(algUpper)
532                || KeyProperties.KEY_ALGORITHM_RSA.equals(algUpper);
533    }
534
535    /**
536     * Returns {@code true} if the current device's {@code KeyChain} binds any
537     * {@code PrivateKey} of the given {@code algorithm} to the device once
538     * imported or generated. This can be used to tell if there is special
539     * hardware support that can be used to bind keys to the device in a way
540     * that makes it non-exportable.
541     *
542     * @deprecated Whether the key is bound to the secure hardware is known only
543     * once the key has been imported. To find out, use:
544     * <pre>{@code
545     * PrivateKey key = ...; // private key from KeyChain
546     *
547     * KeyFactory keyFactory =
548     *     KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
549     * KeyInfo keyInfo = keyFactory.getKeySpec(key, KeyInfo.class);
550     * if (keyInfo.isInsideSecureHardware()) {
551     *     // The key is bound to the secure hardware of this Android
552     * }}</pre>
553     */
554    @Deprecated
555    public static boolean isBoundKeyAlgorithm(
556            @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
557        if (!isKeyAlgorithmSupported(algorithm)) {
558            return false;
559        }
560
561        return KeyStore.getInstance().isHardwareBacked(algorithm);
562    }
563
564    /** @hide */
565    @NonNull
566    public static X509Certificate toCertificate(@NonNull byte[] bytes) {
567        if (bytes == null) {
568            throw new IllegalArgumentException("bytes == null");
569        }
570        try {
571            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
572            Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
573            return (X509Certificate) cert;
574        } catch (CertificateException e) {
575            throw new AssertionError(e);
576        }
577    }
578
579    /** @hide */
580    @NonNull
581    public static Collection<X509Certificate> toCertificates(@NonNull byte[] bytes) {
582        if (bytes == null) {
583            throw new IllegalArgumentException("bytes == null");
584        }
585        try {
586            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
587            return (Collection<X509Certificate>) certFactory.generateCertificates(
588                    new ByteArrayInputStream(bytes));
589        } catch (CertificateException e) {
590            throw new AssertionError(e);
591        }
592    }
593
594    /**
595     * @hide for reuse by CertInstaller and Settings.
596     * @see KeyChain#bind
597     */
598    public static class KeyChainConnection implements Closeable {
599        private final Context context;
600        private final ServiceConnection serviceConnection;
601        private final IKeyChainService service;
602        protected KeyChainConnection(Context context,
603                                     ServiceConnection serviceConnection,
604                                     IKeyChainService service) {
605            this.context = context;
606            this.serviceConnection = serviceConnection;
607            this.service = service;
608        }
609        @Override public void close() {
610            context.unbindService(serviceConnection);
611        }
612        public IKeyChainService getService() {
613            return service;
614        }
615    }
616
617    /**
618     * @hide for reuse by CertInstaller and Settings.
619     *
620     * Caller should call unbindService on the result when finished.
621     */
622    @WorkerThread
623    public static KeyChainConnection bind(@NonNull Context context) throws InterruptedException {
624        return bindAsUser(context, Process.myUserHandle());
625    }
626
627    /**
628     * @hide
629     */
630    @WorkerThread
631    public static KeyChainConnection bindAsUser(@NonNull Context context, UserHandle user)
632            throws InterruptedException {
633        if (context == null) {
634            throw new NullPointerException("context == null");
635        }
636        ensureNotOnMainThread(context);
637        final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1);
638        ServiceConnection keyChainServiceConnection = new ServiceConnection() {
639            volatile boolean mConnectedAtLeastOnce = false;
640            @Override public void onServiceConnected(ComponentName name, IBinder service) {
641                if (!mConnectedAtLeastOnce) {
642                    mConnectedAtLeastOnce = true;
643                    try {
644                        q.put(IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
645                    } catch (InterruptedException e) {
646                        // will never happen, since the queue starts with one available slot
647                    }
648                }
649            }
650            @Override public void onServiceDisconnected(ComponentName name) {}
651        };
652        Intent intent = new Intent(IKeyChainService.class.getName());
653        ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
654        intent.setComponent(comp);
655        if (comp == null || !context.bindServiceAsUser(
656                intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) {
657            throw new AssertionError("could not bind to KeyChainService");
658        }
659        return new KeyChainConnection(context, keyChainServiceConnection, q.take());
660    }
661
662    private static void ensureNotOnMainThread(@NonNull Context context) {
663        Looper looper = Looper.myLooper();
664        if (looper != null && looper == context.getMainLooper()) {
665            throw new IllegalStateException(
666                    "calling this from your main thread can lead to deadlock");
667        }
668    }
669}
670