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 static android.nfc.cardemulation.CardEmulation.CATEGORY_OTHER; 20 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET; 21 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE; 22 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR; 23 import static android.nfc.cardemulation.CardEmulation.SET_SERVICE_ENABLED_STATUS_OK; 24 25 import android.app.ActivityManager; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageManager; 33 import android.content.pm.PackageManager.NameNotFoundException; 34 import android.content.pm.PackageManager.ResolveInfoFlags; 35 import android.content.pm.ResolveInfo; 36 import android.content.pm.ServiceInfo; 37 import android.nfc.cardemulation.AidGroup; 38 import android.nfc.cardemulation.ApduServiceInfo; 39 import android.nfc.cardemulation.CardEmulation; 40 import android.nfc.cardemulation.HostApduService; 41 import android.nfc.cardemulation.OffHostApduService; 42 import android.os.ParcelFileDescriptor; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.sysprop.NfcProperties; 46 import android.text.TextUtils; 47 import android.util.AtomicFile; 48 import android.util.Log; 49 import android.util.Pair; 50 import android.util.SparseArray; 51 import android.util.Xml; 52 import android.util.proto.ProtoOutputStream; 53 54 import androidx.annotation.VisibleForTesting; 55 56 import com.android.internal.annotations.GuardedBy; 57 import com.android.internal.util.FastXmlSerializer; 58 import com.android.nfc.NfcInjector; 59 import com.android.nfc.NfcService; 60 import com.android.nfc.R; 61 import com.android.nfc.Utils; 62 import com.android.nfc.cardemulation.util.NfcFileUtils; 63 64 import org.xmlpull.v1.XmlPullParser; 65 import org.xmlpull.v1.XmlPullParserException; 66 import org.xmlpull.v1.XmlSerializer; 67 68 import java.io.File; 69 import java.io.FileDescriptor; 70 import java.io.FileNotFoundException; 71 import java.io.FileOutputStream; 72 import java.io.IOException; 73 import java.io.InputStream; 74 import java.io.PrintWriter; 75 import java.util.ArrayList; 76 import java.util.Collections; 77 import java.util.HashMap; 78 import java.util.Iterator; 79 import java.util.List; 80 import java.util.Map; 81 import java.util.concurrent.atomic.AtomicReference; 82 83 /** 84 * This class is inspired by android.content.pm.RegisteredServicesCache 85 * That class was not re-used because it doesn't support dynamically 86 * registering additional properties, but generates everything from 87 * the manifest. Since we have some properties that are not in the manifest, 88 * it's less suited. 89 */ 90 public class RegisteredServicesCache { 91 static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output"; 92 static final String TAG = "RegisteredServicesCache"; 93 static final String AID_XML_PATH = "dynamic_aids.xml"; 94 static final String OTHER_STATUS_PATH = "other_status.xml"; 95 static final String PACKAGE_DATA = "package"; 96 static final boolean DEBUG = NfcProperties.debug_enabled().orElse(true); 97 private static final boolean VDBG = false; // turn on for local testing. 98 99 final Context mContext; 100 final AtomicReference<BroadcastReceiver> mReceiver; 101 102 final Object mLock = new Object(); 103 // All variables below synchronized on mLock 104 105 // mUserHandles holds the UserHandles of all the profiles that belong to current user 106 @GuardedBy("mLock") 107 List<UserHandle> mUserHandles; 108 109 // mUserServices holds the card emulation services that are running for each user 110 final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>(); 111 final Callback mCallback; 112 SettingsFile mDynamicSettingsFile; 113 SettingsFile mOthersFile; 114 final ServiceParser mServiceParser; 115 final RoutingOptionManager mRoutingOptionManager; 116 117 final Intent mHostApduServiceIntent = new Intent(HostApduService.SERVICE_INTERFACE); 118 final Intent mOffHostApduServiceIntent = new Intent(OffHostApduService.SERVICE_INTERFACE); 119 120 static final String DEFAULT_T4T_NFCEE_AID = "D2760000850101"; 121 122 public interface Callback { 123 /** 124 * ServicesUpdated for specific userId. 125 */ onServicesUpdated(int userId, List<ApduServiceInfo> services, boolean validateInstalled)126 void onServicesUpdated(int userId, List<ApduServiceInfo> services, 127 boolean validateInstalled); 128 }; 129 130 static class DynamicSettings { 131 public final int uid; 132 public final Map<String, Boolean> pollingLoopFilters = new HashMap<>(); 133 public final Map<String, Boolean> pollingLoopPatternFilters = new HashMap<>(); 134 public final HashMap<String, AidGroup> aidGroups = new HashMap<>(); 135 public String offHostSE; 136 public String shouldDefaultToObserveModeStr; 137 DynamicSettings(int uid)138 DynamicSettings(int uid) { 139 this.uid = uid; 140 } 141 }; 142 143 static class OtherServiceStatus { 144 public final int uid; 145 public boolean checked; 146 OtherServiceStatus(int uid, boolean checked)147 OtherServiceStatus(int uid, boolean checked) { 148 this.uid = uid; 149 this.checked = checked; 150 } 151 }; 152 153 @VisibleForTesting 154 static class UserServices { 155 /** 156 * All services that have registered 157 */ 158 final HashMap<ComponentName, ApduServiceInfo> services = 159 new HashMap<>(); // Re-built at run-time 160 final HashMap<ComponentName, DynamicSettings> dynamicSettings = 161 new HashMap<>(); // In memory cache of dynamic settings 162 final HashMap<ComponentName, OtherServiceStatus> others = 163 new HashMap<>(); 164 }; 165 166 @VisibleForTesting 167 static class SettingsFile { 168 final AtomicFile mFile; SettingsFile(Context context, String path)169 SettingsFile(Context context, String path) { 170 File dir = context.getFilesDir(); 171 mFile = new AtomicFile(new File(dir, path)); 172 } 173 exists()174 boolean exists() { 175 return mFile.getBaseFile().exists(); 176 } 177 openRead()178 InputStream openRead() throws FileNotFoundException { 179 return mFile.openRead(); 180 } 181 delete()182 void delete() { 183 mFile.delete(); 184 } 185 startWrite()186 FileOutputStream startWrite() throws IOException { 187 return mFile.startWrite(); 188 } 189 finishWrite(FileOutputStream fileOutputStream)190 void finishWrite(FileOutputStream fileOutputStream) { 191 mFile.finishWrite(fileOutputStream); 192 } 193 failWrite(FileOutputStream fileOutputStream)194 void failWrite(FileOutputStream fileOutputStream) { 195 mFile.failWrite(fileOutputStream); 196 } 197 getBaseFile()198 File getBaseFile() { 199 return mFile.getBaseFile(); 200 } 201 } 202 203 @VisibleForTesting 204 interface ServiceParser { parseApduService(PackageManager packageManager, ResolveInfo resolveInfo, boolean onHost)205 ApduServiceInfo parseApduService(PackageManager packageManager, 206 ResolveInfo resolveInfo, 207 boolean onHost) throws XmlPullParserException, IOException; 208 } 209 210 private static class RealServiceParser implements ServiceParser { 211 212 @Override parseApduService(PackageManager packageManager, ResolveInfo resolveInfo, boolean onHost)213 public ApduServiceInfo parseApduService(PackageManager packageManager, 214 ResolveInfo resolveInfo, boolean onHost) 215 throws XmlPullParserException, IOException { 216 return new ApduServiceInfo(packageManager, resolveInfo, onHost); 217 } 218 } 219 findOrCreateUserLocked(int userId)220 private UserServices findOrCreateUserLocked(int userId) { 221 UserServices services = mUserServices.get(userId); 222 if (services == null) { 223 services = new UserServices(); 224 mUserServices.put(userId, services); 225 } 226 return services; 227 } 228 getProfileParentId(Context context, int userId)229 private int getProfileParentId(Context context, int userId) { 230 UserManager um = context.getSystemService(UserManager.class); 231 UserHandle uh = um.getProfileParent(UserHandle.of(userId)); 232 return uh == null ? userId : uh.getIdentifier(); 233 } 234 getProfileParentId(int userId)235 private int getProfileParentId(int userId) { 236 return getProfileParentId(mContext.createContextAsUser( 237 UserHandle.of(userId), /*flags=*/0), userId); 238 } 239 RegisteredServicesCache(Context context, Callback callback)240 public RegisteredServicesCache(Context context, Callback callback) { 241 this(context, callback, new SettingsFile(context, AID_XML_PATH), 242 new SettingsFile(context, OTHER_STATUS_PATH), new RealServiceParser(), 243 RoutingOptionManager.getInstance()); 244 } 245 246 @VisibleForTesting RegisteredServicesCache(Context context, Callback callback, RoutingOptionManager routingOptionManager)247 RegisteredServicesCache(Context context, Callback callback, 248 RoutingOptionManager routingOptionManager) { 249 this(context, callback, new SettingsFile(context, AID_XML_PATH), 250 new SettingsFile(context, OTHER_STATUS_PATH), new RealServiceParser(), 251 routingOptionManager); 252 } 253 254 @VisibleForTesting RegisteredServicesCache(Context context, Callback callback, SettingsFile dynamicSettings, SettingsFile otherSettings, ServiceParser serviceParser, RoutingOptionManager routingOptionManager)255 RegisteredServicesCache(Context context, Callback callback, SettingsFile dynamicSettings, 256 SettingsFile otherSettings, ServiceParser serviceParser, 257 RoutingOptionManager routingOptionManager) { 258 mContext = context; 259 mCallback = callback; 260 mServiceParser = serviceParser; 261 mRoutingOptionManager = routingOptionManager; 262 263 synchronized (mLock) { 264 refreshUserProfilesLocked(false); 265 } 266 267 final BroadcastReceiver receiver = new BroadcastReceiver() { 268 @Override 269 public void onReceive(Context context, Intent intent) { 270 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 271 String action = intent.getAction(); 272 if (VDBG) Log.d(TAG, "onReceive: Intent action: " + action); 273 274 if (mRoutingOptionManager.isRoutingTableOverrided()) { 275 if (DEBUG) { 276 Log.d(TAG, "onReceive: Routing table overrided. Skip invalidateCache()"); 277 } 278 } 279 if (uid == -1) return; 280 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 281 int currentUser = ActivityManager.getCurrentUser(); 282 if (currentUser != getProfileParentId(context, userId)) { 283 // Cache will automatically be updated on user switch 284 if (VDBG) { 285 Log.d(TAG, 286 "onReceive: Ignoring package change intent from non-current user"); 287 } 288 return; 289 } 290 // If app not removed, check if the app has any valid CE services. 291 if (!Intent.ACTION_PACKAGE_REMOVED.equals(action) && 292 !Utils.hasCeServicesWithValidPermissions(mContext, intent, userId)) { 293 if (VDBG) { 294 Log.d(TAG, "onReceive: Ignoring package change intent from non-CE app"); 295 } 296 return; 297 } 298 boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) 299 && (Intent.ACTION_PACKAGE_ADDED.equals(action) 300 || Intent.ACTION_PACKAGE_REMOVED.equals(action)); 301 if (!replaced) { 302 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 303 invalidateCache(UserHandle. 304 getUserHandleForUid(uid).getIdentifier(), true); 305 } else { 306 invalidateCache(UserHandle. 307 getUserHandleForUid(uid).getIdentifier(), false); 308 } 309 } else { 310 if (DEBUG) { 311 Log.d(TAG, 312 "onReceive: Ignoring package intent due to package " 313 + "being replaced"); 314 } 315 } 316 } 317 }; 318 mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 319 320 IntentFilter intentFilter = new IntentFilter(); 321 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 322 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 323 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 324 intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 325 intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH); 326 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 327 intentFilter.addDataScheme(PACKAGE_DATA); 328 mContext.registerReceiverForAllUsers(mReceiver.get(), intentFilter, null, null); 329 330 // Register for events related to sdcard operations 331 IntentFilter sdFilter = new IntentFilter(); 332 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 333 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 334 mContext.registerReceiverForAllUsers(mReceiver.get(), sdFilter, null, null); 335 336 mDynamicSettingsFile = dynamicSettings; 337 mOthersFile = otherSettings; 338 } 339 initialize()340 void initialize() { 341 synchronized (mLock) { 342 readDynamicSettingsLocked(); 343 readOthersLocked(); 344 if (NfcInjector.getInstance().isBootCompleted()) { 345 for (UserHandle uh : mUserHandles) { 346 invalidateCache(uh.getIdentifier(), false); 347 } 348 } 349 } 350 } 351 onBootCompleted()352 public void onBootCompleted() { 353 synchronized (mLock) { 354 refreshUserProfilesLocked(true); 355 } 356 } 357 onUserSwitched()358 public void onUserSwitched() { 359 synchronized (mLock) { 360 refreshUserProfilesLocked(true); 361 } 362 } 363 onManagedProfileChanged()364 public void onManagedProfileChanged() { 365 synchronized (mLock) { 366 refreshUserProfilesLocked(true); 367 } 368 } 369 migrateFromCe(Context ceContext)370 void migrateFromCe(Context ceContext) { 371 File ceFilesDir = ceContext.getFilesDir(); 372 File deFilesDir = mContext.getFilesDir(); 373 if (NfcFileUtils.isEmptyDir(ceFilesDir)) { 374 Log.d(TAG, "migrateFromCe: Nothing to migrate from CE data"); 375 return; 376 } 377 if (NfcFileUtils.moveFiles(ceFilesDir, deFilesDir) < 0) { 378 Log.e(TAG, "migrateFromCe: Failed to move directory from " + ceFilesDir + " to " 379 + deFilesDir); 380 return; 381 } 382 Log.i(TAG, "migrateFromCe: Moved directory from " + ceFilesDir + " to " + deFilesDir 383 + ". Reinitializing cache."); 384 mDynamicSettingsFile = new SettingsFile(mContext, AID_XML_PATH); 385 mOthersFile = new SettingsFile(mContext, OTHER_STATUS_PATH); 386 } 387 migrateSettingsFilesFromCe(Context ceContext)388 public void migrateSettingsFilesFromCe(Context ceContext) { 389 synchronized (mLock) { 390 migrateFromCe(ceContext); 391 initialize(); 392 } 393 } 394 refreshUserProfilesLocked(boolean invalidateCache)395 private void refreshUserProfilesLocked(boolean invalidateCache) { 396 UserManager um = mContext.createContextAsUser( 397 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0) 398 .getSystemService(UserManager.class); 399 mUserHandles = um.getEnabledProfiles(); 400 List<UserHandle> removeUserHandles = new ArrayList<UserHandle>(); 401 402 for (UserHandle uh : mUserHandles) { 403 if (um.isQuietModeEnabled(uh)) { 404 removeUserHandles.add(uh); 405 } 406 } 407 mUserHandles.removeAll(removeUserHandles); 408 if (invalidateCache) { 409 for (UserHandle uh : mUserHandles) { 410 invalidateCache(uh.getIdentifier(), false); 411 } 412 } 413 } 414 dump(List<ApduServiceInfo> services)415 void dump(List<ApduServiceInfo> services) { 416 for (ApduServiceInfo service : services) { 417 if (DEBUG) Log.d(TAG, service.toString()); 418 } 419 } 420 dump(ArrayList<ComponentName> services)421 void dump(ArrayList<ComponentName> services) { 422 for (ComponentName service : services) { 423 if (DEBUG) Log.d(TAG, service.toString()); 424 } 425 } 426 containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName)427 boolean containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName) { 428 for (ApduServiceInfo service : services) { 429 if (service.getComponent().equals(serviceName)) return true; 430 } 431 return false; 432 } 433 hasService(int userId, ComponentName service)434 public boolean hasService(int userId, ComponentName service) { 435 return getService(userId, service) != null; 436 } 437 getService(int userId, ComponentName service)438 public ApduServiceInfo getService(int userId, ComponentName service) { 439 synchronized (mLock) { 440 UserServices userServices = findOrCreateUserLocked(userId); 441 return userServices.services.get(service); 442 } 443 } 444 getServices(int userId)445 public List<ApduServiceInfo> getServices(int userId) { 446 final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>(); 447 synchronized (mLock) { 448 UserServices userServices = findOrCreateUserLocked(userId); 449 services.addAll(userServices.services.values()); 450 } 451 return services; 452 } 453 getServicesForCategory(int userId, String category)454 public List<ApduServiceInfo> getServicesForCategory(int userId, String category) { 455 final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>(); 456 synchronized (mLock) { 457 UserServices userServices = findOrCreateUserLocked(userId); 458 for (ApduServiceInfo service : userServices.services.values()) { 459 if (service.hasCategory(category)) services.add(service); 460 } 461 } 462 return services; 463 } 464 getInstalledServices(int userId)465 ArrayList<ApduServiceInfo> getInstalledServices(int userId) { 466 PackageManager pm; 467 try { 468 pm = mContext.createPackageContextAsUser("android", 0, 469 UserHandle.of(userId)).getPackageManager(); 470 } catch (NameNotFoundException e) { 471 Log.e(TAG, "getInstalledServices: Could not create user package context"); 472 return null; 473 } 474 475 ArrayList<ApduServiceInfo> validServices = new ArrayList<ApduServiceInfo>(); 476 477 List<ResolveInfo> resolvedServices = new ArrayList<>(pm.queryIntentServicesAsUser( 478 mHostApduServiceIntent, 479 ResolveInfoFlags.of(PackageManager.GET_META_DATA), UserHandle.of(userId))); 480 481 List<ResolveInfo> resolvedOffHostServices = pm.queryIntentServicesAsUser( 482 mOffHostApduServiceIntent, 483 ResolveInfoFlags.of(PackageManager.GET_META_DATA), UserHandle.of(userId)); 484 resolvedServices.addAll(resolvedOffHostServices); 485 for (ResolveInfo resolvedService : resolvedServices) { 486 try { 487 boolean onHost = !resolvedOffHostServices.contains(resolvedService); 488 ServiceInfo si = resolvedService.serviceInfo; 489 ComponentName componentName = new ComponentName(si.packageName, si.name); 490 // Check if the package exported the service in manifest 491 if (!si.exported) { 492 Log.e(TAG, "getInstalledServices: Skipping application component " 493 + componentName + ": it must configured as exported"); 494 continue; 495 } 496 // Check if the package holds the NFC permission 497 if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) != 498 PackageManager.PERMISSION_GRANTED) { 499 Log.e(TAG, 500 "getInstalledServices: Skipping application component " + componentName 501 + ": it must request the permission " 502 + android.Manifest.permission.NFC); 503 continue; 504 } 505 if (!android.Manifest.permission.BIND_NFC_SERVICE.equals( 506 si.permission)) { 507 Log.e(TAG, 508 "getInstalledServices: Skipping APDU service " + componentName 509 + ": it does not require the permission " 510 + android.Manifest.permission.BIND_NFC_SERVICE); 511 continue; 512 } 513 ApduServiceInfo service = mServiceParser.parseApduService(pm, resolvedService, 514 onHost); 515 if (service != null) { 516 validServices.add(service); 517 } 518 } catch (XmlPullParserException e) { 519 Log.w(TAG, "getInstalledServices: Unable to load component info " 520 + resolvedService.toString(), e); 521 } catch (IOException e) { 522 Log.w(TAG, "getInstalledServices: Unable to load component info " 523 + resolvedService.toString(), e); 524 } 525 } 526 527 // Add NDEF-NFCEE AID - Only if NDEF-NFCEE feature supported 528 // And only for user 0 to avoid adding several times (if multiple profiles) 529 if (userId == UserHandle.SYSTEM.getIdentifier() 530 && NfcService.getInstance().isNdefNfceefeatureEnabled()) { 531 ResolveInfo ndefNfceeAppInfo = new ResolveInfo(); 532 ndefNfceeAppInfo.resolvePackageName = "NdefNfceeAidRoute"; 533 ndefNfceeAppInfo.serviceInfo = new ServiceInfo(); 534 ndefNfceeAppInfo.serviceInfo.packageName = "com.android.nfc.ndef_nfcee"; 535 ndefNfceeAppInfo.serviceInfo.name = "com.android.nfc.ndef_nfcee.NdefNfceeService"; 536 ndefNfceeAppInfo.serviceInfo.applicationInfo = new ApplicationInfo(); 537 List<String> ndefNfceeAid = new ArrayList<String>(); 538 ndefNfceeAid.add(DEFAULT_T4T_NFCEE_AID); 539 AidGroup ndefNfceeAidGroup = new AidGroup(ndefNfceeAid, CATEGORY_OTHER); 540 ArrayList<AidGroup> ndefNfceeAidStaticGroups = new ArrayList<>(); 541 ndefNfceeAidStaticGroups.add(ndefNfceeAidGroup); 542 ArrayList<AidGroup> ndefNfceeAidDynamicGroups = new ArrayList<>(); 543 ApduServiceInfo ndefNfceeAidService = new ApduServiceInfo( 544 ndefNfceeAppInfo, 545 false, 546 mContext.getResources().getString(R.string.device_ndef_nfcee_service_name), 547 ndefNfceeAidStaticGroups, 548 ndefNfceeAidDynamicGroups, 549 false, 550 0, 551 userId, 552 mContext.getResources().getString(R.string.device_ndef_nfcee_service_name), 553 RoutingOptionManager.SE_NDEF_NFCEE, 554 RoutingOptionManager.SE_NDEF_NFCEE); 555 validServices.add(ndefNfceeAidService); 556 } 557 558 return validServices; 559 } 560 561 /** 562 * invalidateCache for specific userId. 563 */ invalidateCache(int userId, boolean validateInstalled)564 public void invalidateCache(int userId, boolean validateInstalled) { 565 final ArrayList<ApduServiceInfo> validServices = getInstalledServices(userId); 566 if (validServices == null) { 567 return; 568 } 569 ArrayList<ApduServiceInfo> toBeAdded = new ArrayList<>(); 570 ArrayList<ApduServiceInfo> toBeRemoved = new ArrayList<>(); 571 synchronized (mLock) { 572 UserServices userServices = findOrCreateUserLocked(userId); 573 574 // Find removed services 575 Iterator<Map.Entry<ComponentName, ApduServiceInfo>> it = 576 userServices.services.entrySet().iterator(); 577 while (it.hasNext()) { 578 Map.Entry<ComponentName, ApduServiceInfo> entry = 579 (Map.Entry<ComponentName, ApduServiceInfo>) it.next(); 580 if (!containsServiceLocked(validServices, entry.getKey())) { 581 toBeRemoved.add(entry.getValue()); 582 it.remove(); 583 } 584 } 585 for (ApduServiceInfo service : validServices) { 586 toBeAdded.add(service); 587 userServices.services.put(service.getComponent(), service); 588 } 589 590 // Apply dynamic settings mappings 591 ArrayList<ComponentName> toBeRemovedComponent = new ArrayList<ComponentName>(); 592 for (Map.Entry<ComponentName, DynamicSettings> entry : 593 userServices.dynamicSettings.entrySet()) { 594 // Verify component / uid match 595 ComponentName component = entry.getKey(); 596 DynamicSettings dynamicSettings = entry.getValue(); 597 ApduServiceInfo serviceInfo = userServices.services.get(component); 598 if (serviceInfo == null) { 599 toBeRemovedComponent.add(component); 600 continue; 601 } else { 602 for (AidGroup group : dynamicSettings.aidGroups.values()) { 603 serviceInfo.setDynamicAidGroup(group); 604 } 605 for (Map.Entry<String, Boolean> filter : dynamicSettings.pollingLoopFilters 606 .entrySet()) { 607 serviceInfo.addPollingLoopFilter(filter.getKey(), filter.getValue()); 608 } 609 for (Map.Entry<String, Boolean> filter : dynamicSettings 610 .pollingLoopPatternFilters 611 .entrySet()) { 612 serviceInfo.addPollingLoopPatternFilter(filter.getKey(), filter.getValue()); 613 } 614 if (dynamicSettings.offHostSE != null) { 615 serviceInfo.setOffHostSecureElement(dynamicSettings.offHostSE); 616 } 617 if (dynamicSettings.shouldDefaultToObserveModeStr != null) { 618 serviceInfo.setShouldDefaultToObserveMode( 619 convertValueToBoolean(dynamicSettings.shouldDefaultToObserveModeStr, 620 false)); 621 } 622 } 623 } 624 if (toBeRemoved.size() > 0) { 625 for (ComponentName component : toBeRemovedComponent) { 626 Log.d(TAG, "invalidateCache: Removing dynamic AIDs registered by " + component); 627 userServices.dynamicSettings.remove(component); 628 } 629 // Persist to filesystem 630 writeDynamicSettingsLocked(); 631 } 632 } 633 634 List<ApduServiceInfo> otherServices = getServicesForCategory(userId, 635 CardEmulation.CATEGORY_OTHER); 636 invalidateOther(userId, otherServices); 637 638 mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices), 639 validateInstalled); 640 if (VDBG) { 641 Log.i(TAG, "invalidateCache: Services => "); 642 dump(validServices); 643 } else { 644 // dump only new services added or removed 645 Log.i(TAG, "invalidateCache: New Services => "); 646 dump(toBeAdded); 647 Log.i(TAG, "invalidateCache: Removed Services => "); 648 dump(toBeRemoved); 649 } 650 } 651 invalidateOther(int userId, List<ApduServiceInfo> validOtherServices)652 private void invalidateOther(int userId, List<ApduServiceInfo> validOtherServices) { 653 Log.d(TAG, "invalidateOther : " + userId); 654 ArrayList<ComponentName> toBeAdded = new ArrayList<>(); 655 ArrayList<ComponentName> toBeRemoved = new ArrayList<>(); 656 // remove services 657 synchronized (mLock) { 658 UserServices userServices = findOrCreateUserLocked(userId); 659 boolean needToWrite = false; 660 Iterator<Map.Entry<ComponentName, OtherServiceStatus>> it = 661 userServices.others.entrySet().iterator(); 662 663 while (it.hasNext()) { 664 Map.Entry<ComponentName, OtherServiceStatus> entry = it.next(); 665 if (!containsServiceLocked((ArrayList<ApduServiceInfo>) validOtherServices, 666 entry.getKey())) { 667 toBeRemoved.add(entry.getKey()); 668 needToWrite = true; 669 it.remove(); 670 } 671 } 672 673 UserManager um = mContext.createContextAsUser( 674 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0) 675 .getSystemService(UserManager.class); 676 boolean isManagedProfile = um.isManagedProfile(userId); 677 Log.i(TAG, "invalidateOther: current user: " + ActivityManager.getCurrentUser() 678 + ", is managed profile : " + isManagedProfile); 679 boolean isChecked = true; 680 if (NfcInjector.getInstance().getDeviceConfigFacade().getCeDisableOtherServicesOnManagedProfiles()) { 681 isChecked = !(isManagedProfile); 682 } 683 for (ApduServiceInfo service : validOtherServices) { 684 if (VDBG) { 685 Log.d(TAG, "invalidateOther: update valid otherService: " 686 + service.getComponent() + " AIDs: " + service.getAids()); 687 } 688 if (!service.hasCategory(CardEmulation.CATEGORY_OTHER)) { 689 Log.e(TAG, "invalidateOther: service does not have other category"); 690 continue; 691 } 692 693 ComponentName component = service.getComponent(); 694 OtherServiceStatus status = userServices.others.get(component); 695 696 if (status == null) { 697 toBeAdded.add(service.getComponent()); 698 status = new OtherServiceStatus(service.getUid(), isChecked); 699 needToWrite = true; 700 } 701 service.setCategoryOtherServiceEnabled(status.checked); 702 userServices.others.put(component, status); 703 } 704 705 if (needToWrite) { 706 writeOthersLocked(); 707 } 708 } 709 if (VDBG) { 710 Log.i(TAG, "invalidateOther: Services => "); 711 dump(validOtherServices); 712 } else { 713 // dump only new services added or removed 714 Log.i(TAG, "invalidateOther: New Services => "); 715 dump(toBeAdded); 716 Log.i(TAG, "invalidateOther: Removed Services => "); 717 dump(toBeRemoved); 718 } 719 } 720 convertValueToBoolean(CharSequence value, boolean defaultValue)721 private static final boolean convertValueToBoolean(CharSequence value, boolean defaultValue) { 722 boolean result = false; 723 724 if (TextUtils.isEmpty(value)) { 725 return defaultValue; 726 } 727 728 if (value.equals("1") 729 || value.equals("true") 730 || value.equals("TRUE")) 731 result = true; 732 733 return result; 734 } 735 736 @VisibleForTesting 737 static Map<Integer, List<Pair<ComponentName, DynamicSettings>>> readDynamicSettingsFromFile(SettingsFile settingsFile)738 readDynamicSettingsFromFile(SettingsFile settingsFile) { 739 Log.d(TAG, "readDynamicSettingsFromFile"); 740 Map<Integer, List<Pair<ComponentName, DynamicSettings>>> readSettingsMap = 741 new HashMap<>(); 742 InputStream fis = null; 743 try { 744 if (!settingsFile.exists()) { 745 Log.d(TAG, "readDynamicSettingsFromFile: Dynamic AIDs file does not exist"); 746 return new HashMap<>(); 747 } 748 fis = settingsFile.openRead(); 749 XmlPullParser parser = Xml.newPullParser(); 750 parser.setInput(fis, null); 751 int eventType = parser.getEventType(); 752 while (eventType != XmlPullParser.START_TAG && 753 eventType != XmlPullParser.END_DOCUMENT) { 754 eventType = parser.next(); 755 } 756 String tagName = parser.getName(); 757 if ("services".equals(tagName)) { 758 boolean inService = false; 759 ComponentName currentComponent = null; 760 int currentUid = -1; 761 String currentOffHostSE = null; 762 String shouldDefaultToObserveModeStr = null; 763 ArrayList<AidGroup> currentGroups = new ArrayList<AidGroup>(); 764 Map<String, Boolean> plFilters = new HashMap<>(); 765 Map<String, Boolean> plPatternFilters = new HashMap<>(); 766 while (eventType != XmlPullParser.END_DOCUMENT) { 767 tagName = parser.getName(); 768 if (eventType == XmlPullParser.START_TAG) { 769 if ("service".equals(tagName) && parser.getDepth() == 2) { 770 String compString = parser.getAttributeValue(null, "component"); 771 String uidString = parser.getAttributeValue(null, "uid"); 772 String offHostString 773 = parser.getAttributeValue(null, "offHostSE"); 774 shouldDefaultToObserveModeStr = 775 parser.getAttributeValue(null, "shouldDefaultToObserveMode"); 776 if (compString == null || uidString == null) { 777 Log.e(TAG, 778 "readDynamicSettingsFromFile: Invalid service attributes"); 779 } else { 780 try { 781 currentUid = Integer.parseInt(uidString); 782 currentComponent = ComponentName 783 .unflattenFromString(compString); 784 currentOffHostSE = offHostString; 785 inService = true; 786 } catch (NumberFormatException e) { 787 Log.e(TAG, 788 "readDynamicSettingsFromFile: " 789 + "Could not parse service uid"); 790 } 791 } 792 } 793 if ("aid-group".equals(tagName) && parser.getDepth() == 3 && inService) { 794 AidGroup group = AidGroup.createFromXml(parser); 795 if (group != null) { 796 currentGroups.add(group); 797 } else { 798 Log.e(TAG, 799 "readDynamicSettingsFromFile: Could not parse AID group"); 800 } 801 } 802 if ("pl_filter".equals(tagName) && parser.getDepth() == 4 && inService) { 803 String filter = parser.getAttributeValue(null, "value"); 804 String autoTransact = parser 805 .getAttributeValue(null, "auto_transact"); 806 plFilters.put(filter, Boolean.parseBoolean(autoTransact)); 807 } 808 if ("pl_pattern_filter".equals(tagName) && parser.getDepth() == 4 809 && inService) { 810 String pattern = parser.getAttributeValue(null, "value"); 811 String autoTransact = parser 812 .getAttributeValue(null, "auto_transact"); 813 plPatternFilters.put(pattern, Boolean.parseBoolean(autoTransact)); 814 } 815 } else if (eventType == XmlPullParser.END_TAG) { 816 if ("service".equals(tagName)) { 817 // See if we have a valid service 818 if (currentComponent != null && currentUid >= 0 && 819 (currentGroups.size() > 0 || currentOffHostSE != null)) { 820 final int userId = UserHandle. 821 getUserHandleForUid(currentUid).getIdentifier(); 822 Log.d(TAG, "readDynamicSettingsFromFile: ## user id - " + userId); 823 DynamicSettings dynSettings = new DynamicSettings(currentUid); 824 for (AidGroup group : currentGroups) { 825 dynSettings.aidGroups.put(group.getCategory(), group); 826 } 827 dynSettings.pollingLoopFilters.putAll(plFilters); 828 dynSettings.pollingLoopPatternFilters.putAll(plPatternFilters); 829 dynSettings.offHostSE = currentOffHostSE; 830 dynSettings.shouldDefaultToObserveModeStr 831 = shouldDefaultToObserveModeStr; 832 if (!readSettingsMap.containsKey(userId)) { 833 readSettingsMap.put(userId, new ArrayList<>()); 834 } 835 readSettingsMap.get(userId) 836 .add(new Pair<>(currentComponent, dynSettings)); 837 } 838 currentUid = -1; 839 currentComponent = null; 840 plFilters.clear(); 841 plPatternFilters.clear(); 842 currentGroups.clear(); 843 inService = false; 844 currentOffHostSE = null; 845 } 846 } 847 eventType = parser.next(); 848 }; 849 } 850 } catch (Exception e) { 851 Log.e(TAG, "readDynamicSettingsFromFile: Could not parse dynamic AIDs file, trashing", 852 e); 853 settingsFile.delete(); 854 } finally { 855 if (fis != null) { 856 try { 857 fis.close(); 858 } catch (IOException e) { 859 } 860 } 861 } 862 return readSettingsMap; 863 } 864 readDynamicSettingsLocked()865 private void readDynamicSettingsLocked() { 866 Map<Integer, List<Pair<ComponentName, DynamicSettings>>> readSettingsMap 867 = readDynamicSettingsFromFile(mDynamicSettingsFile); 868 for (Integer userId: readSettingsMap.keySet()) { 869 UserServices services = findOrCreateUserLocked(userId); 870 List<Pair<ComponentName, DynamicSettings>> componentNameDynamicServiceStatusPairs 871 = readSettingsMap.get(userId); 872 int pairsSize = componentNameDynamicServiceStatusPairs.size(); 873 for (int i = 0; i < pairsSize; i++) { 874 Pair<ComponentName, DynamicSettings> pair 875 = componentNameDynamicServiceStatusPairs.get(i); 876 services.dynamicSettings.put(pair.first, pair.second); 877 } 878 } 879 } 880 881 @VisibleForTesting 882 static Map<Integer, List<Pair<ComponentName, OtherServiceStatus>>> readOtherFromFile(SettingsFile settingsFile)883 readOtherFromFile(SettingsFile settingsFile) { 884 Map<Integer, List<Pair<ComponentName, OtherServiceStatus>>> readSettingsMap = 885 new HashMap<>(); 886 Log.d(TAG, "readOtherFromFile"); 887 888 InputStream fis = null; 889 try { 890 if (!settingsFile.exists()) { 891 Log.d(TAG, "readOtherFromFile: Other settings file does not exist."); 892 return new HashMap<>(); 893 } 894 fis = settingsFile.openRead(); 895 XmlPullParser parser = Xml.newPullParser(); 896 parser.setInput(fis, null); 897 int eventType = parser.getEventType(); 898 while (eventType != XmlPullParser.START_TAG && 899 eventType != XmlPullParser.END_DOCUMENT) { 900 eventType = parser.next(); 901 } 902 String tagName = parser.getName(); 903 if ("services".equals(tagName)) { 904 boolean checked = false; 905 ComponentName currentComponent = null; 906 int currentUid = -1; 907 908 while (eventType != XmlPullParser.END_DOCUMENT) { 909 tagName = parser.getName(); 910 if (eventType == XmlPullParser.START_TAG) { 911 if ("service".equals(tagName) && parser.getDepth() == 2) { 912 String compString = parser.getAttributeValue(null, "component"); 913 String uidString = parser.getAttributeValue(null, "uid"); 914 String checkedString = parser.getAttributeValue(null, "checked"); 915 if (compString == null || uidString == null || checkedString == null) { 916 Log.e(TAG, "readOtherFromFile: Invalid service attributes"); 917 } else { 918 try { 919 currentUid = Integer.parseInt(uidString); 920 currentComponent = 921 ComponentName.unflattenFromString(compString); 922 checked = checkedString.equals("true") ? true : false; 923 } catch (NumberFormatException e) { 924 Log.e(TAG, "readOtherFromFile: Could not parse service uid"); 925 } 926 } 927 } 928 } else if (eventType == XmlPullParser.END_TAG) { 929 if ("service".equals(tagName)) { 930 // See if we have a valid service 931 if (currentComponent != null && currentUid >= 0) { 932 Log.d(TAG, "readOtherFromFile: end of service tag"); 933 final int userId = 934 UserHandle.getUserHandleForUid(currentUid).getIdentifier(); 935 OtherServiceStatus status = 936 new OtherServiceStatus(currentUid, checked); 937 Log.d(TAG, "readOtherFromFile: ## user id - " + userId); 938 if (!readSettingsMap.containsKey(userId)) { 939 readSettingsMap.put(userId, new ArrayList<>()); 940 } 941 readSettingsMap.get(userId) 942 .add(new Pair<>(currentComponent, status)); 943 } 944 currentUid = -1; 945 currentComponent = null; 946 checked = false; 947 } 948 } 949 eventType = parser.next(); 950 } 951 } 952 } catch (Exception e) { 953 Log.e(TAG, "readOtherFromFile: Could not parse others AIDs file, trashing", e); 954 settingsFile.delete(); 955 } finally { 956 if (fis != null) { 957 try { 958 fis.close(); 959 } catch (IOException e) { 960 // It is safe to ignore I/O exceptions when closing FileInputStream 961 } 962 } 963 } 964 return readSettingsMap; 965 } 966 readOthersLocked()967 private void readOthersLocked() { 968 Map<Integer, List<Pair<ComponentName, OtherServiceStatus>>> readSettingsMap 969 = readOtherFromFile(mOthersFile); 970 for (Integer userId: readSettingsMap.keySet()) { 971 UserServices services = findOrCreateUserLocked(userId); 972 List<Pair<ComponentName, OtherServiceStatus>> componentNameOtherServiceStatusPairs 973 = readSettingsMap.get(userId); 974 int pairsSize = componentNameOtherServiceStatusPairs.size(); 975 for (int i = 0; i < pairsSize; i++) { 976 Pair<ComponentName, OtherServiceStatus> pair 977 = componentNameOtherServiceStatusPairs.get(i); 978 services.others.put(pair.first, 979 pair.second); 980 } 981 } 982 } 983 writeDynamicSettingsLocked()984 private boolean writeDynamicSettingsLocked() { 985 FileOutputStream fos = null; 986 try { 987 fos = mDynamicSettingsFile.startWrite(); 988 XmlSerializer out = Xml.newSerializer(); 989 out.setOutput(fos, "utf-8"); 990 out.startDocument(null, true); 991 out.setFeature(XML_INDENT_OUTPUT_FEATURE, true); 992 out.startTag(null, "services"); 993 for (int i = 0; i < mUserServices.size(); i++) { 994 final UserServices user = mUserServices.valueAt(i); 995 for (Map.Entry<ComponentName, DynamicSettings> service : 996 user.dynamicSettings.entrySet()) { 997 out.startTag(null, "service"); 998 out.attribute(null, "component", service.getKey().flattenToString()); 999 out.attribute(null, "uid", Integer.toString(service.getValue().uid)); 1000 if (service.getValue().offHostSE != null) { 1001 out.attribute(null, "offHostSE", service.getValue().offHostSE); 1002 } 1003 if (service.getValue().shouldDefaultToObserveModeStr != null) { 1004 out.attribute(null, "shouldDefaultToObserveMode", 1005 service.getValue().shouldDefaultToObserveModeStr); 1006 } 1007 for (AidGroup group : service.getValue().aidGroups.values()) { 1008 group.writeAsXml(out); 1009 } 1010 out.startTag(null , "pl_filters"); 1011 for (Map.Entry<String, Boolean> filter 1012 : service.getValue().pollingLoopFilters.entrySet()) { 1013 out.startTag(null, "pl_filter"); 1014 out.attribute(null, "value", filter.getKey()); 1015 out.attribute(null, "auto_transact", 1016 Boolean.toString(filter.getValue())); 1017 out.endTag(null, "pl_filter"); 1018 } 1019 out.endTag(null, "pl_filters"); 1020 out.startTag(null , "pl_pattern_filters"); 1021 for (Map.Entry<String, Boolean> filter 1022 : service.getValue().pollingLoopPatternFilters.entrySet()) { 1023 out.startTag(null, "pl_pattern_filter"); 1024 out.attribute(null, "value", filter.getKey()); 1025 out.attribute(null, "auto_transact", 1026 Boolean.toString(filter.getValue())); 1027 out.endTag(null, "pl_pattern_filter"); 1028 } 1029 out.endTag(null, "pl_pattern_filters"); 1030 out.endTag(null, "service"); 1031 } 1032 } 1033 out.endTag(null, "services"); 1034 out.endDocument(); 1035 mDynamicSettingsFile.finishWrite(fos); 1036 return true; 1037 } catch (Exception e) { 1038 Log.e(TAG, "writeDynamicSettingsLocked: Error writing dynamic AIDs", e); 1039 if (fos != null) { 1040 mDynamicSettingsFile.failWrite(fos); 1041 } 1042 return false; 1043 } 1044 } 1045 writeOthersLocked()1046 private boolean writeOthersLocked() { 1047 Log.d(TAG, "writeOthersLocked"); 1048 1049 FileOutputStream fos = null; 1050 try { 1051 fos = mOthersFile.startWrite(); 1052 XmlSerializer out = new FastXmlSerializer(); 1053 out.setOutput(fos, "utf-8"); 1054 out.startDocument(null, true); 1055 out.setFeature(XML_INDENT_OUTPUT_FEATURE, true); 1056 out.startTag(null, "services"); 1057 1058 Log.d(TAG, "writeOthersLocked: userServices.size: " + mUserServices.size()); 1059 for (int i = 0; i < mUserServices.size(); i++) { 1060 final UserServices user = mUserServices.valueAt(i); 1061 int userId = mUserServices.keyAt(i); 1062 // Checking for 1 times 1063 Log.d(TAG, "writeOthersLocked: userId: " + userId); 1064 Log.d(TAG, "writeOthersLocked: others size: " + user.others.size()); 1065 ArrayList<ComponentName> currentService = new ArrayList<ComponentName>(); 1066 for (Map.Entry<ComponentName, OtherServiceStatus> service : user.others 1067 .entrySet()) { 1068 Log.d(TAG, "writeOthersLocked: component: " + service.getKey().flattenToString() 1069 + ", checked: " + service.getValue().checked); 1070 1071 boolean hasDupe = false; 1072 for (ComponentName cn : currentService) { 1073 if (cn.equals(service.getKey())) { 1074 hasDupe = true; 1075 break; 1076 } 1077 } 1078 if (hasDupe) { 1079 continue; 1080 } else { 1081 Log.d(TAG, "writeOthersLocked: Already written"); 1082 currentService.add(service.getKey()); 1083 } 1084 1085 out.startTag(null, "service"); 1086 out.attribute(null, "component", service.getKey().flattenToString()); 1087 out.attribute(null, "uid", Integer.toString(service.getValue().uid)); 1088 out.attribute(null, "checked", Boolean.toString(service.getValue().checked)); 1089 out.endTag(null, "service"); 1090 } 1091 } 1092 out.endTag(null, "services"); 1093 out.endDocument(); 1094 mOthersFile.finishWrite(fos); 1095 return true; 1096 } catch (Exception e) { 1097 Log.e(TAG, "writeOthersLocked: Error writing other status", e); 1098 if (fos != null) { 1099 mOthersFile.failWrite(fos); 1100 } 1101 return false; 1102 } 1103 } 1104 setOffHostSecureElement(int userId, int uid, ComponentName componentName, String offHostSE)1105 public boolean setOffHostSecureElement(int userId, int uid, ComponentName componentName, 1106 String offHostSE) { 1107 ArrayList<ApduServiceInfo> newServices = null; 1108 synchronized (mLock) { 1109 UserServices services = findOrCreateUserLocked(userId); 1110 // Check if we can find this service 1111 ApduServiceInfo serviceInfo = getService(userId, componentName); 1112 if (serviceInfo == null) { 1113 Log.e(TAG, "setOffHostSecureElement: Service " + componentName + " does not exist"); 1114 return false; 1115 } 1116 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1117 // This is probably a good indication something is wrong here. 1118 // Either newer service installed with different uid (but then 1119 // we should have known about it), or somebody calling us from 1120 // a different uid. 1121 Log.e(TAG, "setOffHostSecureElement: UID mismatch"); 1122 return false; 1123 } 1124 if (offHostSE == null || serviceInfo.isOnHost()) { 1125 Log.e(TAG, "setOffHostSecureElement: OffHostSE mismatch with Service type"); 1126 return false; 1127 } 1128 1129 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 1130 if (dynSettings == null) { 1131 dynSettings = new DynamicSettings(serviceInfo.getUid()); 1132 } 1133 dynSettings.offHostSE = offHostSE; 1134 boolean success = writeDynamicSettingsLocked(); 1135 if (!success) { 1136 Log.e(TAG, "setOffHostSecureElement: Failed to persist AID group"); 1137 dynSettings.offHostSE = null; 1138 return false; 1139 } 1140 1141 serviceInfo.setOffHostSecureElement(offHostSE); 1142 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1143 } 1144 // Make callback without the lock held 1145 mCallback.onServicesUpdated(userId, newServices, true); 1146 return true; 1147 } 1148 resetOffHostSecureElement(int userId, int uid, ComponentName componentName)1149 public boolean resetOffHostSecureElement(int userId, int uid, ComponentName componentName) { 1150 ArrayList<ApduServiceInfo> newServices = null; 1151 synchronized (mLock) { 1152 UserServices services = findOrCreateUserLocked(userId); 1153 // Check if we can find this service 1154 ApduServiceInfo serviceInfo = getService(userId, componentName); 1155 if (serviceInfo == null) { 1156 Log.e(TAG, 1157 "resetOffHostSecureElement: Service " + componentName + " does not exist"); 1158 return false; 1159 } 1160 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1161 // This is probably a good indication something is wrong here. 1162 // Either newer service installed with different uid (but then 1163 // we should have known about it), or somebody calling us from 1164 // a different uid. 1165 Log.e(TAG, "resetOffHostSecureElement: UID mismatch"); 1166 return false; 1167 } 1168 if (serviceInfo.isOnHost() || serviceInfo.getOffHostSecureElement() == null) { 1169 Log.e(TAG, "resetOffHostSecureElement: OffHostSE is not set"); 1170 return false; 1171 } 1172 1173 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 1174 String offHostSE = dynSettings.offHostSE; 1175 dynSettings.offHostSE = null; 1176 boolean success = writeDynamicSettingsLocked(); 1177 if (!success) { 1178 Log.e(TAG, "resetOffHostSecureElement: Failed to persist AID group"); 1179 dynSettings.offHostSE = offHostSE; 1180 return false; 1181 } 1182 1183 serviceInfo.resetOffHostSecureElement(); 1184 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1185 } 1186 // Make callback without the lock held 1187 mCallback.onServicesUpdated(userId, newServices, true); 1188 return true; 1189 } 1190 setShouldDefaultToObserveModeForService(int userId, int uid, ComponentName componentName, boolean enable)1191 public boolean setShouldDefaultToObserveModeForService(int userId, int uid, 1192 ComponentName componentName, boolean enable) { 1193 synchronized (mLock) { 1194 UserServices services = findOrCreateUserLocked(userId); 1195 // Check if we can find this service 1196 ApduServiceInfo serviceInfo = getService(userId, componentName); 1197 if (serviceInfo == null) { 1198 Log.e(TAG, "setShouldDefaultToObserveModeForService: Service " + componentName 1199 + " does not exist"); 1200 return false; 1201 } 1202 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1203 // This is probably a good indication something is wrong here. 1204 // Either newer service installed with different uid (but then 1205 // we should have known about it), or somebody calling us from 1206 // a different uid. 1207 Log.e(TAG, "setShouldDefaultToObserveModeForService UID mismatch"); 1208 return false; 1209 } 1210 serviceInfo.setShouldDefaultToObserveMode(enable); 1211 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 1212 if (dynSettings == null) { 1213 dynSettings = new DynamicSettings(serviceInfo.getUid()); 1214 dynSettings.offHostSE = null; 1215 services.dynamicSettings.put(componentName, dynSettings); 1216 } 1217 dynSettings.shouldDefaultToObserveModeStr = Boolean.toString(enable); 1218 } 1219 return true; 1220 } 1221 registerPollingLoopFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopFilter, boolean autoTransact)1222 public boolean registerPollingLoopFilterForService(int userId, int uid, 1223 ComponentName componentName, String pollingLoopFilter, 1224 boolean autoTransact) { 1225 ArrayList<ApduServiceInfo> newServices = null; 1226 synchronized (mLock) { 1227 UserServices services = findOrCreateUserLocked(userId); 1228 // Check if we can find this service 1229 ApduServiceInfo serviceInfo = getService(userId, componentName); 1230 if (serviceInfo == null) { 1231 Log.e(TAG, "registerPollingLoopFilterForService: Service " + componentName 1232 + " does not exist"); 1233 return false; 1234 } 1235 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1236 // This is probably a good indication something is wrong here. 1237 // Either newer service installed with different uid (but then 1238 // we should have known about it), or somebody calling us from 1239 // a different uid. 1240 Log.e(TAG, "registerPollingLoopFilterForService: UID mismatch"); 1241 return false; 1242 } 1243 if (!serviceInfo.isOnHost() && !autoTransact) { 1244 return false; 1245 } 1246 DynamicSettings dynamicSettings = 1247 getOrCreateSettings(services, componentName, serviceInfo.getUid()); 1248 dynamicSettings.pollingLoopFilters.put(pollingLoopFilter, 1249 autoTransact); 1250 serviceInfo.addPollingLoopFilter(pollingLoopFilter, autoTransact); 1251 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1252 } 1253 mCallback.onServicesUpdated(userId, newServices, true); 1254 return true; 1255 } 1256 removePollingLoopFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopFilter)1257 public boolean removePollingLoopFilterForService(int userId, int uid, 1258 ComponentName componentName, String pollingLoopFilter) { 1259 ArrayList<ApduServiceInfo> newServices = null; 1260 synchronized (mLock) { 1261 UserServices services = findOrCreateUserLocked(userId); 1262 // Check if we can find this service 1263 ApduServiceInfo serviceInfo = getService(userId, componentName); 1264 if (serviceInfo == null) { 1265 Log.e(TAG, "removePollingLoopFilterForService: Service " + componentName 1266 + " does not exist"); 1267 return false; 1268 } 1269 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1270 // This is probably a good indication something is wrong here. 1271 // Either newer service installed with different uid (but then 1272 // we should have known about it), or somebody calling us from 1273 // a different uid. 1274 Log.e(TAG, "removePollingLoopFilterForService: UID mismatch"); 1275 return false; 1276 } 1277 DynamicSettings dynamicSettings = 1278 getOrCreateSettings(services, componentName, serviceInfo.getUid()); 1279 dynamicSettings.pollingLoopFilters.remove(pollingLoopFilter); 1280 serviceInfo.removePollingLoopFilter(pollingLoopFilter); 1281 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1282 } 1283 mCallback.onServicesUpdated(userId, newServices, true); 1284 return true; 1285 } 1286 registerPollingLoopPatternFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopPatternFilter, boolean autoTransact)1287 public boolean registerPollingLoopPatternFilterForService(int userId, int uid, 1288 ComponentName componentName, String pollingLoopPatternFilter, 1289 boolean autoTransact) { 1290 ArrayList<ApduServiceInfo> newServices = null; 1291 synchronized (mLock) { 1292 UserServices services = findOrCreateUserLocked(userId); 1293 // Check if we can find this service 1294 ApduServiceInfo serviceInfo = getService(userId, componentName); 1295 if (serviceInfo == null) { 1296 Log.e(TAG, "removePollingLoopFilterForService: Service " + componentName 1297 + " does not exist"); 1298 return false; 1299 } 1300 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1301 // This is probably a good indication something is wrong here. 1302 // Either newer service installed with different uid (but then 1303 // we should have known about it), or somebody calling us from 1304 // a different uid. 1305 Log.e(TAG, "removePollingLoopFilterForService: UID mismatch"); 1306 return false; 1307 } 1308 if (!serviceInfo.isOnHost() && !autoTransact) { 1309 return false; 1310 } 1311 DynamicSettings dynamicSettings = 1312 getOrCreateSettings(services, componentName, serviceInfo.getUid()); 1313 dynamicSettings.pollingLoopPatternFilters 1314 .put(pollingLoopPatternFilter, autoTransact); 1315 serviceInfo.addPollingLoopPatternFilter(pollingLoopPatternFilter, autoTransact); 1316 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1317 } 1318 mCallback.onServicesUpdated(userId, newServices, true); 1319 return true; 1320 } 1321 removePollingLoopPatternFilterForService(int userId, int uid, ComponentName componentName, String pollingLoopPatternFilter)1322 public boolean removePollingLoopPatternFilterForService(int userId, int uid, 1323 ComponentName componentName, String pollingLoopPatternFilter) { 1324 ArrayList<ApduServiceInfo> newServices = null; 1325 synchronized (mLock) { 1326 UserServices services = findOrCreateUserLocked(userId); 1327 // Check if we can find this service 1328 ApduServiceInfo serviceInfo = getService(userId, componentName); 1329 if (serviceInfo == null) { 1330 Log.e(TAG, "removePollingLoopPatternFilterForService: Service " + componentName 1331 + " does not exist"); 1332 return false; 1333 } 1334 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1335 // This is probably a good indication something is wrong here. 1336 // Either newer service installed with different uid (but then 1337 // we should have known about it), or somebody calling us from 1338 // a different uid. 1339 Log.e(TAG, "removePollingLoopPatternFilterForService: UID mismatch"); 1340 return false; 1341 } 1342 DynamicSettings dynamicSettings = 1343 getOrCreateSettings(services, componentName, serviceInfo.getUid()); 1344 dynamicSettings.pollingLoopPatternFilters.remove(pollingLoopPatternFilter); 1345 serviceInfo.removePollingLoopPatternFilter(pollingLoopPatternFilter); 1346 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1347 } 1348 mCallback.onServicesUpdated(userId, newServices, true); 1349 return true; 1350 } 1351 1352 1353 registerAidGroupForService(int userId, int uid, ComponentName componentName, AidGroup aidGroup)1354 public boolean registerAidGroupForService(int userId, int uid, 1355 ComponentName componentName, AidGroup aidGroup) { 1356 ArrayList<ApduServiceInfo> newServices = null; 1357 boolean success; 1358 synchronized (mLock) { 1359 UserServices services = findOrCreateUserLocked(userId); 1360 // Check if we can find this service 1361 ApduServiceInfo serviceInfo = getService(userId, componentName); 1362 if (serviceInfo == null) { 1363 Log.e(TAG, 1364 "registerAidGroupForService: Service " + componentName + " does not exist"); 1365 return false; 1366 } 1367 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1368 // This is probably a good indication something is wrong here. 1369 // Either newer service installed with different uid (but then 1370 // we should have known about it), or somebody calling us from 1371 // a different uid. 1372 Log.e(TAG, "registerAidGroupForService: UID mismatch"); 1373 return false; 1374 } 1375 // Do another AID validation, since a caller could have thrown in a 1376 // modified AidGroup object with invalid AIDs over Binder. 1377 List<String> aids = aidGroup.getAids(); 1378 for (String aid : aids) { 1379 if (!CardEmulation.isValidAid(aid)) { 1380 Log.e(TAG, "registerAidGroupForService: AID " + aid + " is not a valid AID"); 1381 return false; 1382 } 1383 } 1384 serviceInfo.setDynamicAidGroup(aidGroup); 1385 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 1386 if (dynSettings == null) { 1387 dynSettings = new DynamicSettings(serviceInfo.getUid()); 1388 dynSettings.offHostSE = null; 1389 services.dynamicSettings.put(componentName, dynSettings); 1390 } 1391 dynSettings.aidGroups.put(aidGroup.getCategory(), aidGroup); 1392 success = writeDynamicSettingsLocked(); 1393 if (success) { 1394 newServices = 1395 new ArrayList<ApduServiceInfo>(services.services.values()); 1396 } else { 1397 Log.e(TAG, "registerAidGroupForService: Failed to persist AID group"); 1398 // Undo registration 1399 dynSettings.aidGroups.remove(aidGroup.getCategory()); 1400 } 1401 } 1402 if (success) { 1403 List<ApduServiceInfo> otherServices = getServicesForCategory(userId, 1404 CardEmulation.CATEGORY_OTHER); 1405 invalidateOther(userId, otherServices); 1406 // Make callback without the lock held 1407 mCallback.onServicesUpdated(userId, newServices, true); 1408 } 1409 return success; 1410 } 1411 registerOtherForService(int userId, ComponentName componentName, boolean checked)1412 public int registerOtherForService(int userId, 1413 ComponentName componentName, boolean checked) { 1414 if (DEBUG) { 1415 Log.d(TAG, "registerOtherForService: checked:" + checked + ", " + componentName); 1416 } 1417 1418 ArrayList<ApduServiceInfo> newServices = null; 1419 int success = SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR; 1420 1421 synchronized (mLock) { 1422 1423 Log.d(TAG, "registerOtherForService: / ComponentName" + componentName); 1424 ApduServiceInfo serviceInfo = getService(userId, componentName); 1425 1426 if (serviceInfo == null) { 1427 Log.e(TAG, "registerOtherForService: Service " + componentName + "does not exist"); 1428 return SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE; 1429 } 1430 1431 success = updateOtherServiceStatus(userId, serviceInfo, checked) 1432 ? SET_SERVICE_ENABLED_STATUS_OK 1433 : SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET; 1434 1435 if (success == SET_SERVICE_ENABLED_STATUS_OK) { 1436 UserServices userService = findOrCreateUserLocked(userId); 1437 newServices = new ArrayList<ApduServiceInfo>(userService.services.values()); 1438 } else { 1439 Log.e(TAG, "registerOtherForService: Fail to other checked"); 1440 } 1441 } 1442 1443 if (success == SET_SERVICE_ENABLED_STATUS_OK) { 1444 if (DEBUG) { 1445 Log.d(TAG, "registerOtherForService: other list update due to User Select " 1446 + componentName); 1447 } 1448 mCallback.onServicesUpdated(userId, Collections.unmodifiableList(newServices),false); 1449 } 1450 1451 return success; 1452 } 1453 getAidGroupForService(int userId, int uid, ComponentName componentName, String category)1454 public AidGroup getAidGroupForService(int userId, int uid, ComponentName componentName, 1455 String category) { 1456 ApduServiceInfo serviceInfo = getService(userId, componentName); 1457 if (serviceInfo != null) { 1458 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1459 Log.e(TAG, "getAidGroupForService: UID mismatch"); 1460 return null; 1461 } 1462 return serviceInfo.getDynamicAidGroupForCategory(category); 1463 } else { 1464 Log.e(TAG, "getAidGroupForService: Could not find service " + componentName); 1465 return null; 1466 } 1467 } 1468 removeAidGroupForService(int userId, int uid, ComponentName componentName, String category)1469 public boolean removeAidGroupForService(int userId, int uid, ComponentName componentName, 1470 String category) { 1471 boolean success = false; 1472 ArrayList<ApduServiceInfo> newServices = null; 1473 synchronized (mLock) { 1474 UserServices services = findOrCreateUserLocked(userId); 1475 ApduServiceInfo serviceInfo = getService(userId, componentName); 1476 if (serviceInfo != null) { 1477 if (!NfcInjector.isPrivileged(uid) && serviceInfo.getUid() != uid) { 1478 // Calling from different uid 1479 Log.e(TAG, "removeAidGroupForService: UID mismatch"); 1480 return false; 1481 } 1482 if (!serviceInfo.removeDynamicAidGroupForCategory(category)) { 1483 Log.e(TAG, "removeAidGroupForService: Could not find dynamic AIDs for category " 1484 + category); 1485 return false; 1486 } 1487 // Remove from local cache 1488 DynamicSettings dynSettings = services.dynamicSettings.get(componentName); 1489 if (dynSettings != null) { 1490 AidGroup deletedGroup = dynSettings.aidGroups.remove(category); 1491 success = writeDynamicSettingsLocked(); 1492 if (success) { 1493 newServices = new ArrayList<ApduServiceInfo>(services.services.values()); 1494 } else { 1495 Log.e(TAG, "removeAidGroupForService: Could not persist deleted AID group"); 1496 dynSettings.aidGroups.put(category, deletedGroup); 1497 return false; 1498 } 1499 } else { 1500 Log.e(TAG, "removeAidGroupForService: Could not find aid group in " 1501 + "local cache"); 1502 } 1503 } else { 1504 Log.e(TAG, 1505 "removeAidGroupForService: Service " + componentName + " does not exist"); 1506 } 1507 } 1508 if (success) { 1509 List<ApduServiceInfo> otherServices = getServicesForCategory(userId, 1510 CardEmulation.CATEGORY_OTHER); 1511 invalidateOther(userId, otherServices); 1512 mCallback.onServicesUpdated(userId, newServices, true); 1513 } 1514 return success; 1515 } 1516 doesServiceShouldDefaultToObserveMode(int userId, ComponentName service)1517 boolean doesServiceShouldDefaultToObserveMode(int userId, ComponentName service) { 1518 UserServices services = findOrCreateUserLocked(userId); 1519 ApduServiceInfo serviceInfo = services.services.get(service); 1520 if (serviceInfo == null) { 1521 Log.d(TAG, "removeAidGroupForService: serviceInfo is null"); 1522 return false; 1523 } 1524 return serviceInfo.shouldDefaultToObserveMode(); 1525 } 1526 updateOtherServiceStatus(int userId, ApduServiceInfo service, boolean checked)1527 private boolean updateOtherServiceStatus(int userId, ApduServiceInfo service, boolean checked) { 1528 UserServices userServices = findOrCreateUserLocked(userId); 1529 1530 OtherServiceStatus status = userServices.others.get(service.getComponent()); 1531 // This is Error handling code if otherServiceStatus is null 1532 if (status == null) { 1533 Log.d(TAG, "updateOtherServiceStatus: " + service.getComponent() + " status is null"); 1534 return false; 1535 } 1536 1537 if (service.isCategoryOtherServiceEnabled() == checked) { 1538 Log.d(TAG, "updateOtherServiceStatus: already same status: " + checked); 1539 return false; 1540 } 1541 1542 service.setCategoryOtherServiceEnabled(checked); 1543 status.checked = checked; 1544 1545 return writeOthersLocked(); 1546 } 1547 getOrCreateSettings(UserServices services, ComponentName componentName, int uid)1548 private DynamicSettings getOrCreateSettings(UserServices services, ComponentName componentName, 1549 int uid) { 1550 if (!services.dynamicSettings.containsKey(componentName)) { 1551 services.dynamicSettings.put(componentName, new DynamicSettings(uid)); 1552 } 1553 return services.dynamicSettings.get(componentName); 1554 } 1555 dump(FileDescriptor fd, PrintWriter pw, String[] args)1556 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1557 pw.println("Registered HCE services for current user: "); 1558 ParcelFileDescriptor pFd; 1559 try { 1560 pFd = ParcelFileDescriptor.dup(fd); 1561 synchronized (mLock) { 1562 for (UserHandle uh : mUserHandles) { 1563 UserManager um = mContext.createContextAsUser( 1564 uh, /*flags=*/0).getSystemService(UserManager.class); 1565 pw.println("User " + Utils.maskSubstring(um.getUserName(), 3)); 1566 UserServices userServices = findOrCreateUserLocked(uh.getIdentifier()); 1567 for (ApduServiceInfo service : userServices.services.values()) { 1568 service.dump(pFd, pw, args); 1569 pw.println(""); 1570 } 1571 pw.println(""); 1572 } 1573 } 1574 pFd.close(); 1575 } catch (IOException e) { 1576 pw.println("Failed to dump HCE services: " + e); 1577 } 1578 } 1579 1580 /** 1581 * Dump debugging information as a RegisteredServicesCacheProto 1582 * 1583 * Note: 1584 * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto 1585 * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and 1586 * {@link ProtoOutputStream#end(long)} after. 1587 * Never reuse a proto field number. When removing a field, mark it as reserved. 1588 */ dumpDebug(ProtoOutputStream proto)1589 void dumpDebug(ProtoOutputStream proto) { 1590 UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser()); 1591 for (ApduServiceInfo service : userServices.services.values()) { 1592 long token = proto.start(RegisteredServicesCacheProto.APDU_SERVICE_INFOS); 1593 service.dumpDebug(proto); 1594 proto.end(token); 1595 } 1596 } 1597 } 1598