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