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