1 /* 2 * Copyright (C) 2013 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.nfc.cardemulation; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 import org.xmlpull.v1.XmlSerializer; 22 23 import android.app.ActivityManager; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.content.pm.ServiceInfo; 32 import android.content.pm.PackageManager.NameNotFoundException; 33 import android.nfc.cardemulation.AidGroup; 34 import android.nfc.cardemulation.ApduServiceInfo; 35 import android.nfc.cardemulation.CardEmulation; 36 import android.nfc.cardemulation.HostApduService; 37 import android.nfc.cardemulation.OffHostApduService; 38 import android.os.UserHandle; 39 import android.util.AtomicFile; 40 import android.util.Log; 41 import android.util.SparseArray; 42 import android.util.Xml; 43 44 import com.android.internal.util.FastXmlSerializer; 45 import com.google.android.collect.Maps; 46 47 import java.io.File; 48 import java.io.FileDescriptor; 49 import java.io.FileInputStream; 50 import java.io.FileOutputStream; 51 import java.io.IOException; 52 import java.io.PrintWriter; 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.Iterator; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.concurrent.atomic.AtomicReference; 60 61 /** 62 * This class is inspired by android.content.pm.RegisteredServicesCache 63 * That class was not re-used because it doesn't support dynamically 64 * registering additional properties, but generates everything from 65 * the manifest. Since we have some properties that are not in the manifest, 66 * it's less suited. 67 */ 68 public class RegisteredServicesCache { 69 static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output"; 70 static final String TAG = "RegisteredServicesCache"; 71 static final boolean DEBUG = false; 72 73 final Context mContext; 74 final AtomicReference<BroadcastReceiver> mReceiver; 75 76 final Object mLock = new Object(); 77 // All variables below synchronized on mLock 78 79 // mUserServices holds the card emulation services that are running for each user 80 final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>(); 81 final Callback mCallback; 82 final AtomicFile mDynamicSettingsFile; 83 84 public interface Callback { onServicesUpdated(int userId, final List<ApduServiceInfo> services)85 void onServicesUpdated(int userId, final List<ApduServiceInfo> services); 86 }; 87 88 static class DynamicSettings { 89 public final int uid; 90 public final HashMap<String, AidGroup> aidGroups = Maps.newHashMap(); 91 public String offHostSE; 92 DynamicSettings(int uid)93 DynamicSettings(int uid) { 94 this.uid = uid; 95 } 96 }; 97 98 private static class UserServices { 99 /** 100 * All services that have registered 101 */ 102 final HashMap<ComponentName, ApduServiceInfo> services = 103 Maps.newHashMap(); // Re-built at run-time 104 final HashMap<ComponentName, DynamicSettings> dynamicSettings = 105 Maps.newHashMap(); // In memory cache of dynamic settings 106 }; 107 findOrCreateUserLocked(int userId)108 private UserServices findOrCreateUserLocked(int userId) { 109 UserServices services = mUserServices.get(userId); 110 if (services == null) { 111 services = new UserServices(); 112 mUserServices.put(userId, services); 113 } 114 return services; 115 } 116 RegisteredServicesCache(Context context, Callback callback)117 public RegisteredServicesCache(Context context, Callback callback) { 118 mContext = context; 119 mCallback = callback; 120 121 final BroadcastReceiver receiver = new BroadcastReceiver() { 122 @Override 123 public void onReceive(Context context, Intent intent) { 124 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 125 String action = intent.getAction(); 126 if (DEBUG) Log.d(TAG, "Intent action: " + action); 127 if (uid != -1) { 128 boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) && 129 (Intent.ACTION_PACKAGE_ADDED.equals(action) || 130 Intent.ACTION_PACKAGE_REMOVED.equals(action)); 131 if (!replaced) { 132 int currentUser = ActivityManager.getCurrentUser(); 133 if (currentUser == UserHandle.getUserId(uid)) { 134 invalidateCache(UserHandle.getUserId(uid)); 135 } else { 136 // Cache will automatically be updated on user switch 137 } 138 } else { 139 if (DEBUG) Log.d(TAG, "Ignoring package intent due to package being replaced."); 140 } 141 } 142 } 143 }; 144 mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 145 146 IntentFilter intentFilter = new IntentFilter(); 147 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 148 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 149 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 150 intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 151 intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH); 152 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 153 intentFilter.addDataScheme("package"); 154 mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, intentFilter, null, null); 155 156 // Register for events related to sdcard operations 157 IntentFilter sdFilter = new IntentFilter(); 158 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 159 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 160 mContext.registerReceiverAsUser(mReceiver.get(), UserHandle.ALL, sdFilter, null, null); 161 162 File dataDir = mContext.getFilesDir(); 163 mDynamicSettingsFile = new AtomicFile(new File(dataDir, "dynamic_aids.xml")); 164 } 165 initialize()166 void initialize() { 167 synchronized (mLock) { 168 readDynamicSettingsLocked(); 169 } 170 invalidateCache(ActivityManager.getCurrentUser()); 171 } 172 dump(ArrayList<ApduServiceInfo> services)173 void dump(ArrayList<ApduServiceInfo> services) { 174 for (ApduServiceInfo service : services) { 175 if (DEBUG) Log.d(TAG, service.toString()); 176 } 177 } 178 containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName)179 boolean containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName) { 180 for (ApduServiceInfo service : services) { 181 if (service.getComponent().equals(serviceName)) return true; 182 } 183 return false; 184 } 185 hasService(int userId, ComponentName service)186 public boolean hasService(int userId, ComponentName service) { 187 return getService(userId, service) != null; 188 } 189 getService(int userId, ComponentName service)190 public ApduServiceInfo getService(int userId, ComponentName service) { 191 synchronized (mLock) { 192 UserServices userServices = findOrCreateUserLocked(userId); 193 return userServices.services.get(service); 194 } 195 } 196 getServices(int userId)197 public List<ApduServiceInfo> getServices(int userId) { 198 final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>(); 199 synchronized (mLock) { 200 UserServices userServices = findOrCreateUserLocked(userId); 201 services.addAll(userServices.services.values()); 202 } 203 return services; 204 } 205 getServicesForCategory(int userId, String category)206 public List<ApduServiceInfo> getServicesForCategory(int userId, String category) { 207 final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>(); 208 synchronized (mLock) { 209 UserServices userServices = findOrCreateUserLocked(userId); 210 for (ApduServiceInfo service : userServices.services.values()) { 211 if (service.hasCategory(category)) services.add(service); 212 } 213 } 214 return services; 215 } 216 getInstalledServices(int userId)217 ArrayList<ApduServiceInfo> getInstalledServices(int userId) { 218 PackageManager pm; 219 try { 220 pm = mContext.createPackageContextAsUser("android", 0, 221 new UserHandle(userId)).getPackageManager(); 222 } catch (NameNotFoundException e) { 223 Log.e(TAG, "Could not create user package context"); 224 return null; 225 } 226 227 ArrayList<ApduServiceInfo> validServices = new ArrayList<ApduServiceInfo>(); 228 229 List<ResolveInfo> resolvedServices = new ArrayList<>(pm.queryIntentServicesAsUser( 230 new Intent(HostApduService.SERVICE_INTERFACE), 231 PackageManager.GET_META_DATA, userId)); 232 233 List<ResolveInfo> resolvedOffHostServices = pm.queryIntentServicesAsUser( 234 new Intent(OffHostApduService.SERVICE_INTERFACE), 235 PackageManager.GET_META_DATA, userId); 236 resolvedServices.addAll(resolvedOffHostServices); 237 238 for (ResolveInfo resolvedService : resolvedServices) { 239 try { 240 boolean onHost = !resolvedOffHostServices.contains(resolvedService); 241 ServiceInfo si = resolvedService.serviceInfo; 242 ComponentName componentName = new ComponentName(si.packageName, si.name); 243 // Check if the package holds the NFC permission 244 if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) != 245 PackageManager.PERMISSION_GRANTED) { 246 Log.e(TAG, "Skipping application component " + componentName + 247 ": it must request the permission " + 248 android.Manifest.permission.NFC); 249 continue; 250 } 251 if (!android.Manifest.permission.BIND_NFC_SERVICE.equals( 252 si.permission)) { 253 Log.e(TAG, "Skipping APDU service " + componentName + 254 ": it does not require the permission " + 255 android.Manifest.permission.BIND_NFC_SERVICE); 256 continue; 257 } 258 ApduServiceInfo service = new ApduServiceInfo(pm, resolvedService, onHost); 259 if (service != null) { 260 validServices.add(service); 261 } 262 } catch (XmlPullParserException e) { 263 Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e); 264 } catch (IOException e) { 265 Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e); 266 } 267 } 268 269 return validServices; 270 } 271 invalidateCache(int userId)272 public void invalidateCache(int userId) { 273 final ArrayList<ApduServiceInfo> validServices = getInstalledServices(userId); 274 if (validServices == null) { 275 return; 276 } 277 synchronized (mLock) { 278 UserServices userServices = findOrCreateUserLocked(userId); 279 280 // Find removed services 281 Iterator<Map.Entry<ComponentName, ApduServiceInfo>> it = 282 userServices.services.entrySet().iterator(); 283 while (it.hasNext()) { 284 Map.Entry<ComponentName, ApduServiceInfo> entry = 285 (Map.Entry<ComponentName, ApduServiceInfo>) it.next(); 286 if (!containsServiceLocked(validServices, entry.getKey())) { 287 Log.d(TAG, "Service removed: " + entry.getKey()); 288 it.remove(); 289 } 290 } 291 for (ApduServiceInfo service : validServices) { 292 if (DEBUG) Log.d(TAG, "Adding service: " + service.getComponent() + 293 " AIDs: " + service.getAids()); 294 userServices.services.put(service.getComponent(), service); 295 } 296 297 // Apply dynamic settings mappings 298 ArrayList<ComponentName> toBeRemoved = new ArrayList<ComponentName>(); 299 for (Map.Entry<ComponentName, DynamicSettings> entry : 300 userServices.dynamicSettings.entrySet()) { 301 // Verify component / uid match 302 ComponentName component = entry.getKey(); 303 DynamicSettings dynamicSettings = entry.getValue(); 304 ApduServiceInfo serviceInfo = userServices.services.get(component); 305 if (serviceInfo == null || (serviceInfo.getUid() != dynamicSettings.uid)) { 306 toBeRemoved.add(component); 307 continue; 308 } else { 309 for (AidGroup group : dynamicSettings.aidGroups.values()) { 310 serviceInfo.setOrReplaceDynamicAidGroup(group); 311 } 312 if (dynamicSettings.offHostSE != null) { 313 serviceInfo.setOffHostSecureElement(dynamicSettings.offHostSE); 314 } 315 } 316 } 317 if (toBeRemoved.size() > 0) { 318 for (ComponentName component : toBeRemoved) { 319 Log.d(TAG, "Removing dynamic AIDs registered by " + component); 320 userServices.dynamicSettings.remove(component); 321 } 322 // Persist to filesystem 323 writeDynamicSettingsLocked(); 324 } 325 } 326 mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices)); 327 dump(validServices); 328 } 329 readDynamicSettingsLocked()330 private void readDynamicSettingsLocked() { 331 FileInputStream fis = null; 332 try { 333 if (!mDynamicSettingsFile.getBaseFile().exists()) { 334 Log.d(TAG, "Dynamic AIDs file does not exist."); 335 return; 336 } 337 fis = mDynamicSettingsFile.openRead(); 338 XmlPullParser parser = Xml.newPullParser(); 339 parser.setInput(fis, null); 340 int eventType = parser.getEventType(); 341 while (eventType != XmlPullParser.START_TAG && 342 eventType != XmlPullParser.END_DOCUMENT) { 343 eventType = parser.next(); 344 } 345 String tagName = parser.getName(); 346 if ("services".equals(tagName)) { 347 boolean inService = false; 348 ComponentName currentComponent = null; 349 int currentUid = -1; 350 String currentOffHostSE = null; 351 ArrayList<AidGroup> currentGroups = new ArrayList<AidGroup>(); 352 while (eventType != XmlPullParser.END_DOCUMENT) { 353 tagName = parser.getName(); 354 if (eventType == XmlPullParser.START_TAG) { 355 if ("service".equals(tagName) && parser.getDepth() == 2) { 356 String compString = parser.getAttributeValue(null, "component"); 357 String uidString = parser.getAttributeValue(null, "uid"); 358 String offHostString = parser.getAttributeValue(null, "offHostSE"); 359 if (compString == null || uidString == null) { 360 Log.e(TAG, "Invalid service attributes"); 361 } else { 362 try { 363 currentUid = Integer.parseInt(uidString); 364 currentComponent = ComponentName.unflattenFromString(compString); 365 currentOffHostSE = offHostString; 366 inService = true; 367 } catch (NumberFormatException e) { 368 Log.e(TAG, "Could not parse service uid"); 369 } 370 } 371 } 372 if ("aid-group".equals(tagName) && parser.getDepth() == 3 && inService) { 373 AidGroup group = AidGroup.createFromXml(parser); 374 if (group != null) { 375 currentGroups.add(group); 376 } else { 377 Log.e(TAG, "Could not parse AID group."); 378 } 379 } 380 } else if (eventType == XmlPullParser.END_TAG) { 381 if ("service".equals(tagName)) { 382 // See if we have a valid service 383 if (currentComponent != null && currentUid >= 0 && 384 (currentGroups.size() > 0 || currentOffHostSE != null)) { 385 final int userId = UserHandle.getUserId(currentUid); 386 DynamicSettings dynSettings = new DynamicSettings(currentUid); 387 for (AidGroup group : currentGroups) { 388 dynSettings.aidGroups.put(group.getCategory(), group); 389 } 390 dynSettings.offHostSE = currentOffHostSE; 391 UserServices services = findOrCreateUserLocked(userId); 392 services.dynamicSettings.put(currentComponent, dynSettings); 393 } 394 currentUid = -1; 395 currentComponent = null; 396 currentGroups.clear(); 397 inService = false; 398 currentOffHostSE = null; 399 } 400 } 401 eventType = parser.next(); 402 }; 403 } 404 } catch (Exception e) { 405 Log.e(TAG, "Could not parse dynamic AIDs file, trashing."); 406 mDynamicSettingsFile.delete(); 407 } finally { 408 if (fis != null) { 409 try { 410 fis.close(); 411 } catch (IOException e) { 412 } 413 } 414 } 415 } 416 writeDynamicSettingsLocked()417 private boolean writeDynamicSettingsLocked() { 418 FileOutputStream fos = null; 419 try { 420 fos = mDynamicSettingsFile.startWrite(); 421 XmlSerializer out = new FastXmlSerializer(); 422 out.setOutput(fos, "utf-8"); 423 out.startDocument(null, true); 424 out.setFeature(XML_INDENT_OUTPUT_FEATURE, true); 425 out.startTag(null, "services"); 426 for (int i = 0; i < mUserServices.size(); i++) { 427 final UserServices user = mUserServices.valueAt(i); 428 for (Map.Entry<ComponentName, DynamicSettings> service : user.dynamicSettings.entrySet()) { 429 out.startTag(null, "service"); 430 out.attribute(null, "component", service.getKey().flattenToString()); 431 out.attribute(null, "uid", Integer.toString(service.getValue().uid)); 432 if(service.getValue().offHostSE != null) { 433 out.attribute(null, "offHostSE", service.getValue().offHostSE); 434 } 435 for (AidGroup group : service.getValue().aidGroups.values()) { 436 group.writeAsXml(out); 437 } 438 out.endTag(null, "service"); 439 } 440 } 441 out.endTag(null, "services"); 442 out.endDocument(); 443 mDynamicSettingsFile.finishWrite(fos); 444 return true; 445 } catch (Exception e) { 446 Log.e(TAG, "Error writing dynamic AIDs", e); 447 if (fos != null) { 448 mDynamicSettingsFile.failWrite(fos); 449 } 450 return false; 451 } 452 } 453 setOffHostSecureElement(int userId, int uid, ComponentName componentName, String offHostSE)454 public boolean setOffHostSecureElement(int userId, int uid, ComponentName componentName, 455 String offHostSE) { 456 ArrayList<ApduServiceInfo> newServices = null; 457 synchronized (mLock) { 458 UserServices services = findOrCreateUserLocked(userId); 459 // Check if we can find this service 460 ApduServiceInfo serviceInfo = getService(userId, componentName); 461 if (serviceInfo == null) { 462 Log.e(TAG, "Service " + componentName + " does not exist."); 463 return false; 464 } 465 if (serviceInfo.getUid() != uid) { 466 // This is probably a good indication something is wrong here. 467 // Either newer service installed with different uid (but then 468 // we should have known about it), or somebody calling us from 469 // a different uid. 470 Log.e(TAG, "UID mismatch."); 471 return false; 472 } 473 if (offHostSE == null || serviceInfo.isOnHost()) { 474 Log.e(TAG, "OffHostSE mismatch with Service type"); 475 return false; 476 } 477 478 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 479 if (dynSettings == null) { 480 dynSettings = new DynamicSettings(uid); 481 } 482 dynSettings.offHostSE = offHostSE; 483 boolean success = writeDynamicSettingsLocked(); 484 if (!success) { 485 Log.e(TAG, "Failed to persist AID group."); 486 dynSettings.offHostSE = null; 487 return false; 488 } 489 490 serviceInfo.setOffHostSecureElement(offHostSE); 491 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 492 } 493 // Make callback without the lock held 494 mCallback.onServicesUpdated(userId, newServices); 495 return true; 496 } 497 unsetOffHostSecureElement(int userId, int uid, ComponentName componentName)498 public boolean unsetOffHostSecureElement(int userId, int uid, ComponentName componentName) { 499 ArrayList<ApduServiceInfo> newServices = null; 500 synchronized (mLock) { 501 UserServices services = findOrCreateUserLocked(userId); 502 // Check if we can find this service 503 ApduServiceInfo serviceInfo = getService(userId, componentName); 504 if (serviceInfo == null) { 505 Log.e(TAG, "Service " + componentName + " does not exist."); 506 return false; 507 } 508 if (serviceInfo.getUid() != uid) { 509 // This is probably a good indication something is wrong here. 510 // Either newer service installed with different uid (but then 511 // we should have known about it), or somebody calling us from 512 // a different uid. 513 Log.e(TAG, "UID mismatch."); 514 return false; 515 } 516 if (serviceInfo.isOnHost() || serviceInfo.getOffHostSecureElement() == null) { 517 Log.e(TAG, "OffHostSE is not set"); 518 return false; 519 } 520 521 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 522 String offHostSE = dynSettings.offHostSE; 523 dynSettings.offHostSE = null; 524 boolean success = writeDynamicSettingsLocked(); 525 if (!success) { 526 Log.e(TAG, "Failed to persist AID group."); 527 dynSettings.offHostSE = offHostSE; 528 return false; 529 } 530 531 serviceInfo.unsetOffHostSecureElement(); 532 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 533 } 534 // Make callback without the lock held 535 mCallback.onServicesUpdated(userId, newServices); 536 return true; 537 } 538 registerAidGroupForService(int userId, int uid, ComponentName componentName, AidGroup aidGroup)539 public boolean registerAidGroupForService(int userId, int uid, 540 ComponentName componentName, AidGroup aidGroup) { 541 ArrayList<ApduServiceInfo> newServices = null; 542 boolean success; 543 synchronized (mLock) { 544 UserServices services = findOrCreateUserLocked(userId); 545 // Check if we can find this service 546 ApduServiceInfo serviceInfo = getService(userId, componentName); 547 if (serviceInfo == null) { 548 Log.e(TAG, "Service " + componentName + " does not exist."); 549 return false; 550 } 551 if (serviceInfo.getUid() != uid) { 552 // This is probably a good indication something is wrong here. 553 // Either newer service installed with different uid (but then 554 // we should have known about it), or somebody calling us from 555 // a different uid. 556 Log.e(TAG, "UID mismatch."); 557 return false; 558 } 559 // Do another AID validation, since a caller could have thrown in a 560 // modified AidGroup object with invalid AIDs over Binder. 561 List<String> aids = aidGroup.getAids(); 562 for (String aid : aids) { 563 if (!CardEmulation.isValidAid(aid)) { 564 Log.e(TAG, "AID " + aid + " is not a valid AID"); 565 return false; 566 } 567 } 568 serviceInfo.setOrReplaceDynamicAidGroup(aidGroup); 569 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 570 if (dynSettings == null) { 571 dynSettings = new DynamicSettings(uid); 572 dynSettings.offHostSE = null; 573 services.dynamicSettings.put(componentName, dynSettings); 574 } 575 dynSettings.aidGroups.put(aidGroup.getCategory(), aidGroup); 576 success = writeDynamicSettingsLocked(); 577 if (success) { 578 newServices = 579 new ArrayList<ApduServiceInfo>(services.services.values()); 580 } else { 581 Log.e(TAG, "Failed to persist AID group."); 582 // Undo registration 583 dynSettings.aidGroups.remove(aidGroup.getCategory()); 584 } 585 } 586 if (success) { 587 // Make callback without the lock held 588 mCallback.onServicesUpdated(userId, newServices); 589 } 590 return success; 591 } 592 getAidGroupForService(int userId, int uid, ComponentName componentName, String category)593 public AidGroup getAidGroupForService(int userId, int uid, ComponentName componentName, 594 String category) { 595 ApduServiceInfo serviceInfo = getService(userId, componentName); 596 if (serviceInfo != null) { 597 if (serviceInfo.getUid() != uid) { 598 Log.e(TAG, "UID mismatch"); 599 return null; 600 } 601 return serviceInfo.getDynamicAidGroupForCategory(category); 602 } else { 603 Log.e(TAG, "Could not find service " + componentName); 604 return null; 605 } 606 } 607 removeAidGroupForService(int userId, int uid, ComponentName componentName, String category)608 public boolean removeAidGroupForService(int userId, int uid, ComponentName componentName, 609 String category) { 610 boolean success = false; 611 ArrayList<ApduServiceInfo> newServices = null; 612 synchronized (mLock) { 613 UserServices services = findOrCreateUserLocked(userId); 614 ApduServiceInfo serviceInfo = getService(userId, componentName); 615 if (serviceInfo != null) { 616 if (serviceInfo.getUid() != uid) { 617 // Calling from different uid 618 Log.e(TAG, "UID mismatch"); 619 return false; 620 } 621 if (!serviceInfo.removeDynamicAidGroupForCategory(category)) { 622 Log.e(TAG," Could not find dynamic AIDs for category " + category); 623 return false; 624 } 625 // Remove from local cache 626 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 627 if (dynSettings != null) { 628 AidGroup deletedGroup = dynSettings.aidGroups.remove(category); 629 success = writeDynamicSettingsLocked(); 630 if (success) { 631 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 632 } else { 633 Log.e(TAG, "Could not persist deleted AID group."); 634 dynSettings.aidGroups.put(category, deletedGroup); 635 return false; 636 } 637 } else { 638 Log.e(TAG, "Could not find aid group in local cache."); 639 } 640 } else { 641 Log.e(TAG, "Service " + componentName + " does not exist."); 642 } 643 } 644 if (success) { 645 mCallback.onServicesUpdated(userId, newServices); 646 } 647 return success; 648 } 649 dump(FileDescriptor fd, PrintWriter pw, String[] args)650 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 651 pw.println("Registered HCE services for current user: "); 652 UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser()); 653 for (ApduServiceInfo service : userServices.services.values()) { 654 service.dump(fd, pw, args); 655 pw.println(""); 656 } 657 pw.println(""); 658 } 659 660 } 661