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