blob: 8ba71ac82a11cad15a525df76582103ee00c7e5d [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
Fred Quintanafb2e18e2011-07-13 14:54:05 -070019import android.app.IntentService;
20import android.content.ContentValues;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070021import android.content.Context;
22import android.content.Intent;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070023import android.content.pm.PackageManager;
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +010024import android.content.pm.ParceledListSlice;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070025import android.database.Cursor;
26import android.database.DatabaseUtils;
27import android.database.sqlite.SQLiteDatabase;
28import android.database.sqlite.SQLiteOpenHelper;
Kenny Root6f1f03b2012-03-08 10:30:39 -080029import android.os.Binder;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070030import android.os.IBinder;
Kenny Root6f1f03b2012-03-08 10:30:39 -080031import android.os.Process;
Robin Lee93772c32014-09-02 14:53:50 +010032import android.os.UserHandle;
Julia Reynolds3fb74492014-06-30 16:54:50 -040033import android.os.UserManager;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070034import android.security.Credentials;
35import android.security.IKeyChainService;
Selim Gurun39e36e52012-02-14 10:50:42 -080036import android.security.KeyChain;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070037import android.security.KeyStore;
38import android.util.Log;
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +010039import com.android.internal.util.ParcelableString;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070040import java.io.ByteArrayInputStream;
Brian Carlstroma58db542011-05-11 23:02:20 -070041import java.io.IOException;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070042import java.security.cert.CertificateException;
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +010043import java.security.cert.CertificateEncodingException;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070044import java.security.cert.CertificateFactory;
45import java.security.cert.X509Certificate;
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +010046import java.util.Set;
47import java.util.List;
48import java.util.ArrayList;
49import java.util.Collections;
Fred Quintanafb2e18e2011-07-13 14:54:05 -070050
Kenny Root3048b6c2013-04-23 22:38:11 -070051import com.android.org.conscrypt.TrustedCertificateStore;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070052
Fred Quintanafb2e18e2011-07-13 14:54:05 -070053public class KeyChainService extends IntentService {
Selim Gurun39e36e52012-02-14 10:50:42 -080054
Fred Quintanafb2e18e2011-07-13 14:54:05 -070055 private static final String TAG = "KeyChain";
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070056
Fred Quintanafb2e18e2011-07-13 14:54:05 -070057 private static final String DATABASE_NAME = "grants.db";
58 private static final int DATABASE_VERSION = 1;
59 private static final String TABLE_GRANTS = "grants";
60 private static final String GRANTS_ALIAS = "alias";
61 private static final String GRANTS_GRANTEE_UID = "uid";
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070062
Fred Quintanafb2e18e2011-07-13 14:54:05 -070063 /** created in onCreate(), closed in onDestroy() */
64 public DatabaseHelper mDatabaseHelper;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070065
Fred Quintanafb2e18e2011-07-13 14:54:05 -070066 private static final String SELECTION_COUNT_OF_MATCHING_GRANTS =
67 "SELECT COUNT(*) FROM " + TABLE_GRANTS
68 + " WHERE " + GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
69
70 private static final String SELECT_GRANTS_BY_UID_AND_ALIAS =
71 GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
72
73 private static final String SELECTION_GRANTS_BY_UID = GRANTS_GRANTEE_UID + "=?";
74
Robin Leeba755b12016-02-24 15:27:43 +000075 private static final String SELECTION_GRANTS_BY_ALIAS = GRANTS_ALIAS + "=?";
76
Fred Quintanafb2e18e2011-07-13 14:54:05 -070077 public KeyChainService() {
78 super(KeyChainService.class.getSimpleName());
79 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070080
81 @Override public void onCreate() {
82 super.onCreate();
Fred Quintanafb2e18e2011-07-13 14:54:05 -070083 mDatabaseHelper = new DatabaseHelper(this);
84 }
85
86 @Override
87 public void onDestroy() {
88 super.onDestroy();
89 mDatabaseHelper.close();
90 mDatabaseHelper = null;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070091 }
92
93 private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070094 private final KeyStore mKeyStore = KeyStore.getInstance();
Brian Carlstroma58db542011-05-11 23:02:20 -070095 private final TrustedCertificateStore mTrustedCertificateStore
96 = new TrustedCertificateStore();
Brian Carlstrom3e6251d2011-04-11 09:05:06 -070097
Kenny Root6f1f03b2012-03-08 10:30:39 -080098 @Override
99 public String requestPrivateKey(String alias) {
100 checkArgs(alias);
101
102 final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
103 final int uid = Binder.getCallingUid();
104 if (!mKeyStore.grant(keystoreAlias, uid)) {
105 return null;
106 }
Robin Lee93772c32014-09-02 14:53:50 +0100107 final int userHandle = UserHandle.getUserId(uid);
108 final int systemUidForUser = UserHandle.getUid(userHandle, Process.SYSTEM_UID);
Kenny Root6f1f03b2012-03-08 10:30:39 -0800109
110 final StringBuilder sb = new StringBuilder();
Robin Lee93772c32014-09-02 14:53:50 +0100111 sb.append(systemUidForUser);
Kenny Root6f1f03b2012-03-08 10:30:39 -0800112 sb.append('_');
113 sb.append(keystoreAlias);
114
115 return sb.toString();
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700116 }
117
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700118 @Override public byte[] getCertificate(String alias) {
Kenny Root6f1f03b2012-03-08 10:30:39 -0800119 checkArgs(alias);
120 return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700121 }
122
Kenny Root6f1f03b2012-03-08 10:30:39 -0800123 private void checkArgs(String alias) {
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700124 if (alias == null) {
125 throw new NullPointerException("alias == null");
126 }
Kenny Root4ff22962013-02-14 10:17:06 -0800127 if (!mKeyStore.isUnlocked()) {
Nick Kralevichc8b04632012-05-21 15:13:07 -0700128 throw new IllegalStateException("keystore is "
129 + mKeyStore.state().toString());
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700130 }
Nick Kralevichc8b04632012-05-21 15:13:07 -0700131
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700132 final int callingUid = getCallingUid();
133 if (!hasGrantInternal(mDatabaseHelper.getReadableDatabase(), callingUid, alias)) {
134 throw new IllegalStateException("uid " + callingUid
135 + " doesn't have permission to access the requested alias");
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700136 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700137 }
138
Brian Carlstroma58db542011-05-11 23:02:20 -0700139 @Override public void installCaCertificate(byte[] caCertificate) {
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700140 checkCertInstallerOrSystemCaller();
Julia Reynolds3fb74492014-06-30 16:54:50 -0400141 checkUserRestriction();
Brian Carlstroma58db542011-05-11 23:02:20 -0700142 try {
143 synchronized (mTrustedCertificateStore) {
144 mTrustedCertificateStore.installCertificate(parseCertificate(caCertificate));
145 }
146 } catch (IOException e) {
147 throw new IllegalStateException(e);
148 } catch (CertificateException e) {
149 throw new IllegalStateException(e);
150 }
Selim Gurun39e36e52012-02-14 10:50:42 -0800151 broadcastStorageChange();
Brian Carlstroma58db542011-05-11 23:02:20 -0700152 }
Brian Carlstrom5aeadd92011-05-17 00:40:33 -0700153
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100154 @Override public boolean installKeyPair(byte[] privateKey, byte[] userCertificate,
155 String alias) {
156 checkCertInstallerOrSystemCaller();
Robin Lee8847b122015-07-27 12:50:28 +0100157 if (!mKeyStore.isUnlocked()) {
158 Log.e(TAG, "Keystore is " + mKeyStore.state().toString() + ". Credentials cannot"
159 + " be installed until device is unlocked");
160 return false;
161 }
Robin Leeba755b12016-02-24 15:27:43 +0000162 if (!removeKeyPair(alias)) {
163 return false;
164 }
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100165 if (!mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, privateKey, -1,
166 KeyStore.FLAG_ENCRYPTED)) {
167 Log.e(TAG, "Failed to import private key " + alias);
168 return false;
169 }
170 if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate, -1,
171 KeyStore.FLAG_ENCRYPTED)) {
172 Log.e(TAG, "Failed to import user certificate " + userCertificate);
Alex Klyubin44c777b2015-06-08 09:46:15 -0700173 if (!mKeyStore.delete(Credentials.USER_PRIVATE_KEY + alias)) {
Bernhard Bauerd300fc52014-07-21 15:32:30 +0100174 Log.e(TAG, "Failed to delete private key after certificate importing failed");
175 }
176 return false;
177 }
178 broadcastStorageChange();
179 return true;
180 }
181
Robin Leef44a5192015-08-03 17:18:02 +0100182 @Override public boolean removeKeyPair(String alias) {
183 checkCertInstallerOrSystemCaller();
Robin Leeba755b12016-02-24 15:27:43 +0000184 if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
185 return false;
186 }
187 removeGrantsForAlias(alias);
188 broadcastStorageChange();
189 return true;
Robin Leef44a5192015-08-03 17:18:02 +0100190 }
191
Brian Carlstrom5aeadd92011-05-17 00:40:33 -0700192 private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
193 CertificateFactory cf = CertificateFactory.getInstance("X.509");
194 return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
195 }
196
Brian Carlstroma58db542011-05-11 23:02:20 -0700197 @Override public boolean reset() {
198 // only Settings should be able to reset
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700199 checkSystemCaller();
Julia Reynolds3fb74492014-06-30 16:54:50 -0400200 checkUserRestriction();
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700201 removeAllGrants(mDatabaseHelper.getWritableDatabase());
Brian Carlstroma58db542011-05-11 23:02:20 -0700202 boolean ok = true;
Brian Carlstroma58db542011-05-11 23:02:20 -0700203 synchronized (mTrustedCertificateStore) {
204 // delete user-installed CA certs
205 for (String alias : mTrustedCertificateStore.aliases()) {
206 if (TrustedCertificateStore.isUser(alias)) {
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700207 if (!deleteCertificateEntry(alias)) {
Brian Carlstroma58db542011-05-11 23:02:20 -0700208 ok = false;
209 }
210 }
211 }
Brian Carlstroma58db542011-05-11 23:02:20 -0700212 }
Selim Gurun39e36e52012-02-14 10:50:42 -0800213 broadcastStorageChange();
214 return ok;
Brian Carlstroma58db542011-05-11 23:02:20 -0700215 }
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700216
217 @Override public boolean deleteCaCertificate(String alias) {
218 // only Settings should be able to delete
219 checkSystemCaller();
Julia Reynolds3fb74492014-06-30 16:54:50 -0400220 checkUserRestriction();
Selim Gurun39e36e52012-02-14 10:50:42 -0800221 boolean ok = true;
222 synchronized (mTrustedCertificateStore) {
223 ok = deleteCertificateEntry(alias);
224 }
225 broadcastStorageChange();
226 return ok;
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700227 }
228
229 private boolean deleteCertificateEntry(String alias) {
230 try {
231 mTrustedCertificateStore.deleteCertificateEntry(alias);
232 return true;
233 } catch (IOException e) {
234 Log.w(TAG, "Problem removing CA certificate " + alias, e);
235 return false;
236 } catch (CertificateException e) {
237 Log.w(TAG, "Problem removing CA certificate " + alias, e);
238 return false;
239 }
240 }
241
242 private void checkCertInstallerOrSystemCaller() {
243 String actual = checkCaller("com.android.certinstaller");
244 if (actual == null) {
245 return;
246 }
247 checkSystemCaller();
248 }
249 private void checkSystemCaller() {
250 String actual = checkCaller("android.uid.system:1000");
251 if (actual != null) {
252 throw new IllegalStateException(actual);
253 }
254 }
Julia Reynolds3fb74492014-06-30 16:54:50 -0400255 private void checkUserRestriction() {
256 UserManager um = (UserManager) getSystemService(USER_SERVICE);
257 if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)) {
258 throw new SecurityException("User cannot modify credentials");
259 }
260 }
Brian Carlstrom43f5b772011-06-27 02:27:16 -0700261 /**
262 * Returns null if actually caller is expected, otherwise return bad package to report
263 */
264 private String checkCaller(String expectedPackage) {
265 String actualPackage = getPackageManager().getNameForUid(getCallingUid());
266 return (!expectedPackage.equals(actualPackage)) ? actualPackage : null;
267 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700268
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700269 @Override public boolean hasGrant(int uid, String alias) {
270 checkSystemCaller();
271 return hasGrantInternal(mDatabaseHelper.getReadableDatabase(), uid, alias);
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700272 }
273
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700274 @Override public void setGrant(int uid, String alias, boolean value) {
275 checkSystemCaller();
276 setGrantInternal(mDatabaseHelper.getWritableDatabase(), uid, alias, value);
Selim Gurun39e36e52012-02-14 10:50:42 -0800277 broadcastStorageChange();
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700278 }
Zoltan Szatmary-Ban3d25b312014-08-18 10:54:19 +0100279
280 private ParceledListSlice<ParcelableString> makeAliasesParcelableSynchronised(
281 Set<String> aliasSet) {
282 List<ParcelableString> aliases = new ArrayList<ParcelableString>(aliasSet.size());
283 for (String alias : aliasSet) {
284 ParcelableString parcelableString = new ParcelableString();
285 parcelableString.string = alias;
286 aliases.add(parcelableString);
287 }
288 return new ParceledListSlice<ParcelableString>(aliases);
289 }
290
291 @Override
292 public ParceledListSlice<ParcelableString> getUserCaAliases() {
293 synchronized (mTrustedCertificateStore) {
294 Set<String> aliasSet = mTrustedCertificateStore.userAliases();
295 return makeAliasesParcelableSynchronised(aliasSet);
296 }
297 }
298
299 @Override
300 public ParceledListSlice<ParcelableString> getSystemCaAliases() {
301 synchronized (mTrustedCertificateStore) {
302 Set<String> aliasSet = mTrustedCertificateStore.allSystemAliases();
303 return makeAliasesParcelableSynchronised(aliasSet);
304 }
305 }
306
307 @Override
308 public boolean containsCaAlias(String alias) {
309 return mTrustedCertificateStore.containsAlias(alias);
310 }
311
312 @Override
313 public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
314 synchronized (mTrustedCertificateStore) {
315 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
316 .getCertificate(alias, includeDeletedSystem);
317 if (certificate == null) {
318 Log.w(TAG, "Could not find CA certificate " + alias);
319 return null;
320 }
321 try {
322 return certificate.getEncoded();
323 } catch (CertificateEncodingException e) {
324 Log.w(TAG, "Error while encoding CA certificate " + alias);
325 return null;
326 }
327 }
328 }
329
330 @Override
331 public List<String> getCaCertificateChainAliases(String rootAlias,
332 boolean includeDeletedSystem) {
333 synchronized (mTrustedCertificateStore) {
334 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
335 rootAlias, includeDeletedSystem);
336 try {
337 List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
338 root);
339 List<String> aliases = new ArrayList<String>(chain.size());
340 final int n = chain.size();
341 for (int i = 0; i < n; ++i) {
342 String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
343 true);
344 if (alias != null) {
345 aliases.add(alias);
346 }
347 }
348 return aliases;
349 } catch (CertificateException e) {
350 Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
351 return Collections.emptyList();
352 }
353 }
354 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700355 };
356
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700357 private boolean hasGrantInternal(final SQLiteDatabase db, final int uid, final String alias) {
358 final long numMatches = DatabaseUtils.longForQuery(db, SELECTION_COUNT_OF_MATCHING_GRANTS,
359 new String[]{String.valueOf(uid), alias});
360 return numMatches > 0;
361 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700362
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700363 private void setGrantInternal(final SQLiteDatabase db,
364 final int uid, final String alias, final boolean value) {
365 if (value) {
366 if (!hasGrantInternal(db, uid, alias)) {
367 final ContentValues values = new ContentValues();
368 values.put(GRANTS_ALIAS, alias);
369 values.put(GRANTS_GRANTEE_UID, uid);
370 db.insert(TABLE_GRANTS, GRANTS_ALIAS, values);
371 }
372 } else {
373 db.delete(TABLE_GRANTS, SELECT_GRANTS_BY_UID_AND_ALIAS,
374 new String[]{String.valueOf(uid), alias});
375 }
376 }
377
Robin Leeba755b12016-02-24 15:27:43 +0000378 private void removeGrantsForAlias(String alias) {
379 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
380 db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_ALIAS, new String[] {alias});
381 }
382
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700383 private void removeAllGrants(final SQLiteDatabase db) {
384 db.delete(TABLE_GRANTS, null /* whereClause */, null /* whereArgs */);
385 }
386
387 private class DatabaseHelper extends SQLiteOpenHelper {
388 public DatabaseHelper(Context context) {
389 super(context, DATABASE_NAME, null /* CursorFactory */, DATABASE_VERSION);
390 }
391
392 @Override
393 public void onCreate(final SQLiteDatabase db) {
394 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
395 + GRANTS_ALIAS + " STRING NOT NULL, "
396 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
397 + "UNIQUE (" + GRANTS_ALIAS + "," + GRANTS_GRANTEE_UID + "))");
398 }
399
400 @Override
401 public void onUpgrade(final SQLiteDatabase db, int oldVersion, final int newVersion) {
402 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
403
404 if (oldVersion == 1) {
405 // the first upgrade step goes here
406 oldVersion++;
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700407 }
Brian Carlstrom7037b732011-06-30 15:04:49 -0700408 }
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700409 }
410
411 @Override public IBinder onBind(Intent intent) {
Brian Carlstrom7037b732011-06-30 15:04:49 -0700412 if (IKeyChainService.class.getName().equals(intent.getAction())) {
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700413 return mIKeyChainService;
414 }
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700415 return null;
416 }
Fred Quintanafb2e18e2011-07-13 14:54:05 -0700417
418 @Override
419 protected void onHandleIntent(final Intent intent) {
420 if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
421 purgeOldGrants();
422 }
423 }
424
425 private void purgeOldGrants() {
426 final PackageManager packageManager = getPackageManager();
427 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
428 Cursor cursor = null;
429 db.beginTransaction();
430 try {
431 cursor = db.query(TABLE_GRANTS,
432 new String[]{GRANTS_GRANTEE_UID}, null, null, GRANTS_GRANTEE_UID, null, null);
433 while (cursor.moveToNext()) {
434 final int uid = cursor.getInt(0);
435 final boolean packageExists = packageManager.getPackagesForUid(uid) != null;
436 if (packageExists) {
437 continue;
438 }
439 Log.d(TAG, "deleting grants for UID " + uid
440 + " because its package is no longer installed");
441 db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_UID,
442 new String[]{Integer.toString(uid)});
443 }
444 db.setTransactionSuccessful();
445 } finally {
446 if (cursor != null) {
447 cursor.close();
448 }
449 db.endTransaction();
450 }
451 }
Selim Gurun39e36e52012-02-14 10:50:42 -0800452
453 private void broadcastStorageChange() {
454 Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
Robin Lee1f00eaf2014-10-16 16:27:02 +0100455 sendBroadcastAsUser(intent, new UserHandle(UserHandle.myUserId()));
Selim Gurun39e36e52012-02-14 10:50:42 -0800456 }
457
Brian Carlstrom3e6251d2011-04-11 09:05:06 -0700458}