1 /* 2 * Copyright (C) 2015 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 android.app.ActivityManager; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.content.pm.PackageManager.NameNotFoundException; 27 import android.content.pm.PackageManager.ResolveInfoFlags; 28 import android.content.pm.ResolveInfo; 29 import android.content.pm.ServiceInfo; 30 import android.nfc.cardemulation.HostNfcFService; 31 import android.nfc.cardemulation.NfcFCardEmulation; 32 import android.nfc.cardemulation.NfcFServiceInfo; 33 import android.os.ParcelFileDescriptor; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.sysprop.NfcProperties; 37 import android.util.AtomicFile; 38 import android.util.Log; 39 import android.util.SparseArray; 40 import android.util.Xml; 41 import android.util.proto.ProtoOutputStream; 42 43 import androidx.annotation.VisibleForTesting; 44 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.nfc.Utils; 47 48 import org.xmlpull.v1.XmlPullParser; 49 import org.xmlpull.v1.XmlPullParserException; 50 import org.xmlpull.v1.XmlSerializer; 51 52 import java.io.File; 53 import java.io.FileDescriptor; 54 import java.io.FileInputStream; 55 import java.io.FileOutputStream; 56 import java.io.IOException; 57 import java.io.PrintWriter; 58 import java.util.ArrayList; 59 import java.util.Collections; 60 import java.util.HashMap; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.concurrent.atomic.AtomicReference; 64 65 public class RegisteredNfcFServicesCache { 66 static final String XML_INDENT_OUTPUT_FEATURE = "http://xmlpull.org/v1/doc/features.html#indent-output"; 67 static final String TAG = "RegisteredNfcFServicesCache"; 68 static final boolean DBG = NfcProperties.debug_enabled().orElse(true); 69 private static final boolean VDBG = false; // turn on for local testing. 70 71 final Context mContext; 72 final AtomicReference<BroadcastReceiver> mReceiver; 73 74 final Object mLock = new Object(); 75 // All variables below synchronized on mLock 76 77 // mUserHandles holds the UserHandles of all the profiles that belong to current user 78 @GuardedBy("mLock") 79 List<UserHandle> mUserHandles; 80 81 // mUserServices holds the card emulation services that are running for each user 82 final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>(); 83 final Callback mCallback; 84 final AtomicFile mDynamicSystemCodeNfcid2File; 85 boolean mActivated = false; 86 boolean mUserSwitched = false; 87 88 public interface Callback { onNfcFServicesUpdated(int userId, final List<NfcFServiceInfo> services)89 void onNfcFServicesUpdated(int userId, final List<NfcFServiceInfo> services); 90 }; 91 92 static class DynamicSystemCode { 93 public final int uid; 94 public final String systemCode; 95 DynamicSystemCode(int uid, String systemCode)96 DynamicSystemCode(int uid, String systemCode) { 97 this.uid = uid; 98 this.systemCode = systemCode; 99 } 100 }; 101 102 static class DynamicNfcid2 { 103 public final int uid; 104 public final String nfcid2; 105 DynamicNfcid2(int uid, String nfcid2)106 DynamicNfcid2(int uid, String nfcid2) { 107 this.uid = uid; 108 this.nfcid2 = nfcid2; 109 } 110 }; 111 112 @VisibleForTesting 113 static class UserServices { 114 /** 115 * All services that have registered 116 */ 117 final HashMap<ComponentName, NfcFServiceInfo> services = 118 new HashMap<>(); // Re-built at run-time 119 final HashMap<ComponentName, DynamicSystemCode> dynamicSystemCode = 120 new HashMap<>(); // In memory cache of dynamic System Code store 121 final HashMap<ComponentName, DynamicNfcid2> dynamicNfcid2 = 122 new HashMap<>(); // In memory cache of dynamic NFCID2 store 123 }; 124 findOrCreateUserLocked(int userId)125 private UserServices findOrCreateUserLocked(int userId) { 126 UserServices userServices = mUserServices.get(userId); 127 if (userServices == null) { 128 userServices = new UserServices(); 129 mUserServices.put(userId, userServices); 130 } 131 return userServices; 132 } 133 getProfileParentId(Context context, int userId)134 private int getProfileParentId(Context context, int userId) { 135 UserManager um = context.getSystemService(UserManager.class); 136 UserHandle uh = um.getProfileParent(UserHandle.of(userId)); 137 return uh == null ? userId : uh.getIdentifier(); 138 } 139 getProfileParentId(int userId)140 private int getProfileParentId(int userId) { 141 return getProfileParentId(mContext.createContextAsUser( 142 UserHandle.of(userId), /*flags=*/0), userId); 143 } 144 RegisteredNfcFServicesCache(Context context, Callback callback)145 public RegisteredNfcFServicesCache(Context context, Callback callback) { 146 this(context, callback, null); 147 } 148 149 @VisibleForTesting RegisteredNfcFServicesCache(Context context, Callback callback, AtomicFile atomicFile)150 RegisteredNfcFServicesCache(Context context, Callback callback, AtomicFile atomicFile) { 151 mContext = context; 152 mCallback = callback; 153 154 refreshUserProfilesLocked(); 155 156 final BroadcastReceiver receiver = new BroadcastReceiver() { 157 @Override 158 public void onReceive(Context context, Intent intent) { 159 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 160 String action = intent.getAction(); 161 if (VDBG) Log.d(TAG, "onReceive: Intent action: " + action); 162 if (uid == -1) return; 163 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 164 int currentUser = ActivityManager.getCurrentUser(); 165 if (currentUser != getProfileParentId(context, userId)) { 166 // Cache will automatically be updated on user switch 167 if (VDBG) { 168 Log.d(TAG, 169 "onReceive: Ignoring package change intent from non-current user"); 170 } 171 return; 172 } 173 // If app not removed, check if the app has any valid CE services. 174 if (!Intent.ACTION_PACKAGE_REMOVED.equals(action) && 175 !Utils.hasCeServicesWithValidPermissions(mContext, intent, userId)) { 176 if (VDBG) { 177 Log.d(TAG, "onReceive: Ignoring package change intent from non-CE app"); 178 } 179 return; 180 } 181 boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) && 182 (Intent.ACTION_PACKAGE_ADDED.equals(action) || 183 Intent.ACTION_PACKAGE_REMOVED.equals(action)); 184 if (!replaced) { 185 invalidateCache(UserHandle.getUserHandleForUid(uid).getIdentifier()); 186 } else { 187 if (DBG) Log.d(TAG, 188 "onReceive: Ignoring package intent due to package being replaced."); 189 } 190 } 191 }; 192 mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 193 194 IntentFilter intentFilter = new IntentFilter(); 195 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 196 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 197 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 198 intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 199 intentFilter.addAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH); 200 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 201 intentFilter.addDataScheme("package"); 202 mContext.registerReceiverForAllUsers(mReceiver.get(), intentFilter, null, null); 203 204 // Register for events related to sdcard operations 205 IntentFilter sdFilter = new IntentFilter(); 206 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 207 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 208 mContext.registerReceiverForAllUsers(mReceiver.get(), sdFilter, null, null); 209 210 File dataDir = mContext.getFilesDir(); 211 if (atomicFile == null) { 212 mDynamicSystemCodeNfcid2File = 213 new AtomicFile(new File(dataDir, "dynamic_systemcode_nfcid2.xml")); 214 } else { 215 mDynamicSystemCodeNfcid2File = atomicFile; 216 } 217 } 218 initialize()219 void initialize() { 220 synchronized (mLock) { 221 readDynamicSystemCodeNfcid2Locked(); 222 for (UserHandle uh : mUserHandles) { 223 invalidateCache(uh.getIdentifier()); 224 } 225 } 226 } 227 dump(ArrayList<NfcFServiceInfo> services)228 void dump(ArrayList<NfcFServiceInfo> services) { 229 for (NfcFServiceInfo service : services) { 230 Log.d(TAG, service.toString()); 231 } 232 } 233 containsServiceLocked(ArrayList<NfcFServiceInfo> services, ComponentName componentName)234 boolean containsServiceLocked(ArrayList<NfcFServiceInfo> services, 235 ComponentName componentName) { 236 for (NfcFServiceInfo service : services) { 237 if (service.getComponent().equals(componentName)) return true; 238 } 239 return false; 240 } 241 hasService(int userId, ComponentName componentName)242 public boolean hasService(int userId, ComponentName componentName) { 243 return getService(userId, componentName) != null; 244 } 245 getService(int userId, ComponentName componentName)246 public NfcFServiceInfo getService(int userId, ComponentName componentName) { 247 synchronized (mLock) { 248 UserServices userServices = findOrCreateUserLocked(userId); 249 return userServices.services.get(componentName); 250 } 251 } 252 getServices(int userId)253 public List<NfcFServiceInfo> getServices(int userId) { 254 final ArrayList<NfcFServiceInfo> services = new ArrayList<NfcFServiceInfo>(); 255 synchronized (mLock) { 256 UserServices userServices = findOrCreateUserLocked(userId); 257 services.addAll(userServices.services.values()); 258 } 259 return services; 260 } 261 getInstalledServices(int userId)262 ArrayList<NfcFServiceInfo> getInstalledServices(int userId) { 263 if (DBG) Log.d(TAG, "getInstalledServices"); 264 PackageManager pm; 265 try { 266 pm = mContext.createPackageContextAsUser("android", 0, 267 UserHandle.of(userId)).getPackageManager(); 268 } catch (NameNotFoundException e) { 269 Log.e(TAG, "getInstalledServices: Could not create user package context"); 270 return null; 271 } 272 273 ArrayList<NfcFServiceInfo> validServices = new ArrayList<NfcFServiceInfo>(); 274 275 List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser( 276 new Intent(HostNfcFService.SERVICE_INTERFACE), 277 ResolveInfoFlags.of(PackageManager.GET_META_DATA), UserHandle.of(userId)); 278 279 for (ResolveInfo resolvedService : resolvedServices) { 280 try { 281 ServiceInfo si = resolvedService.serviceInfo; 282 ComponentName componentName = new ComponentName(si.packageName, si.name); 283 // Check if the package holds the NFC permission 284 if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) != 285 PackageManager.PERMISSION_GRANTED) { 286 Log.e(TAG, 287 "getInstalledServices: Skipping NfcF service " + componentName 288 + ": it does not require the permission " 289 + android.Manifest.permission.NFC); 290 continue; 291 } 292 if (!android.Manifest.permission.BIND_NFC_SERVICE.equals( 293 si.permission)) { 294 Log.e(TAG, 295 "getInstalledServices: Skipping NfcF service " + componentName 296 + ": it does not require the permission " 297 + android.Manifest.permission.BIND_NFC_SERVICE); 298 continue; 299 } 300 NfcFServiceInfo service = new NfcFServiceInfo(pm, resolvedService); 301 if (service != null) { 302 validServices.add(service); 303 } 304 } catch (XmlPullParserException e) { 305 Log.w(TAG, "getInstalledServices: Unable to load component info " 306 + resolvedService.toString(), e); 307 } catch (IOException e) { 308 Log.w(TAG, "getInstalledServices: Unable to load component info " 309 + resolvedService.toString(), e); 310 } 311 } 312 313 return validServices; 314 } 315 invalidateCache(int userId)316 public void invalidateCache(int userId) { 317 if (DBG) Log.d(TAG, "invalidateCache"); 318 final ArrayList<NfcFServiceInfo> validServices = getInstalledServices(userId); 319 if (validServices == null) { 320 return; 321 } 322 ArrayList<NfcFServiceInfo> newServices = null; 323 ArrayList<NfcFServiceInfo> toBeAdded = new ArrayList<>(); 324 ArrayList<NfcFServiceInfo> toBeRemoved = new ArrayList<>(); 325 synchronized (mLock) { 326 UserServices userServices = findOrCreateUserLocked(userId); 327 328 // Check update 329 ArrayList<NfcFServiceInfo> cachedServices = 330 new ArrayList<NfcFServiceInfo>(userServices.services.values()); 331 boolean matched = false; 332 for (NfcFServiceInfo validService : validServices) { 333 for (NfcFServiceInfo cachedService : cachedServices) { 334 if (validService.equals(cachedService)) { 335 matched = true; 336 break; 337 } 338 } 339 if (!matched) { 340 toBeAdded.add(validService); 341 } 342 matched = false; 343 } 344 for (NfcFServiceInfo cachedService : cachedServices) { 345 for (NfcFServiceInfo validService : validServices) { 346 if (cachedService.equals(validService)) { 347 matched = true; 348 break; 349 } 350 } 351 if (!matched) { 352 toBeRemoved.add(cachedService); 353 } 354 matched = false; 355 } 356 if (mUserSwitched) { 357 Log.d(TAG, "invalidateCache: User switched, rebuild internal cache"); 358 mUserSwitched = false; 359 } else if (toBeAdded.size() == 0 && toBeRemoved.size() == 0) { 360 Log.d(TAG, "invalidateCache: Service unchanged, not updating"); 361 return; 362 } 363 364 // Update cache 365 for (NfcFServiceInfo service : toBeAdded) { 366 userServices.services.put(service.getComponent(), service); 367 } 368 for (NfcFServiceInfo service : toBeRemoved) { 369 userServices.services.remove(service.getComponent()); 370 } 371 // Apply dynamic System Code mappings 372 ArrayList<ComponentName> toBeRemovedDynamicSystemCode = 373 new ArrayList<ComponentName>(); 374 for (Map.Entry<ComponentName, DynamicSystemCode> entry : 375 userServices.dynamicSystemCode.entrySet()) { 376 // Verify component / uid match 377 ComponentName componentName = entry.getKey(); 378 DynamicSystemCode dynamicSystemCode = entry.getValue(); 379 NfcFServiceInfo service = userServices.services.get(componentName); 380 if (service == null || (service.getUid() != dynamicSystemCode.uid)) { 381 toBeRemovedDynamicSystemCode.add(componentName); 382 continue; 383 } else { 384 service.setDynamicSystemCode(dynamicSystemCode.systemCode); 385 } 386 } 387 // Apply dynamic NFCID2 mappings 388 ArrayList<ComponentName> toBeRemovedDynamicNfcid2 = 389 new ArrayList<ComponentName>(); 390 for (Map.Entry<ComponentName, DynamicNfcid2> entry : 391 userServices.dynamicNfcid2.entrySet()) { 392 // Verify component / uid match 393 ComponentName componentName = entry.getKey(); 394 DynamicNfcid2 dynamicNfcid2 = entry.getValue(); 395 NfcFServiceInfo service = userServices.services.get(componentName); 396 if (service == null || (service.getUid() != dynamicNfcid2.uid)) { 397 toBeRemovedDynamicNfcid2.add(componentName); 398 continue; 399 } else { 400 service.setDynamicNfcid2(dynamicNfcid2.nfcid2); 401 } 402 } 403 for (ComponentName removedComponent : toBeRemovedDynamicSystemCode) { 404 Log.d(TAG, "invalidateCache: Removing dynamic System Code registered by " 405 + removedComponent); 406 userServices.dynamicSystemCode.remove(removedComponent); 407 } 408 for (ComponentName removedComponent : toBeRemovedDynamicNfcid2) { 409 Log.d(TAG, "invalidateCache: Removing dynamic NFCID2 registered by " 410 + removedComponent); 411 userServices.dynamicNfcid2.remove(removedComponent); 412 } 413 // Assign a NFCID2 for services requesting a random NFCID2, then apply 414 boolean nfcid2Assigned = false; 415 for (Map.Entry<ComponentName, NfcFServiceInfo> entry : 416 userServices.services.entrySet()) { 417 NfcFServiceInfo service = entry.getValue(); 418 if (service.getNfcid2().equalsIgnoreCase("RANDOM")) { 419 String randomNfcid2 = generateRandomNfcid2(); 420 service.setDynamicNfcid2(randomNfcid2); 421 DynamicNfcid2 dynamicNfcid2 = 422 new DynamicNfcid2(service.getUid(), randomNfcid2); 423 userServices.dynamicNfcid2.put(entry.getKey(), dynamicNfcid2); 424 nfcid2Assigned = true; 425 } 426 } 427 428 // Persist to filesystem 429 if (toBeRemovedDynamicSystemCode.size() > 0 || 430 toBeRemovedDynamicNfcid2.size() > 0 || 431 nfcid2Assigned) { 432 writeDynamicSystemCodeNfcid2Locked(); 433 } 434 435 newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values()); 436 } 437 mCallback.onNfcFServicesUpdated(userId, Collections.unmodifiableList(newServices)); 438 if (VDBG) { 439 Log.i(TAG, "invalidateCache: Services => "); 440 dump(newServices); 441 } else { 442 // dump only new services added or removed 443 Log.i(TAG, "invalidateCache: New Services => "); 444 dump(toBeAdded); 445 Log.i(TAG, "invalidateCache: Removed Services => "); 446 dump(toBeRemoved); 447 } 448 } 449 450 @VisibleForTesting readDynamicSystemCodeNfcid2Locked()451 void readDynamicSystemCodeNfcid2Locked() { 452 if (DBG) Log.d(TAG, "readDynamicSystemCodeNfcid2Locked"); 453 FileInputStream fis = null; 454 try { 455 if (!mDynamicSystemCodeNfcid2File.getBaseFile().exists()) { 456 Log.d(TAG, "readDynamicSystemCodeNfcid2Locked: Dynamic System Code, " 457 + "NFCID2 file does not exist"); 458 return; 459 } 460 fis = mDynamicSystemCodeNfcid2File.openRead(); 461 XmlPullParser parser = Xml.newPullParser(); 462 parser.setInput(fis, null); 463 int eventType = parser.getEventType(); 464 while (eventType != XmlPullParser.START_TAG && 465 eventType != XmlPullParser.END_DOCUMENT) { 466 eventType = parser.next(); 467 } 468 String tagName = parser.getName(); 469 if ("services".equals(tagName)) { 470 ComponentName componentName = null; 471 int currentUid = -1; 472 String systemCode = null; 473 String nfcid2 = null; 474 String description = null; 475 while (eventType != XmlPullParser.END_DOCUMENT) { 476 tagName = parser.getName(); 477 if (eventType == XmlPullParser.START_TAG) { 478 if ("service".equals(tagName) && parser.getDepth() == 2) { 479 String compString = 480 parser.getAttributeValue(null, "component"); 481 String uidString = 482 parser.getAttributeValue(null, "uid"); 483 if (compString == null || uidString == null) { 484 Log.e(TAG, "readDynamicSystemCodeNfcid2Locked: " 485 + "Invalid service attributes"); 486 } else { 487 try { 488 componentName = ComponentName.unflattenFromString(compString); 489 currentUid = Integer.parseInt(uidString); 490 systemCode = parser.getAttributeValue(null, "system-code"); 491 description = parser.getAttributeValue(null, "description"); 492 nfcid2 = parser.getAttributeValue(null, "nfcid2"); 493 } catch (NumberFormatException e) { 494 Log.e(TAG, "readDynamicSystemCodeNfcid2Locked: " 495 + "Could not parse service uid"); 496 } 497 } 498 } 499 } else if (eventType == XmlPullParser.END_TAG) { 500 if ("service".equals(tagName)) { 501 // See if we have a valid service 502 if (componentName != null && currentUid >= 0) { 503 final int userId = UserHandle. 504 getUserHandleForUid(currentUid).getIdentifier(); 505 UserServices userServices = findOrCreateUserLocked(userId); 506 if (systemCode != null) { 507 DynamicSystemCode dynamicSystemCode = 508 new DynamicSystemCode(currentUid, systemCode); 509 userServices.dynamicSystemCode.put( 510 componentName, dynamicSystemCode); 511 } 512 if (nfcid2 != null) { 513 DynamicNfcid2 dynamicNfcid2 = 514 new DynamicNfcid2(currentUid, nfcid2); 515 userServices.dynamicNfcid2.put( 516 componentName, dynamicNfcid2); 517 } 518 } 519 componentName = null; 520 currentUid = -1; 521 systemCode = null; 522 description = null; 523 nfcid2 = null; 524 } 525 } 526 eventType = parser.next(); 527 }; 528 } 529 } catch (Exception e) { 530 Log.e(TAG, "readDynamicSystemCodeNfcid2Locked: Could not parse dynamic System Code, " 531 + "NFCID2 file, trashing"); 532 mDynamicSystemCodeNfcid2File.delete(); 533 } finally { 534 if (fis != null) { 535 try { 536 fis.close(); 537 } catch (IOException e) { 538 } 539 } 540 } 541 } 542 543 @VisibleForTesting writeDynamicSystemCodeNfcid2Locked()544 boolean writeDynamicSystemCodeNfcid2Locked() { 545 if (DBG) Log.d(TAG, "writeDynamicSystemCodeNfcid2Locked"); 546 FileOutputStream fos = null; 547 try { 548 fos = mDynamicSystemCodeNfcid2File.startWrite(); 549 XmlSerializer out = Xml.newSerializer(); 550 out.setOutput(fos, "utf-8"); 551 out.startDocument(null, true); 552 out.setFeature(XML_INDENT_OUTPUT_FEATURE, true); 553 out.startTag(null, "services"); 554 for (int i = 0; i < mUserServices.size(); i++) { 555 final UserServices userServices = mUserServices.valueAt(i); 556 for (Map.Entry<ComponentName, DynamicSystemCode> entry : 557 userServices.dynamicSystemCode.entrySet()) { 558 out.startTag(null, "service"); 559 out.attribute(null, "component", entry.getKey().flattenToString()); 560 out.attribute(null, "uid", Integer.toString(entry.getValue().uid)); 561 out.attribute(null, "system-code", entry.getValue().systemCode); 562 if (userServices.dynamicNfcid2.containsKey(entry.getKey())) { 563 out.attribute(null, "nfcid2", 564 userServices.dynamicNfcid2.get(entry.getKey()).nfcid2); 565 } 566 out.endTag(null, "service"); 567 } 568 for (Map.Entry<ComponentName, DynamicNfcid2> entry : 569 userServices.dynamicNfcid2.entrySet()) { 570 if (!userServices.dynamicSystemCode.containsKey(entry.getKey())) { 571 out.startTag(null, "service"); 572 out.attribute(null, "component", entry.getKey().flattenToString()); 573 out.attribute(null, "uid", Integer.toString(entry.getValue().uid)); 574 out.attribute(null, "nfcid2", entry.getValue().nfcid2); 575 out.endTag(null, "service"); 576 } 577 } 578 } 579 out.endTag(null, "services"); 580 out.endDocument(); 581 mDynamicSystemCodeNfcid2File.finishWrite(fos); 582 return true; 583 } catch (Exception e) { 584 Log.e(TAG, 585 "writeDynamicSystemCodeNfcid2Locked: " 586 + "Error writing dynamic System Code, NFCID2", 587 e); 588 if (fos != null) { 589 mDynamicSystemCodeNfcid2File.failWrite(fos); 590 } 591 return false; 592 } 593 } 594 registerSystemCodeForService(int userId, int uid, ComponentName componentName, String systemCode)595 public boolean registerSystemCodeForService(int userId, int uid, 596 ComponentName componentName, String systemCode) { 597 if (DBG) Log.d(TAG, "registerSystemCodeForService"); 598 ArrayList<NfcFServiceInfo> newServices = null; 599 boolean success; 600 synchronized (mLock) { 601 if (mActivated) { 602 Log.d(TAG, "registerSystemCodeForService: failed to register " 603 + "System Code during activation"); 604 return false; 605 } 606 UserServices userServices = findOrCreateUserLocked(userId); 607 // Check if we can find this service 608 NfcFServiceInfo service = getService(userId, componentName); 609 if (service == null) { 610 Log.e(TAG, "registerSystemCodeForService: Service " + componentName 611 + " does not exist."); 612 return false; 613 } 614 if (service.getUid() != uid) { 615 // This is probably a good indication something is wrong here. 616 // Either newer service installed with different uid (but then 617 // we should have known about it), or somebody calling us from 618 // a different uid. 619 Log.e(TAG, "registerSystemCodeForService: UID mismatch."); 620 return false; 621 } 622 if (!systemCode.equalsIgnoreCase("NULL") && 623 !NfcFCardEmulation.isValidSystemCode(systemCode)) { 624 Log.e(TAG, "registerSystemCodeForService: System Code " + systemCode 625 + " is not a valid System Code"); 626 return false; 627 } 628 // Apply dynamic System Code mappings 629 systemCode = systemCode.toUpperCase(); 630 DynamicSystemCode oldDynamicSystemCode = 631 userServices.dynamicSystemCode.get(componentName); 632 DynamicSystemCode dynamicSystemCode = new DynamicSystemCode(uid, systemCode); 633 userServices.dynamicSystemCode.put(componentName, dynamicSystemCode); 634 success = writeDynamicSystemCodeNfcid2Locked(); 635 if (success) { 636 service.setDynamicSystemCode(systemCode); 637 newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values()); 638 } else { 639 Log.e(TAG, "registerSystemCodeForService: Failed to persist System Code."); 640 // Undo registration 641 if (oldDynamicSystemCode == null) { 642 userServices.dynamicSystemCode.remove(componentName); 643 } else { 644 userServices.dynamicSystemCode.put(componentName, oldDynamicSystemCode); 645 } 646 } 647 } 648 if (success) { 649 // Make callback without the lock held 650 mCallback.onNfcFServicesUpdated(userId, newServices); 651 } 652 return success; 653 } 654 getSystemCodeForService(int userId, int uid, ComponentName componentName)655 public String getSystemCodeForService(int userId, int uid, ComponentName componentName) { 656 if (DBG) Log.d(TAG, "getSystemCodeForService"); 657 NfcFServiceInfo service = getService(userId, componentName); 658 if (service != null) { 659 if (service.getUid() != uid) { 660 Log.e(TAG, "getSystemCodeForService: UID mismatch"); 661 return null; 662 } 663 return service.getSystemCode(); 664 } else { 665 Log.e(TAG, "getSystemCodeForService: Could not find service " + componentName); 666 return null; 667 } 668 } 669 removeSystemCodeForService(int userId, int uid, ComponentName componentName)670 public boolean removeSystemCodeForService(int userId, int uid, ComponentName componentName) { 671 if (DBG) Log.d(TAG, "removeSystemCodeForService"); 672 return registerSystemCodeForService(userId, uid, componentName, "NULL"); 673 } 674 setNfcid2ForService(int userId, int uid, ComponentName componentName, String nfcid2)675 public boolean setNfcid2ForService(int userId, int uid, 676 ComponentName componentName, String nfcid2) { 677 if (DBG) Log.d(TAG, "setNfcid2ForService"); 678 ArrayList<NfcFServiceInfo> newServices = null; 679 boolean success; 680 synchronized (mLock) { 681 if (mActivated) { 682 Log.d(TAG, "setNfcid2ForService: failed to set NFCID2 during activation"); 683 return false; 684 } 685 UserServices userServices = findOrCreateUserLocked(userId); 686 // Check if we can find this service 687 NfcFServiceInfo service = getService(userId, componentName); 688 if (service == null) { 689 Log.e(TAG, "setNfcid2ForService: Service " + componentName + " does not exist"); 690 return false; 691 } 692 if (service.getUid() != uid) { 693 // This is probably a good indication something is wrong here. 694 // Either newer service installed with different uid (but then 695 // we should have known about it), or somebody calling us from 696 // a different uid. 697 Log.e(TAG, "setNfcid2ForService: UID mismatch"); 698 return false; 699 } 700 if (!NfcFCardEmulation.isValidNfcid2(nfcid2)) { 701 Log.e(TAG, "setNfcid2ForService: NFCID2 " + nfcid2 + " is not a valid NFCID2"); 702 return false; 703 } 704 // Apply dynamic NFCID2 mappings 705 nfcid2 = nfcid2.toUpperCase(); 706 DynamicNfcid2 oldDynamicNfcid2 = userServices.dynamicNfcid2.get(componentName); 707 DynamicNfcid2 dynamicNfcid2 = new DynamicNfcid2(uid, nfcid2); 708 userServices.dynamicNfcid2.put(componentName, dynamicNfcid2); 709 success = writeDynamicSystemCodeNfcid2Locked(); 710 if (success) { 711 service.setDynamicNfcid2(nfcid2); 712 newServices = new ArrayList<NfcFServiceInfo>(userServices.services.values()); 713 } else { 714 Log.e(TAG, "setNfcid2ForService: Failed to persist NFCID2"); 715 // Undo registration 716 if (oldDynamicNfcid2 == null) { 717 userServices.dynamicNfcid2.remove(componentName); 718 } else { 719 userServices.dynamicNfcid2.put(componentName, oldDynamicNfcid2); 720 } 721 } 722 } 723 if (success) { 724 // Make callback without the lock held 725 mCallback.onNfcFServicesUpdated(userId, newServices); 726 } 727 return success; 728 } 729 getNfcid2ForService(int userId, int uid, ComponentName componentName)730 public String getNfcid2ForService(int userId, int uid, ComponentName componentName) { 731 if (DBG) Log.d(TAG, "getNfcid2ForService"); 732 NfcFServiceInfo service = getService(userId, componentName); 733 if (service != null) { 734 if (service.getUid() != uid) { 735 Log.e(TAG, "getNfcid2ForService: UID mismatch"); 736 return null; 737 } 738 return service.getNfcid2(); 739 } else { 740 Log.e(TAG, "getNfcid2ForService: Could not find service " + componentName); 741 return null; 742 } 743 } 744 onHostEmulationActivated()745 public void onHostEmulationActivated() { 746 if (DBG) Log.d(TAG, "onHostEmulationActivated"); 747 synchronized (mLock) { 748 mActivated = true; 749 } 750 } 751 onHostEmulationDeactivated()752 public void onHostEmulationDeactivated() { 753 if (DBG) Log.d(TAG, "onHostEmulationDeactivated"); 754 synchronized (mLock) { 755 mActivated = false; 756 } 757 } 758 onNfcDisabled()759 public void onNfcDisabled() { 760 synchronized (mLock) { 761 mActivated = false; 762 } 763 } 764 onUserSwitched()765 public void onUserSwitched() { 766 synchronized (mLock) { 767 mUserSwitched = true; 768 refreshUserProfilesLocked(); 769 } 770 } 771 onManagedProfileChanged()772 public void onManagedProfileChanged() { 773 synchronized (mLock) { 774 refreshUserProfilesLocked(); 775 } 776 } 777 refreshUserProfilesLocked()778 private void refreshUserProfilesLocked() { 779 UserManager um = mContext.createContextAsUser( 780 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0) 781 .getSystemService(UserManager.class); 782 mUserHandles = um.getEnabledProfiles(); 783 List<UserHandle> removeUserHandles = new ArrayList<UserHandle>(); 784 785 for (UserHandle uh : mUserHandles) { 786 if (um.isQuietModeEnabled(uh)) { 787 removeUserHandles.add(uh); 788 } 789 } 790 mUserHandles.removeAll(removeUserHandles); 791 } 792 generateRandomNfcid2()793 private String generateRandomNfcid2() { 794 long min = 0L; 795 long max = 0xFFFFFFFFFFFFL; 796 797 long randomNfcid2 = (long)Math.floor(Math.random() * (max-min+1)) + min; 798 return String.format("02FE%02X%02X%02X%02X%02X%02X", 799 (randomNfcid2 >>> 8 * 5) & 0xFF, (randomNfcid2 >>> 8 * 4) & 0xFF, 800 (randomNfcid2 >>> 8 * 3) & 0xFF, (randomNfcid2 >>> 8 * 2) & 0xFF, 801 (randomNfcid2 >>> 8 * 1) & 0xFF, (randomNfcid2 >>> 8 * 0) & 0xFF); 802 } 803 dump(FileDescriptor fd, PrintWriter pw, String[] args)804 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 805 pw.println("Registered HCE-F services for current user: "); 806 ParcelFileDescriptor pFd; 807 try { 808 pFd = ParcelFileDescriptor.dup(fd); 809 synchronized (mLock) { 810 for (UserHandle uh : mUserHandles) { 811 UserManager um = mContext.createContextAsUser( 812 uh, /*flags=*/0).getSystemService(UserManager.class); 813 pw.println("User " + Utils.maskSubstring(um.getUserName(), 3)); 814 UserServices userServices = findOrCreateUserLocked(uh.getIdentifier()); 815 for (NfcFServiceInfo service : userServices.services.values()) { 816 service.dump(pFd, pw, args); 817 pw.println(""); 818 } 819 pw.println(""); 820 } 821 } 822 pFd.close(); 823 } catch (IOException e) { 824 pw.println("Failed to dump HCE-F services: " + e); 825 } 826 } 827 828 /** 829 * Dump debugging information as a RegisteredNfcFServicesCacheProto 830 * 831 * Note: 832 * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto 833 * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and 834 * {@link ProtoOutputStream#end(long)} after. 835 * Never reuse a proto field number. When removing a field, mark it as reserved. 836 */ dumpDebug(ProtoOutputStream proto)837 void dumpDebug(ProtoOutputStream proto) { 838 synchronized (mLock) { 839 UserServices userServices = findOrCreateUserLocked(ActivityManager.getCurrentUser()); 840 for (NfcFServiceInfo service : userServices.services.values()) { 841 long token = proto.start(RegisteredNfcFServicesCacheProto.NFC_FSERVICE_INFO); 842 service.dumpDebug(proto); 843 proto.end(token); 844 } 845 } 846 } 847 848 @VisibleForTesting isActivated()849 public boolean isActivated() { 850 return mActivated; 851 } 852 } 853