blob: 419da2ed102eb26660e3e956f98a1004dcac3504 [file] [log] [blame]
Brian Carlstrom3e6251d2011-04-11 09:05:06 -07001/*
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
17package com.android.keychain;
18
Chad Brubaker2099a7d2016-05-02 13:13:23 -070019import android.app.BroadcastOptions;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070020import android.app.IntentService;
21import android.content.ContentValues;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070022import android.content.Context;
23import android.content.Intent;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070024import android.content.pm.PackageManager;
Robin Lee7e9d0452017-02-20 20:57:41 +000025import android.content.pm.StringParceledListSlice;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070026import android.database.Cursor;
27import android.database.DatabaseUtils;
28import android.database.sqlite.SQLiteDatabase;
29import android.database.sqlite.SQLiteOpenHelper;
Kenny Root6f1f03b2012-03-08 10:30:39 -080030import android.os.Binder;
Chad Brubaker2099a7d2016-05-02 13:13:23 -070031import android.os.Build;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070032import android.os.IBinder;
Kenny Root6f1f03b2012-03-08 10:30:39 -080033import android.os.Process;
Robin Lee93772c32014-09-02 14:53:50 +010034import android.os.UserHandle;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070035import android.security.Credentials;
36import android.security.IKeyChainService;
Selim Gurun39e36e52012-02-14 10:50:42 -080037import android.security.KeyChain;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070038import android.security.KeyStore;
39import android.util.Log;
Eran Messeri5844b632017-11-03 10:28:34 +000040import com.android.keychain.internal.GrantsDatabase;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070041import java.io.ByteArrayInputStream;
Brian Carlstroma58db542011-05-11 23:02:20 -070042import java.io.IOException;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070043import java.security.cert.CertificateException;
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +010044import java.security.cert.CertificateEncodingException;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070045import java.security.cert.CertificateFactory;
46import java.security.cert.X509Certificate;
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +010047import java.util.Set;
48import java.util.List;
49import java.util.ArrayList;
50import java.util.Collections;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070051
Kenny Root3048b6c2013-04-23 22:38:11 -070052import com.android.org.conscrypt.TrustedCertificateStore;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070053
Fred Quintanafb2e18e2011-07-13 14:54:05 -070054public class KeyChainService extends IntentService {
Selim Gurun39e36e52012-02-14 10:50:42 -080055
Fred Quintanafb2e18e2011-07-13 14:54:05 -070056 private static final String TAG = "KeyChain";
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070057
Fred Quintanafb2e18e2011-07-13 14:54:05 -070058 /** created in onCreate(), closed in onDestroy() */
Eran Messeri5844b632017-11-03 10:28:34 +000059 public GrantsDatabase mGrantsDb;
Robin Leeba755b12016-02-24 15:27:43 +000060
Fred Quintanafb2e18e2011-07-13 14:54:05 -070061 public KeyChainService() {
62 super(KeyChainService.class.getSimpleName());
63 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070064
65 @Override public void onCreate() {
66 super.onCreate();
Eran Messeri5844b632017-11-03 10:28:34 +000067 mGrantsDb = new GrantsDatabase(this);
Fred Quintanafb2e18e2011-07-13 14:54:05 -070068 }
69
70 @Override
71 public void onDestroy() {
72 super.onDestroy();
Eran Messeri5844b632017-11-03 10:28:34 +000073 mGrantsDb.destroy();
74 mGrantsDb = null;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070075 }
76
77 private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070078 private final KeyStore mKeyStore = KeyStore.getInstance();
Brian Carlstroma58db542011-05-11 23:02:20 -070079 private final TrustedCertificateStore mTrustedCertificateStore
80 = new TrustedCertificateStore();
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070081
Kenny Root6f1f03b2012-03-08 10:30:39 -080082 @Override
83 public String requestPrivateKey(String alias) {
84 checkArgs(alias);
85
86 final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
87 final int uid = Binder.getCallingUid();
Janis Danisevskisc394aea2017-06-07 10:20:45 -070088 return mKeyStore.grant(keystoreAlias, uid);
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070089 }
90
Fred Quintanafb2e18e2011-07-13 14:54:05 -070091 @Override public byte[] getCertificate(String alias) {
Kenny Root6f1f03b2012-03-08 10:30:39 -080092 checkArgs(alias);
93 return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070094 }
95
Rubin Xu8714f062016-03-23 12:37:10 +000096 @Override public byte[] getCaCertificates(String alias) {
97 checkArgs(alias);
98 return mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
99 }
100
Eran Messeri1223b5f2017-11-08 18:47:40 +0000101 @Override public boolean isUserSelectable(String alias) {
102 validateAlias(alias);
103 return mGrantsDb.isUserSelectable(alias);
104 }
105
106 @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
107 validateAlias(alias);
108 checkSystemCaller();
109 mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
110 }
111
112 private void validateAlias(String alias) {
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700113 if (alias == null) {
114 throw new NullPointerException("alias == null");
115 }
Eran Messeri1223b5f2017-11-08 18:47:40 +0000116 }
117
118 private void validateKeyStoreState() {
Kenny Root4ff22962013-02-14 10:17:06 -0800119 if (!mKeyStore.isUnlocked()) {
Nick Kralevichc8b04632012-05-21 15:13:07 -0700120 throw new IllegalStateException("keystore is "
121 + mKeyStore.state().toString());
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700122 }
Eran Messeri1223b5f2017-11-08 18:47:40 +0000123 }
124
125 private void checkArgs(String alias) {
126 validateAlias(alias);
127 validateKeyStoreState();
Nick Kralevichc8b04632012-05-21 15:13:07 -0700128
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700129 final int callingUid = getCallingUid();
Eran Messeri5844b632017-11-03 10:28:34 +0000130 if (!mGrantsDb.hasGrant(callingUid, alias)) {
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700131 throw new IllegalStateException("uid " + callingUid
132 + " doesn't have permission to access the requested alias");
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700133 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700134 }
135
Bartosz Fabianowski9bacf2b2017-02-10 02:26:45 +0100136 @Override public String installCaCertificate(byte[] caCertificate) {
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700137 checkCertInstallerOrSystemCaller();
Bartosz Fabianowski9bacf2b2017-02-10 02:26:45 +0100138 final String alias;
Brian Carlstroma58db542011-05-11 23:02:20 -0700139 try {
Bartosz Fabianowski9bacf2b2017-02-10 02:26:45 +0100140 final X509Certificate cert = parseCertificate(caCertificate);
Brian Carlstroma58db542011-05-11 23:02:20 -0700141 synchronized (mTrustedCertificateStore) {
Bartosz Fabianowski9bacf2b2017-02-10 02:26:45 +0100142 mTrustedCertificateStore.installCertificate(cert);
143 alias = mTrustedCertificateStore.getCertificateAlias(cert);
Brian Carlstroma58db542011-05-11 23:02:20 -0700144 }
145 } catch (IOException e) {
146 throw new IllegalStateException(e);
147 } catch (CertificateException e) {
148 throw new IllegalStateException(e);
149 }
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700150 broadcastLegacyStorageChange();
151 broadcastTrustStoreChange();
Bartosz Fabianowski9bacf2b2017-02-10 02:26:45 +0100152 return alias;
Brian Carlstroma58db542011-05-11 23:02:20 -0700153 }
Brian Carlstrom5aeadd92011-05-17 00:40:33 -0700154
Rubin Xu8714f062016-03-23 12:37:10 +0000155 /**
156 * Install a key pair to the keystore.
157 *
158 * @param privateKey The private key associated with the client certificate
159 * @param userCertificate The client certificate to be installed
160 * @param userCertificateChain The rest of the chain for the client certificate
161 * @param alias The alias under which the key pair is installed
162 * @return Whether the operation succeeded or not.
163 */
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100164 @Override public boolean installKeyPair(byte[] privateKey, byte[] userCertificate,
Rubin Xu8714f062016-03-23 12:37:10 +0000165 byte[] userCertificateChain, String alias) {
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100166 checkCertInstallerOrSystemCaller();
Robin Lee8847b122015-07-27 12:50:28 +0100167 if (!mKeyStore.isUnlocked()) {
168 Log.e(TAG, "Keystore is " + mKeyStore.state().toString() + ". Credentials cannot"
169 + " be installed until device is unlocked");
170 return false;
171 }
Robin Leeba755b12016-02-24 15:27:43 +0000172 if (!removeKeyPair(alias)) {
173 return false;
174 }
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100175 if (!mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, privateKey, -1,
176 KeyStore.FLAG_ENCRYPTED)) {
177 Log.e(TAG, "Failed to import private key " + alias);
178 return false;
179 }
180 if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate, -1,
181 KeyStore.FLAG_ENCRYPTED)) {
182 Log.e(TAG, "Failed to import user certificate " + userCertificate);
Alex Klyubin44c777b2015-06-08 09:46:15 -0700183 if (!mKeyStore.delete(Credentials.USER_PRIVATE_KEY + alias)) {
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100184 Log.e(TAG, "Failed to delete private key after certificate importing failed");
185 }
186 return false;
187 }
Rubin Xu8714f062016-03-23 12:37:10 +0000188 if (userCertificateChain != null && userCertificateChain.length > 0) {
189 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain, -1,
190 KeyStore.FLAG_ENCRYPTED)) {
191 Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
192 if (!removeKeyPair(alias)) {
193 Log.e(TAG, "Failed to clean up key chain after certificate chain"
194 + " importing failed");
195 }
196 return false;
197 }
198 }
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700199 broadcastKeychainChange();
200 broadcastLegacyStorageChange();
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100201 return true;
202 }
203
Robin Leef44a5192015-08-03 17:18:02 +0100204 @Override public boolean removeKeyPair(String alias) {
205 checkCertInstallerOrSystemCaller();
Robin Leeba755b12016-02-24 15:27:43 +0000206 if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
207 return false;
208 }
Eran Messerid0bcac82017-11-20 11:55:31 +0000209 mGrantsDb.removeAliasInformation(alias);
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700210 broadcastKeychainChange();
211 broadcastLegacyStorageChange();
Robin Leeba755b12016-02-24 15:27:43 +0000212 return true;
Robin Leef44a5192015-08-03 17:18:02 +0100213 }
214
Brian Carlstrom5aeadd92011-05-17 00:40:33 -0700215 private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
216 CertificateFactory cf = CertificateFactory.getInstance("X.509");
217 return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
218 }
219
Brian Carlstroma58db542011-05-11 23:02:20 -0700220 @Override public boolean reset() {
221 // only Settings should be able to reset
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700222 checkSystemCaller();
Eran Messerid0bcac82017-11-20 11:55:31 +0000223 mGrantsDb.removeAllAliasesInformation();
Brian Carlstroma58db542011-05-11 23:02:20 -0700224 boolean ok = true;
Brian Carlstroma58db542011-05-11 23:02:20 -0700225 synchronized (mTrustedCertificateStore) {
226 // delete user-installed CA certs
227 for (String alias : mTrustedCertificateStore.aliases()) {
228 if (TrustedCertificateStore.isUser(alias)) {
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700229 if (!deleteCertificateEntry(alias)) {
Brian Carlstroma58db542011-05-11 23:02:20 -0700230 ok = false;
231 }
232 }
233 }
Brian Carlstroma58db542011-05-11 23:02:20 -0700234 }
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700235 broadcastTrustStoreChange();
Chad Brubaker8df30b52017-03-10 13:02:48 -0800236 broadcastKeychainChange();
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700237 broadcastLegacyStorageChange();
Selim Gurun39e36e52012-02-14 10:50:42 -0800238 return ok;
Brian Carlstroma58db542011-05-11 23:02:20 -0700239 }
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700240
241 @Override public boolean deleteCaCertificate(String alias) {
242 // only Settings should be able to delete
243 checkSystemCaller();
Selim Gurun39e36e52012-02-14 10:50:42 -0800244 boolean ok = true;
245 synchronized (mTrustedCertificateStore) {
246 ok = deleteCertificateEntry(alias);
247 }
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700248 broadcastTrustStoreChange();
249 broadcastLegacyStorageChange();
Selim Gurun39e36e52012-02-14 10:50:42 -0800250 return ok;
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700251 }
252
253 private boolean deleteCertificateEntry(String alias) {
254 try {
255 mTrustedCertificateStore.deleteCertificateEntry(alias);
256 return true;
257 } catch (IOException e) {
258 Log.w(TAG, "Problem removing CA certificate " + alias, e);
259 return false;
260 } catch (CertificateException e) {
261 Log.w(TAG, "Problem removing CA certificate " + alias, e);
262 return false;
263 }
264 }
265
266 private void checkCertInstallerOrSystemCaller() {
267 String actual = checkCaller("com.android.certinstaller");
268 if (actual == null) {
269 return;
270 }
271 checkSystemCaller();
272 }
273 private void checkSystemCaller() {
274 String actual = checkCaller("android.uid.system:1000");
275 if (actual != null) {
276 throw new IllegalStateException(actual);
277 }
278 }
279 /**
280 * Returns null if actually caller is expected, otherwise return bad package to report
281 */
282 private String checkCaller(String expectedPackage) {
283 String actualPackage = getPackageManager().getNameForUid(getCallingUid());
284 return (!expectedPackage.equals(actualPackage)) ? actualPackage : null;
285 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700286
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700287 @Override public boolean hasGrant(int uid, String alias) {
288 checkSystemCaller();
Eran Messeri5844b632017-11-03 10:28:34 +0000289 return mGrantsDb.hasGrant(uid, alias);
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700290 }
291
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700292 @Override public void setGrant(int uid, String alias, boolean value) {
293 checkSystemCaller();
Eran Messeri5844b632017-11-03 10:28:34 +0000294 mGrantsDb.setGrant(uid, alias, value);
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700295 broadcastPermissionChange(uid, alias, value);
296 broadcastLegacyStorageChange();
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700297 }
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100298
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100299 @Override
Robin Lee7e9d0452017-02-20 20:57:41 +0000300 public StringParceledListSlice getUserCaAliases() {
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100301 synchronized (mTrustedCertificateStore) {
Robin Lee7e9d0452017-02-20 20:57:41 +0000302 return new StringParceledListSlice(new ArrayList<String>(
303 mTrustedCertificateStore.userAliases()));
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100304 }
305 }
306
307 @Override
Robin Lee7e9d0452017-02-20 20:57:41 +0000308 public StringParceledListSlice getSystemCaAliases() {
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100309 synchronized (mTrustedCertificateStore) {
Robin Lee7e9d0452017-02-20 20:57:41 +0000310 return new StringParceledListSlice(new ArrayList<String>(
311 mTrustedCertificateStore.allSystemAliases()));
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100312 }
313 }
314
315 @Override
316 public boolean containsCaAlias(String alias) {
317 return mTrustedCertificateStore.containsAlias(alias);
318 }
319
320 @Override
321 public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
322 synchronized (mTrustedCertificateStore) {
323 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
324 .getCertificate(alias, includeDeletedSystem);
325 if (certificate == null) {
326 Log.w(TAG, "Could not find CA certificate " + alias);
327 return null;
328 }
329 try {
330 return certificate.getEncoded();
331 } catch (CertificateEncodingException e) {
332 Log.w(TAG, "Error while encoding CA certificate " + alias);
333 return null;
334 }
335 }
336 }
337
338 @Override
339 public List<String> getCaCertificateChainAliases(String rootAlias,
340 boolean includeDeletedSystem) {
341 synchronized (mTrustedCertificateStore) {
342 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
343 rootAlias, includeDeletedSystem);
344 try {
345 List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
346 root);
347 List<String> aliases = new ArrayList<String>(chain.size());
348 final int n = chain.size();
349 for (int i = 0; i < n; ++i) {
350 String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
351 true);
352 if (alias != null) {
353 aliases.add(alias);
354 }
355 }
356 return aliases;
357 } catch (CertificateException e) {
358 Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
359 return Collections.emptyList();
360 }
361 }
362 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700363 };
364
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700365 @Override public IBinder onBind(Intent intent) {
Brian Carlstrom7037b732011-06-30 15:04:49 -0700366 if (IKeyChainService.class.getName().equals(intent.getAction())) {
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700367 return mIKeyChainService;
368 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700369 return null;
370 }
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700371
372 @Override
373 protected void onHandleIntent(final Intent intent) {
374 if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
Eran Messeri5844b632017-11-03 10:28:34 +0000375 mGrantsDb.purgeOldGrants(getPackageManager());
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700376 }
377 }
Selim Gurun39e36e52012-02-14 10:50:42 -0800378
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700379 private void broadcastLegacyStorageChange() {
Selim Gurun39e36e52012-02-14 10:50:42 -0800380 Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700381 BroadcastOptions opts = BroadcastOptions.makeBasic();
Chad Brubaker04028072016-07-08 10:48:54 -0700382 opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.N_MR1);
Chad Brubakere5a5dd12016-07-25 14:34:54 -0700383 sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()), null, opts.toBundle());
Selim Gurun39e36e52012-02-14 10:50:42 -0800384 }
385
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700386 private void broadcastKeychainChange() {
387 Intent intent = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
Chad Brubakere5a5dd12016-07-25 14:34:54 -0700388 sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700389 }
390
391 private void broadcastTrustStoreChange() {
392 Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
Chad Brubakere5a5dd12016-07-25 14:34:54 -0700393 sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700394 }
395
396 private void broadcastPermissionChange(int uid, String alias, boolean access) {
397 // Since the permission change only impacts one uid only send to that uid's packages.
398 final PackageManager packageManager = getPackageManager();
399 String[] packages = packageManager.getPackagesForUid(uid);
400 if (packages == null) {
401 return;
402 }
403 for (String pckg : packages) {
404 Intent intent = new Intent(KeyChain.ACTION_KEY_ACCESS_CHANGED);
405 intent.putExtra(KeyChain.EXTRA_KEY_ALIAS, alias);
406 intent.putExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, access);
407 intent.setPackage(pckg);
Chad Brubakere5a5dd12016-07-25 14:34:54 -0700408 sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
Chad Brubaker2099a7d2016-05-02 13:13:23 -0700409 }
410 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700411}