• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 
17 package com.android.keychain;
18 
19 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED;
20 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED;
21 
22 import android.app.BroadcastOptions;
23 import android.app.IntentService;
24 import android.app.admin.SecurityLog;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.content.pm.StringParceledListSlice;
29 import android.os.Binder;
30 import android.os.Build;
31 import android.os.IBinder;
32 import android.os.Process;
33 import android.os.UserHandle;
34 import android.security.Credentials;
35 import android.security.IKeyChainService;
36 import android.security.KeyChain;
37 import android.security.KeyStore;
38 import android.security.keymaster.KeymasterArguments;
39 import android.security.keymaster.KeymasterCertificateChain;
40 import android.security.keystore.AttestationUtils;
41 import android.security.keystore.DeviceIdAttestationException;
42 import android.security.keystore.KeyGenParameterSpec;
43 import android.security.keystore.ParcelableKeyGenParameterSpec;
44 import android.security.keystore.StrongBoxUnavailableException;
45 import android.text.TextUtils;
46 import android.util.Log;
47 
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.internal.widget.LockPatternUtils;
50 import com.android.keychain.internal.ExistingKeysProvider;
51 import com.android.keychain.internal.GrantsDatabase;
52 import com.android.org.conscrypt.TrustedCertificateStore;
53 
54 import java.io.ByteArrayInputStream;
55 import java.io.IOException;
56 import java.security.InvalidAlgorithmParameterException;
57 import java.security.KeyPair;
58 import java.security.KeyPairGenerator;
59 import java.security.NoSuchAlgorithmException;
60 import java.security.NoSuchProviderException;
61 import java.security.cert.Certificate;
62 import java.security.cert.CertificateEncodingException;
63 import java.security.cert.CertificateException;
64 import java.security.cert.CertificateFactory;
65 import java.security.cert.X509Certificate;
66 import java.util.ArrayList;
67 import java.util.Collections;
68 import java.util.List;
69 
70 import javax.security.auth.x500.X500Principal;
71 
72 public class KeyChainService extends IntentService {
73 
74     private static final String TAG = "KeyChain";
75     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
76 
77     /** created in onCreate(), closed in onDestroy() */
78     private GrantsDatabase mGrantsDb;
79     private Injector mInjector;
80     private final KeyStore mKeyStore = KeyStore.getInstance();
81 
KeyChainService()82     public KeyChainService() {
83         super(KeyChainService.class.getSimpleName());
84         mInjector = new Injector();
85     }
86 
onCreate()87     @Override public void onCreate() {
88         super.onCreate();
89         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
90     }
91 
92     @Override
onDestroy()93     public void onDestroy() {
94         super.onDestroy();
95         mGrantsDb.destroy();
96         mGrantsDb = null;
97     }
98 
99     private static class KeyStoreAliasesProvider implements ExistingKeysProvider {
100         private final KeyStore mKeyStore;
101 
KeyStoreAliasesProvider(KeyStore keyStore)102         KeyStoreAliasesProvider(KeyStore keyStore) {
103             mKeyStore = keyStore;
104         }
105 
106         @Override
getExistingKeyAliases()107         public List<String> getExistingKeyAliases() {
108             List<String> aliases = new ArrayList<String>();
109             String[] keyStoreAliases = mKeyStore.list(Credentials.USER_PRIVATE_KEY);
110             if (keyStoreAliases == null) {
111                 return aliases;
112             }
113 
114             for (String alias: keyStoreAliases) {
115                 Log.w(TAG, "Got Alias from KeyStore: " + alias);
116                 String unPrefixedAlias = alias.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "");
117                 if (!unPrefixedAlias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
118                     aliases.add(unPrefixedAlias);
119                 }
120             }
121             return aliases;
122         }
123     }
124 
125     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
126         private final TrustedCertificateStore mTrustedCertificateStore
127                 = new TrustedCertificateStore();
128         private final Context mContext = KeyChainService.this;
129 
130         @Override
131         public String requestPrivateKey(String alias) {
132             if (!hasGrant(alias)) {
133                 return null;
134             }
135 
136             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
137             final int uid = mInjector.getCallingUid();
138             return mKeyStore.grant(keystoreAlias, uid);
139         }
140 
141         @Override public byte[] getCertificate(String alias) {
142             if (!hasGrant(alias)) {
143                 return null;
144             }
145             return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
146         }
147 
148         @Override public byte[] getCaCertificates(String alias) {
149             if (!hasGrant(alias)) {
150                 return null;
151             }
152             return mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
153         }
154 
155         @Override public boolean isUserSelectable(String alias) {
156             validateAlias(alias);
157             return mGrantsDb.isUserSelectable(alias);
158         }
159 
160         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
161             validateAlias(alias);
162             checkSystemCaller();
163             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
164         }
165 
166         @Override public int generateKeyPair(
167                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
168             checkSystemCaller();
169             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
170             final String alias = spec.getKeystoreAlias();
171             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
172             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
173             if (TextUtils.isEmpty(alias) || spec.getUid() != KeyStore.UID_SELF) {
174                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
175                 return KeyChain.KEY_GEN_MISSING_ALIAS;
176             }
177 
178             if (spec.getAttestationChallenge() != null) {
179                 Log.e(TAG, "Key generation request should not include an Attestation challenge.");
180                 return KeyChain.KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE;
181             }
182 
183             if (!removeKeyPair(alias)) {
184                 Log.e(TAG, "Failed to remove previously-installed alias " + alias);
185                 //TODO: Introduce a different error code in R to distinguish the failure to remove
186                 // old keys from other failures.
187                 return KeyChain.KEY_GEN_FAILURE;
188             }
189 
190             try {
191                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
192                         algorithm, "AndroidKeyStore");
193                 // Do not prepend USER_PRIVATE_KEY to the alias because
194                 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in
195                 // generateKeyPair.
196                 generator.initialize(spec);
197                 KeyPair kp = generator.generateKeyPair();
198                 if (kp == null) {
199                     Log.e(TAG, "Key generation failed.");
200                     return KeyChain.KEY_GEN_FAILURE;
201                 }
202                 return KeyChain.KEY_GEN_SUCCESS;
203             } catch (NoSuchAlgorithmException e) {
204                 Log.e(TAG, "Invalid algorithm requested", e);
205                 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM;
206             } catch (InvalidAlgorithmParameterException e) {
207                 Log.e(TAG, "Invalid algorithm params", e);
208                 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS;
209             } catch (NoSuchProviderException e) {
210                 Log.e(TAG, "Could not find Keystore.", e);
211                 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER;
212             } catch (StrongBoxUnavailableException e) {
213                 Log.e(TAG, "StrongBox unavailable.", e);
214                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
215             }
216         }
217 
218         @Override public int attestKey(
219                 String alias, byte[] attestationChallenge,
220                 int[] idAttestationFlags,
221                 KeymasterCertificateChain attestationChain) {
222             checkSystemCaller();
223             validateAlias(alias);
224 
225             if (attestationChallenge == null) {
226                 Log.e(TAG, String.format("Missing attestation challenge for alias %s", alias));
227                 return KeyChain.KEY_ATTESTATION_MISSING_CHALLENGE;
228             }
229 
230             KeymasterArguments attestArgs;
231             try {
232                 attestArgs = AttestationUtils.prepareAttestationArguments(
233                         mContext, idAttestationFlags, attestationChallenge);
234             } catch (DeviceIdAttestationException e) {
235                 Log.e(TAG, "Failed collecting attestation data", e);
236                 return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
237             }
238             int errorCode = checkKeyChainStatus(alias, attestationChain, attestArgs);
239             if (errorCode == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
240                 // b/69471841: id attestation might fail due to incorrect provisioning of device
241                 try {
242                     attestArgs =
243                             AttestationUtils.prepareAttestationArgumentsIfMisprovisioned(
244                             mContext, idAttestationFlags, attestationChallenge);
245                     if (attestArgs == null) {
246                         return errorCode;
247                     }
248                 } catch (DeviceIdAttestationException e) {
249                     Log.e(TAG, "Failed collecting attestation data "
250                             + "during second attempt on misprovisioned device", e);
251                     return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
252                 }
253             }
254 
255             return checkKeyChainStatus(alias, attestationChain, attestArgs);
256         }
257 
258         private int checkKeyChainStatus(
259                 String alias,
260                 KeymasterCertificateChain attestationChain,
261                 KeymasterArguments attestArgs) {
262 
263             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
264             final int errorCode = mKeyStore.attestKey(keystoreAlias, attestArgs, attestationChain);
265             if (errorCode != KeyStore.NO_ERROR) {
266                 Log.e(TAG, String.format("Failure attesting for key %s: %d", alias, errorCode));
267                 if (errorCode == KeyStore.CANNOT_ATTEST_IDS) {
268                     return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
269                 } else {
270                     // General failure, cannot discern which.
271                     return KeyChain.KEY_ATTESTATION_FAILURE;
272                 }
273             }
274 
275             return KeyChain.KEY_ATTESTATION_SUCCESS;
276         }
277 
278         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
279                 byte[] userCertificateChain) {
280             checkSystemCaller();
281             if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
282                         KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
283                 Log.e(TAG, "Failed to import user certificate " + userCertificate);
284                 return false;
285             }
286 
287             if (userCertificateChain != null && userCertificateChain.length > 0) {
288                 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain,
289                             KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
290                     Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
291                     if (!mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)) {
292                         Log.e(TAG, "Failed to clean up key chain after certificate chain"
293                                 + " importing failed");
294                     }
295                     return false;
296                 }
297             } else {
298                 if (!mKeyStore.delete(Credentials.CA_CERTIFICATE + alias)) {
299                     Log.e(TAG, "Failed to remove CA certificate chain for alias " + alias);
300                 }
301             }
302             broadcastKeychainChange();
303             broadcastLegacyStorageChange();
304             return true;
305         }
306 
307         private void validateAlias(String alias) {
308             if (alias == null) {
309                 throw new NullPointerException("alias == null");
310             }
311         }
312 
313         private boolean hasGrant(String alias) {
314             validateAlias(alias);
315 
316             final int callingUid = mInjector.getCallingUid();
317             if (!mGrantsDb.hasGrant(callingUid, alias)) {
318                 Log.w(TAG, String.format(
319                         "uid %d doesn't have permission to access the requested alias %s",
320                         callingUid, alias));
321                 return false;
322             }
323 
324             return true;
325         }
326 
327         @Override public String installCaCertificate(byte[] caCertificate) {
328             checkCertInstallerOrSystemCaller();
329             final String alias;
330             String subjectForAudit = null;
331             try {
332                 final X509Certificate cert = parseCertificate(caCertificate);
333                 if (mInjector.isSecurityLoggingEnabled()) {
334                     subjectForAudit =
335                             cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
336                 }
337                 synchronized (mTrustedCertificateStore) {
338                     mTrustedCertificateStore.installCertificate(cert);
339                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
340                 }
341             } catch (IOException | CertificateException e) {
342                 if (subjectForAudit != null) {
343                     mInjector.writeSecurityEvent(
344                             TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subjectForAudit);
345                 }
346                 throw new IllegalStateException(e);
347             }
348             if (subjectForAudit != null) {
349                 mInjector.writeSecurityEvent(
350                         TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subjectForAudit);
351             }
352             broadcastLegacyStorageChange();
353             broadcastTrustStoreChange();
354             return alias;
355         }
356 
357         /**
358          * Install a key pair to the keystore.
359          *
360          * @param privateKey The private key associated with the client certificate
361          * @param userCertificate The client certificate to be installed
362          * @param userCertificateChain The rest of the chain for the client certificate
363          * @param alias The alias under which the key pair is installed
364          * @return Whether the operation succeeded or not.
365          */
366         @Override public boolean installKeyPair(byte[] privateKey, byte[] userCertificate,
367                 byte[] userCertificateChain, String alias) {
368             checkCertInstallerOrSystemCaller();
369             if (!removeKeyPair(alias)) {
370                 return false;
371             }
372             if (!mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, privateKey, -1,
373                     KeyStore.FLAG_NONE)) {
374                 Log.e(TAG, "Failed to import private key " + alias);
375                 return false;
376             }
377             if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate, -1,
378                     KeyStore.FLAG_NONE)) {
379                 Log.e(TAG, "Failed to import user certificate " + userCertificate);
380                 if (!mKeyStore.delete(Credentials.USER_PRIVATE_KEY + alias)) {
381                     Log.e(TAG, "Failed to delete private key after certificate importing failed");
382                 }
383                 return false;
384             }
385             if (userCertificateChain != null && userCertificateChain.length > 0) {
386                 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain, -1,
387                         KeyStore.FLAG_NONE)) {
388                     Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
389                     if (!removeKeyPair(alias)) {
390                         Log.e(TAG, "Failed to clean up key chain after certificate chain"
391                                 + " importing failed");
392                     }
393                     return false;
394                 }
395             }
396             broadcastKeychainChange();
397             broadcastLegacyStorageChange();
398             return true;
399         }
400 
401         @Override public boolean removeKeyPair(String alias) {
402             checkCertInstallerOrSystemCaller();
403             if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
404                 return false;
405             }
406             Log.w(TAG, String.format(
407                     "WARNING: Removing alias %s, existing grants will be revoked.", alias));
408             mGrantsDb.removeAliasInformation(alias);
409             broadcastKeychainChange();
410             broadcastLegacyStorageChange();
411             return true;
412         }
413 
414         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
415             CertificateFactory cf = CertificateFactory.getInstance("X.509");
416             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
417         }
418 
419         @Override public boolean reset() {
420             // only Settings should be able to reset
421             checkSystemCaller();
422             mGrantsDb.removeAllAliasesInformation();
423             boolean ok = true;
424             synchronized (mTrustedCertificateStore) {
425                 // delete user-installed CA certs
426                 for (String alias : mTrustedCertificateStore.aliases()) {
427                     if (TrustedCertificateStore.isUser(alias)) {
428                         if (!deleteCertificateEntry(alias)) {
429                             ok = false;
430                         }
431                     }
432                 }
433             }
434             broadcastTrustStoreChange();
435             broadcastKeychainChange();
436             broadcastLegacyStorageChange();
437             return ok;
438         }
439 
440         @Override public boolean deleteCaCertificate(String alias) {
441             // only Settings should be able to delete
442             checkSystemCaller();
443             boolean ok = true;
444             synchronized (mTrustedCertificateStore) {
445                 ok = deleteCertificateEntry(alias);
446             }
447             broadcastTrustStoreChange();
448             broadcastLegacyStorageChange();
449             return ok;
450         }
451 
452         private boolean deleteCertificateEntry(String alias) {
453             String subjectForAudit = null;
454             if (mInjector.isSecurityLoggingEnabled()) {
455                 final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
456                 if (cert instanceof X509Certificate) {
457                     subjectForAudit = ((X509Certificate) cert)
458                             .getSubjectX500Principal().getName(X500Principal.CANONICAL);
459                 }
460             }
461 
462             try {
463                 mTrustedCertificateStore.deleteCertificateEntry(alias);
464                 if (subjectForAudit != null) {
465                     mInjector.writeSecurityEvent(
466                             TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit);
467                 }
468                 return true;
469             } catch (IOException | CertificateException e) {
470                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
471                 if (subjectForAudit != null) {
472                     mInjector.writeSecurityEvent(
473                             TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit);
474                 }
475                 return false;
476             }
477         }
478 
479         private void checkCertInstallerOrSystemCaller() {
480             final String caller = callingPackage();
481             if (!isCallerWithSystemUid() && !CERT_INSTALLER_PACKAGE.equals(caller)) {
482                 throw new SecurityException("Not system or cert installer package: " + caller);
483             }
484         }
485 
486         private void checkSystemCaller() {
487             if (!isCallerWithSystemUid()) {
488                 throw new SecurityException("Not system package: " + callingPackage());
489             }
490         }
491 
492         private boolean isCallerWithSystemUid() {
493             return UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID);
494         }
495 
496         private String callingPackage() {
497             return getPackageManager().getNameForUid(mInjector.getCallingUid());
498         }
499 
500         @Override public boolean hasGrant(int uid, String alias) {
501             checkSystemCaller();
502             return mGrantsDb.hasGrant(uid, alias);
503         }
504 
505         @Override public void setGrant(int uid, String alias, boolean value) {
506             checkSystemCaller();
507             mGrantsDb.setGrant(uid, alias, value);
508             broadcastPermissionChange(uid, alias, value);
509             broadcastLegacyStorageChange();
510         }
511 
512         @Override
513         public StringParceledListSlice getUserCaAliases() {
514             synchronized (mTrustedCertificateStore) {
515                 return new StringParceledListSlice(new ArrayList<String>(
516                         mTrustedCertificateStore.userAliases()));
517             }
518         }
519 
520         @Override
521         public StringParceledListSlice getSystemCaAliases() {
522             synchronized (mTrustedCertificateStore) {
523                 return new StringParceledListSlice(new ArrayList<String>(
524                         mTrustedCertificateStore.allSystemAliases()));
525             }
526         }
527 
528         @Override
529         public boolean containsCaAlias(String alias) {
530             return mTrustedCertificateStore.containsAlias(alias);
531         }
532 
533         @Override
534         public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
535             synchronized (mTrustedCertificateStore) {
536                 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
537                         .getCertificate(alias, includeDeletedSystem);
538                 if (certificate == null) {
539                     Log.w(TAG, "Could not find CA certificate " + alias);
540                     return null;
541                 }
542                 try {
543                     return certificate.getEncoded();
544                 } catch (CertificateEncodingException e) {
545                     Log.w(TAG, "Error while encoding CA certificate " + alias);
546                     return null;
547                 }
548             }
549         }
550 
551         @Override
552         public List<String> getCaCertificateChainAliases(String rootAlias,
553                 boolean includeDeletedSystem) {
554             synchronized (mTrustedCertificateStore) {
555                 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
556                         rootAlias, includeDeletedSystem);
557                 try {
558                     List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
559                             root);
560                     List<String> aliases = new ArrayList<String>(chain.size());
561                     final int n = chain.size();
562                     for (int i = 0; i < n; ++i) {
563                         String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
564                                 true);
565                         if (alias != null) {
566                             aliases.add(alias);
567                         }
568                     }
569                     return aliases;
570                 } catch (CertificateException e) {
571                     Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
572                     return Collections.emptyList();
573                 }
574             }
575         }
576     };
577 
onBind(Intent intent)578     @Override public IBinder onBind(Intent intent) {
579         if (IKeyChainService.class.getName().equals(intent.getAction())) {
580             return mIKeyChainService;
581         }
582         return null;
583     }
584 
585     @Override
onHandleIntent(final Intent intent)586     protected void onHandleIntent(final Intent intent) {
587         if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
588             mGrantsDb.purgeOldGrants(getPackageManager());
589         }
590     }
591 
broadcastLegacyStorageChange()592     private void broadcastLegacyStorageChange() {
593         Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
594         BroadcastOptions opts = BroadcastOptions.makeBasic();
595         opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.N_MR1);
596         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()), null, opts.toBundle());
597     }
598 
broadcastKeychainChange()599     private void broadcastKeychainChange() {
600         Intent intent = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
601         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
602     }
603 
broadcastTrustStoreChange()604     private void broadcastTrustStoreChange() {
605         Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
606         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
607     }
608 
broadcastPermissionChange(int uid, String alias, boolean access)609     private void broadcastPermissionChange(int uid, String alias, boolean access) {
610         // Since the permission change only impacts one uid only send to that uid's packages.
611         final PackageManager packageManager = getPackageManager();
612         String[] packages = packageManager.getPackagesForUid(uid);
613         if (packages == null) {
614             return;
615         }
616         for (String pckg : packages) {
617             Intent intent = new Intent(KeyChain.ACTION_KEY_ACCESS_CHANGED);
618             intent.putExtra(KeyChain.EXTRA_KEY_ALIAS, alias);
619             intent.putExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, access);
620             intent.setPackage(pckg);
621             sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
622         }
623     }
624 
625     @VisibleForTesting
setInjector(Injector injector)626     void setInjector(Injector injector) {
627         mInjector = injector;
628     }
629 
630     /**
631      * Injector for mocking out dependencies in tests.
632      */
633     @VisibleForTesting
634     static class Injector {
isSecurityLoggingEnabled()635         public boolean isSecurityLoggingEnabled() {
636             return SecurityLog.isLoggingEnabled();
637         }
638 
writeSecurityEvent(int tag, Object... payload)639         public void writeSecurityEvent(int tag, Object... payload) {
640             SecurityLog.writeEvent(tag, payload);
641         }
642 
getCallingUid()643         public int getCallingUid() {
644             return Binder.getCallingUid();
645         }
646     }
647 }
648