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