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