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