1 /* 2 * Copyright (C) 2024 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 android.nfc; 18 19 import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_DH; 20 import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE; 21 import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_NDEF_NFCEE; 22 import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC; 23 import static android.nfc.cardemulation.CardEmulation.routeIntToString; 24 25 import android.Manifest; 26 import android.annotation.CallbackExecutor; 27 import android.annotation.DurationMillisLong; 28 import android.annotation.FlaggedApi; 29 import android.annotation.IntDef; 30 import android.annotation.NonNull; 31 import android.annotation.RequiresPermission; 32 import android.annotation.SystemApi; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.nfc.cardemulation.ApduServiceInfo; 37 import android.nfc.cardemulation.CardEmulation; 38 import android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute; 39 import android.os.Binder; 40 import android.os.Bundle; 41 import android.os.RemoteException; 42 import android.os.ResultReceiver; 43 import android.se.omapi.Reader; 44 import android.util.Log; 45 46 import com.android.internal.annotations.VisibleForTesting; 47 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 import java.nio.charset.StandardCharsets; 51 import java.util.ArrayList; 52 import java.util.HashMap; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.concurrent.ExecutionException; 56 import java.util.concurrent.Executor; 57 import java.util.concurrent.ExecutorService; 58 import java.util.concurrent.Executors; 59 import java.util.concurrent.FutureTask; 60 import java.util.concurrent.TimeUnit; 61 import java.util.concurrent.TimeoutException; 62 import java.util.function.BiConsumer; 63 import java.util.function.Consumer; 64 import java.util.function.Function; 65 import java.util.function.Supplier; 66 67 /** 68 * Used for OEM extension APIs. 69 * This class holds all the APIs and callbacks defined for OEMs/vendors to extend the NFC stack 70 * for their proprietary features. 71 * 72 * @hide 73 */ 74 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 75 @SystemApi 76 public final class NfcOemExtension { 77 private static final String TAG = "NfcOemExtension"; 78 private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000; 79 private static final int TYPE_TECHNOLOGY = 0; 80 private static final int TYPE_PROTOCOL = 1; 81 private static final int TYPE_AID = 2; 82 private static final int TYPE_SYSTEMCODE = 3; 83 84 private final NfcAdapter mAdapter; 85 private final NfcOemExtensionCallback mOemNfcExtensionCallback; 86 private boolean mIsRegistered = false; 87 private final Map<Callback, Executor> mCallbackMap = new HashMap<>(); 88 private final Context mContext; 89 private final Object mLock = new Object(); 90 private boolean mCardEmulationActivated = false; 91 private boolean mRfFieldActivated = false; 92 private boolean mRfDiscoveryStarted = false; 93 private boolean mEeListenActivated = false; 94 95 /** 96 * Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled. 97 * <p> OEM extension modules should use this intent to start their extension service </p> 98 * @hide 99 */ 100 public static final String ACTION_OEM_EXTENSION_INIT = "android.nfc.action.OEM_EXTENSION_INIT"; 101 102 /** 103 * Mode Type for {@link #setControllerAlwaysOnMode(int)}. 104 * Enables the controller in default mode when NFC is disabled (existing API behavior). 105 * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}. 106 * @hide 107 */ 108 @SystemApi 109 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 110 public static final int ENABLE_DEFAULT = NfcAdapter.CONTROLLER_ALWAYS_ON_MODE_DEFAULT; 111 112 /** 113 * Mode Type for {@link #setControllerAlwaysOnMode(int)}. 114 * Enables the controller in transparent mode when NFC is disabled. 115 * @hide 116 */ 117 @SystemApi 118 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 119 public static final int ENABLE_TRANSPARENT = 2; 120 121 /** 122 * Mode Type for {@link #setControllerAlwaysOnMode(int)}. 123 * Enables the controller and initializes and enables the EE subsystem when NFC is disabled. 124 * @hide 125 */ 126 @SystemApi 127 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 128 public static final int ENABLE_EE = 3; 129 130 /** 131 * Mode Type for {@link #setControllerAlwaysOnMode(int)}. 132 * Disable the Controller Always On Mode. 133 * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}. 134 * @hide 135 */ 136 @SystemApi 137 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 138 public static final int DISABLE = NfcAdapter.CONTROLLER_ALWAYS_ON_DISABLE; 139 140 /** 141 * Possible controller modes for {@link #setControllerAlwaysOnMode(int)}. 142 * 143 * @hide 144 */ 145 @IntDef(prefix = { "" }, value = { 146 ENABLE_DEFAULT, 147 ENABLE_TRANSPARENT, 148 ENABLE_EE, 149 DISABLE, 150 }) 151 @Retention(RetentionPolicy.SOURCE) 152 public @interface ControllerMode{} 153 154 /** 155 * Technology Type for {@link #getActiveNfceeList()}. 156 */ 157 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 158 public static final int NFCEE_TECH_NONE = 0; 159 160 /** 161 * Technology Type for {@link #getActiveNfceeList()}. 162 */ 163 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 164 public static final int NFCEE_TECH_A = 1; 165 166 /** 167 * Technology Type for {@link #getActiveNfceeList()}. 168 */ 169 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 170 public static final int NFCEE_TECH_B = 1 << 1; 171 172 /** 173 * Technology Type for {@link #getActiveNfceeList()}. 174 */ 175 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 176 public static final int NFCEE_TECH_F = 1 << 2; 177 178 /** 179 * Nfc technology flags for {@link #getActiveNfceeList()}. 180 * 181 * @hide 182 */ 183 @IntDef(flag = true, value = { 184 NFCEE_TECH_NONE, 185 NFCEE_TECH_A, 186 NFCEE_TECH_B, 187 NFCEE_TECH_F, 188 }) 189 @Retention(RetentionPolicy.SOURCE) 190 public @interface NfceeTechnology {} 191 192 /** 193 * Event that Host Card Emulation is activated. 194 */ 195 public static final int HCE_ACTIVATE = 1; 196 /** 197 * Event that some data is transferred in Host Card Emulation. 198 */ 199 public static final int HCE_DATA_TRANSFERRED = 2; 200 /** 201 * Event that Host Card Emulation is deactivated. 202 */ 203 public static final int HCE_DEACTIVATE = 3; 204 /** 205 * Possible events from {@link Callback#onHceEventReceived}. 206 * 207 * @hide 208 */ 209 @IntDef(value = { 210 HCE_ACTIVATE, 211 HCE_DATA_TRANSFERRED, 212 HCE_DEACTIVATE 213 }) 214 @Retention(RetentionPolicy.SOURCE) 215 public @interface HostCardEmulationAction {} 216 217 /** 218 * Status code returned when the polling state change request succeeded. 219 * @see #pausePolling() 220 * @see #resumePolling() 221 */ 222 public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; 223 /** 224 * Status code returned when the polling state change request is already in 225 * required state. 226 * @see #pausePolling() 227 * @see #resumePolling() 228 */ 229 public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; 230 /** 231 * Possible status codes for {@link #pausePolling()} and 232 * {@link #resumePolling()}. 233 * @hide 234 */ 235 @IntDef(value = { 236 POLLING_STATE_CHANGE_SUCCEEDED, 237 POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE, 238 }) 239 @Retention(RetentionPolicy.SOURCE) 240 public @interface PollingStateChangeStatusCode {} 241 242 /** 243 * Status OK 244 */ 245 public static final int STATUS_OK = 0; 246 /** 247 * Status unknown error 248 */ 249 public static final int STATUS_UNKNOWN_ERROR = 1; 250 251 /** 252 * Status codes passed to OEM extension callbacks. 253 * 254 * @hide 255 */ 256 @IntDef(value = { 257 STATUS_OK, 258 STATUS_UNKNOWN_ERROR 259 }) 260 @Retention(RetentionPolicy.SOURCE) 261 public @interface StatusCode {} 262 263 /** 264 * Routing commit succeeded. 265 */ 266 public static final int COMMIT_ROUTING_STATUS_OK = 0; 267 /** 268 * Routing commit failed. 269 */ 270 public static final int COMMIT_ROUTING_STATUS_FAILED = 3; 271 /** 272 * Routing commit failed due to the update is in progress. 273 */ 274 public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6; 275 276 /** 277 * Status codes returned when calling {@link #forceRoutingTableCommit()} 278 * @hide 279 */ 280 @IntDef(prefix = "COMMIT_ROUTING_STATUS_", value = { 281 COMMIT_ROUTING_STATUS_OK, 282 COMMIT_ROUTING_STATUS_FAILED, 283 COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS, 284 }) 285 @Retention(RetentionPolicy.SOURCE) 286 public @interface CommitRoutingStatusCode {} 287 /** 288 * Interface for Oem extensions for NFC. 289 */ 290 public interface Callback { 291 /** 292 * Notify Oem to tag is connected or not 293 * ex - if tag is connected notify cover and Nfctest app if app is in testing mode 294 * 295 * @param connected status of the tag true if tag is connected otherwise false 296 */ onTagConnected(boolean connected)297 void onTagConnected(boolean connected); 298 299 /** 300 * Update the Nfc Adapter State 301 * @param state new state that need to be updated 302 */ onStateUpdated(@fcAdapter.AdapterState int state)303 void onStateUpdated(@NfcAdapter.AdapterState int state); 304 /** 305 * Check if NfcService apply routing method need to be skipped for 306 * some feature. 307 * @param isSkipped The {@link Consumer} to be completed. If apply routing can be skipped, 308 * the {@link Consumer#accept(Object)} should be called with 309 * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. 310 */ onApplyRouting(@onNull Consumer<Boolean> isSkipped)311 void onApplyRouting(@NonNull Consumer<Boolean> isSkipped); 312 /** 313 * Check if NfcService ndefRead method need to be skipped To skip 314 * and start checking for presence of tag 315 * @param isSkipped The {@link Consumer} to be completed. If Ndef read can be skipped, 316 * the {@link Consumer#accept(Object)} should be called with 317 * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. 318 */ onNdefRead(@onNull Consumer<Boolean> isSkipped)319 void onNdefRead(@NonNull Consumer<Boolean> isSkipped); 320 /** 321 * Method to check if Nfc is allowed to be enabled by OEMs. 322 * @param isAllowed The {@link Consumer} to be completed. If enabling NFC is allowed, 323 * the {@link Consumer#accept(Object)} should be called with 324 * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. 325 * false if NFC cannot be enabled at this time. 326 */ onEnableRequested(@onNull Consumer<Boolean> isAllowed)327 void onEnableRequested(@NonNull Consumer<Boolean> isAllowed); 328 /** 329 * Method to check if Nfc is allowed to be disabled by OEMs. 330 * @param isAllowed The {@link Consumer} to be completed. If disabling NFC is allowed, 331 * the {@link Consumer#accept(Object)} should be called with 332 * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. 333 * false if NFC cannot be disabled at this time. 334 */ onDisableRequested(@onNull Consumer<Boolean> isAllowed)335 void onDisableRequested(@NonNull Consumer<Boolean> isAllowed); 336 337 /** 338 * Callback to indicate that Nfc starts to boot. 339 */ onBootStarted()340 void onBootStarted(); 341 342 /** 343 * Callback to indicate that Nfc starts to enable. 344 */ onEnableStarted()345 void onEnableStarted(); 346 347 /** 348 * Callback to indicate that Nfc starts to disable. 349 */ onDisableStarted()350 void onDisableStarted(); 351 352 /** 353 * Callback to indicate if NFC boots successfully or not. 354 * @param status the status code indicating if boot finished successfully 355 */ onBootFinished(@tatusCode int status)356 void onBootFinished(@StatusCode int status); 357 358 /** 359 * Callback to indicate if NFC is successfully enabled. 360 * @param status the status code indicating if enable finished successfully 361 */ onEnableFinished(@tatusCode int status)362 void onEnableFinished(@StatusCode int status); 363 364 /** 365 * Callback to indicate if NFC is successfully disabled. 366 * @param status the status code indicating if disable finished successfully 367 */ onDisableFinished(@tatusCode int status)368 void onDisableFinished(@StatusCode int status); 369 370 /** 371 * Check if NfcService tag dispatch need to be skipped. 372 * @param isSkipped The {@link Consumer} to be completed. If tag dispatch can be skipped, 373 * the {@link Consumer#accept(Object)} should be called with 374 * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. 375 */ onTagDispatch(@onNull Consumer<Boolean> isSkipped)376 void onTagDispatch(@NonNull Consumer<Boolean> isSkipped); 377 378 /** 379 * Notifies routing configuration is changed. 380 * @param isCommitRoutingSkipped The {@link Consumer} to be 381 * completed. If routing commit should be skipped, 382 * the {@link Consumer#accept(Object)} should be called with 383 * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. 384 */ onRoutingChanged(@onNull Consumer<Boolean> isCommitRoutingSkipped)385 void onRoutingChanged(@NonNull Consumer<Boolean> isCommitRoutingSkipped); 386 387 /** 388 * API to activate start stop cpu boost on hce event. 389 * 390 * <p>When HCE is activated, transferring data, and deactivated, 391 * must call this method to activate, start and stop cpu boost respectively. 392 * @param action Flag indicating actions to activate, start and stop cpu boost. 393 */ onHceEventReceived(@ostCardEmulationAction int action)394 void onHceEventReceived(@HostCardEmulationAction int action); 395 396 /** 397 * API to notify when reader option has been changed using 398 * {@link NfcAdapter#enableReaderOption(boolean)} by some app. 399 * @param enabled Flag indicating ReaderMode enabled/disabled 400 */ onReaderOptionChanged(boolean enabled)401 void onReaderOptionChanged(boolean enabled); 402 403 /** 404 * Notifies NFC is activated in listen mode. 405 * NFC Forum NCI-2.3 ch.5.2.6 specification 406 * 407 * <p>NFCC is ready to communicate with a Card reader 408 * 409 * @param isActivated true, if card emulation activated, else de-activated. 410 */ onCardEmulationActivated(boolean isActivated)411 void onCardEmulationActivated(boolean isActivated); 412 413 /** 414 * Notifies the Remote NFC Endpoint RF Field is detected. 415 * NFC Forum NCI-2.3 ch.5.3 specification 416 * 417 * @param isActive true, if RF Field is ON, else RF Field is OFF. 418 */ onRfFieldDetected(boolean isActive)419 void onRfFieldDetected(boolean isActive); 420 421 /** 422 * Notifies the NFC RF discovery is started or in the IDLE state. 423 * NFC Forum NCI-2.3 ch.5.2 specification 424 * 425 * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle. 426 */ onRfDiscoveryStarted(boolean isDiscoveryStarted)427 void onRfDiscoveryStarted(boolean isDiscoveryStarted); 428 429 /** 430 * Notifies the NFCEE (NFC Execution Environment) Listen has been activated. 431 * 432 * @param isActivated true, if EE Listen is ON, else EE Listen is OFF. 433 */ onEeListenActivated(boolean isActivated)434 void onEeListenActivated(boolean isActivated); 435 436 /** 437 * Notifies that some NFCEE (NFC Execution Environment) has been updated. 438 * 439 * <p> This indicates that some applet has been installed/updated/removed in 440 * one of the NFCEE's. 441 * </p> 442 */ onEeUpdated()443 void onEeUpdated(); 444 445 /** 446 * Gets the intent to find the OEM package in the OEM App market. If the consumer returns 447 * {@code null} or a timeout occurs, the intent from the first available package will be 448 * used instead. 449 * 450 * @param packages the OEM packages name stored in the tag 451 * @param intentConsumer The {@link Consumer} to be completed. 452 * The {@link Consumer#accept(Object)} should be called with 453 * the Intent required. 454 * 455 */ onGetOemAppSearchIntent(@onNull List<String> packages, @NonNull Consumer<Intent> intentConsumer)456 void onGetOemAppSearchIntent(@NonNull List<String> packages, 457 @NonNull Consumer<Intent> intentConsumer); 458 459 /** 460 * Checks if the NDEF message contains any specific OEM package executable content 461 * 462 * @param tag the {@link android.nfc.Tag Tag} 463 * @param message NDEF Message to read from tag 464 * @param hasOemExecutableContent The {@link Consumer} to be completed. If there is 465 * OEM package executable content, the 466 * {@link Consumer#accept(Object)} should be called with 467 * {@link Boolean#TRUE}, otherwise call with 468 * {@link Boolean#FALSE}. 469 */ onNdefMessage(@onNull Tag tag, @NonNull NdefMessage message, @NonNull Consumer<Boolean> hasOemExecutableContent)470 void onNdefMessage(@NonNull Tag tag, @NonNull NdefMessage message, 471 @NonNull Consumer<Boolean> hasOemExecutableContent); 472 473 /** 474 * Callback to indicate the app chooser activity should be launched for handling CE 475 * transaction. This is invoked for example when there are more than 1 app installed that 476 * can handle the HCE transaction. OEMs can launch the Activity based 477 * on their requirement. 478 * 479 * @param selectedAid the selected AID from APDU 480 * @param services {@link ApduServiceInfo} of the service triggering the activity 481 * @param failedComponent the component failed to be resolved 482 * @param category the category of the service 483 */ onLaunchHceAppChooserActivity(@onNull String selectedAid, @NonNull List<ApduServiceInfo> services, @NonNull ComponentName failedComponent, @NonNull String category)484 void onLaunchHceAppChooserActivity(@NonNull String selectedAid, 485 @NonNull List<ApduServiceInfo> services, 486 @NonNull ComponentName failedComponent, 487 @NonNull String category); 488 489 /** 490 * Callback to indicate tap again dialog should be launched for handling HCE transaction. 491 * This is invoked for example when a CE service needs the device to unlocked before 492 * handling the transaction. OEMs can launch the Activity based on their requirement. 493 * 494 * @param service {@link ApduServiceInfo} of the service triggering the dialog 495 * @param category the category of the service 496 */ onLaunchHceTapAgainDialog(@onNull ApduServiceInfo service, @NonNull String category)497 void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category); 498 499 /** 500 * Callback to indicate that routing table is full and the OEM can optionally launch a 501 * dialog to request the user to remove some Card Emulation apps from the device to free 502 * routing table space. 503 */ onRoutingTableFull()504 void onRoutingTableFull(); 505 506 /** 507 * Callback when OEM specified log event are notified. 508 * @param item the log items that contains log information of NFC event. 509 */ onLogEventNotified(@onNull OemLogItems item)510 void onLogEventNotified(@NonNull OemLogItems item); 511 512 /** 513 * Callback to to extract OEM defined packages from given NDEF message when 514 * a NFC tag is detected. These are used to handle NFC tags encoded with a 515 * proprietary format for storing app name (Android native app format). 516 * 517 * @param message NDEF message containing OEM package names 518 * @param packageConsumer The {@link Consumer} to be completed. 519 * The {@link Consumer#accept(Object)} should be called with 520 * the list of package names. 521 */ onExtractOemPackages(@onNull NdefMessage message, @NonNull Consumer<List<String>> packageConsumer)522 void onExtractOemPackages(@NonNull NdefMessage message, 523 @NonNull Consumer<List<String>> packageConsumer); 524 } 525 526 527 /** 528 * Constructor to be used only by {@link NfcAdapter}. 529 */ NfcOemExtension(@onNull Context context, @NonNull NfcAdapter adapter)530 NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) { 531 mContext = context; 532 mAdapter = adapter; 533 mOemNfcExtensionCallback = new NfcOemExtensionCallback(); 534 } 535 536 /** @hide */ 537 @VisibleForTesting getOemNfcExtensionCallback()538 public NfcOemExtensionCallback getOemNfcExtensionCallback() { 539 return mOemNfcExtensionCallback; 540 } 541 542 /** 543 * Get an instance of {@link T4tNdefNfcee} object for performing T4T (Type-4 Tag) 544 * NDEF (NFC Data Exchange Format) NFCEE (NFC Execution Environment) operations. 545 * This can be used to write NDEF data to emulate a T4T tag in an NFCEE 546 * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification 547 * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details. 548 * 549 * This is a singleton object which shall be used by OEM extension module to do NDEF-NFCEE 550 * read/write operations. 551 * 552 * <p>Returns {@link T4tNdefNfcee} 553 * <p>Does not cause any RF activity and does not block. 554 * @return NFC Data Exchange Format (NDEF) NFC Execution Environment (NFCEE) object 555 * @hide 556 */ 557 @SystemApi 558 @NonNull 559 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) getT4tNdefNfcee()560 public T4tNdefNfcee getT4tNdefNfcee() { 561 return T4tNdefNfcee.getInstance(); 562 } 563 564 /** 565 * Register an {@link Callback} to listen for NFC oem extension callbacks 566 * Multiple clients can register and callbacks will be invoked asynchronously. 567 * 568 * <p>The provided callback will be invoked by the given {@link Executor}. 569 * As part of {@link #registerCallback(Executor, Callback)} the 570 * {@link Callback} will be invoked with current NFC state 571 * before the {@link #registerCallback(Executor, Callback)} function completes. 572 * 573 * @param executor an {@link Executor} to execute given callback 574 * @param callback oem implementation of {@link Callback} 575 */ 576 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 577 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) registerCallback(@onNull @allbackExecutor Executor executor, @NonNull Callback callback)578 public void registerCallback(@NonNull @CallbackExecutor Executor executor, 579 @NonNull Callback callback) { 580 synchronized (mLock) { 581 if (executor == null || callback == null) { 582 Log.e(TAG, "Executor and Callback must not be null!"); 583 throw new IllegalArgumentException(); 584 } 585 586 if (mCallbackMap.containsKey(callback)) { 587 Log.e(TAG, "Callback already registered. Unregister existing callback before" 588 + "registering"); 589 throw new IllegalArgumentException(); 590 } 591 mCallbackMap.put(callback, executor); 592 if (!mIsRegistered) { 593 NfcAdapter.callService(() -> { 594 NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback); 595 mIsRegistered = true; 596 }); 597 } else { 598 updateNfCState(callback, executor); 599 } 600 } 601 } 602 updateNfCState(Callback callback, Executor executor)603 private void updateNfCState(Callback callback, Executor executor) { 604 if (callback != null) { 605 Log.i(TAG, "updateNfCState"); 606 executor.execute(() -> { 607 callback.onCardEmulationActivated(mCardEmulationActivated); 608 callback.onRfFieldDetected(mRfFieldActivated); 609 callback.onRfDiscoveryStarted(mRfDiscoveryStarted); 610 callback.onEeListenActivated(mEeListenActivated); 611 }); 612 } 613 } 614 615 /** 616 * Unregister the specified {@link Callback} 617 * 618 * <p>The same {@link Callback} object used when calling 619 * {@link #registerCallback(Executor, Callback)} must be used. 620 * 621 * <p>Callbacks are automatically unregistered when an application process goes away 622 * 623 * @param callback oem implementation of {@link Callback} 624 */ 625 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 626 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) unregisterCallback(@onNull Callback callback)627 public void unregisterCallback(@NonNull Callback callback) { 628 synchronized (mLock) { 629 if (!mCallbackMap.containsKey(callback) || !mIsRegistered) { 630 Log.e(TAG, "Callback not registered"); 631 throw new IllegalArgumentException(); 632 } 633 if (mCallbackMap.size() == 1) { 634 NfcAdapter.callService(() -> { 635 NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback); 636 mIsRegistered = false; 637 mCallbackMap.remove(callback); 638 }); 639 } else { 640 mCallbackMap.remove(callback); 641 } 642 } 643 } 644 645 /** 646 * Clear NfcService preference, interface method to clear NFC preference values on OEM specific 647 * events. For ex: on soft reset, Nfc default values needs to be overridden by OEM defaults. 648 */ 649 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 650 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) clearPreference()651 public void clearPreference() { 652 NfcAdapter.callService(() -> NfcAdapter.sService.clearPreference()); 653 } 654 655 /** 656 * Get the screen state from system and set it to current screen state. 657 */ 658 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 659 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) synchronizeScreenState()660 public void synchronizeScreenState() { 661 NfcAdapter.callService(() -> NfcAdapter.sService.setScreenState()); 662 } 663 664 /** 665 * Check if the firmware needs updating. 666 * 667 * <p>If an update is needed, a firmware will be triggered when NFC is disabled. 668 */ 669 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 670 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) maybeTriggerFirmwareUpdate()671 public void maybeTriggerFirmwareUpdate() { 672 NfcAdapter.callService(() -> NfcAdapter.sService.checkFirmware()); 673 } 674 675 /** 676 * Get the Active NFCEE (NFC Execution Environment) List 677 * 678 * @return Map< String, @NfceeTechnology Integer > 679 * A HashMap where keys are activated secure elements and 680 * the values are bitmap of technologies supported by each secure element: 681 * NFCEE_TECH_A == 0x1 682 * NFCEE_TECH_B == 0x2 683 * NFCEE_TECH_F == 0x4 684 * and keys can contain "eSE" and "SIM" with a number, 685 * in case of failure an empty map is returned. 686 * @see Reader#getName() for the list of possible NFCEE names. 687 */ 688 @NonNull 689 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) getActiveNfceeList()690 public Map<String, Integer> getActiveNfceeList() { 691 return NfcAdapter.callServiceReturn(() -> 692 NfcAdapter.sService.fetchActiveNfceeList(), new HashMap<String, Integer>()); 693 } 694 695 /** 696 * Sets NFC controller always on feature. 697 * <p>This API is for the NFCC internal state management. It allows to discriminate 698 * the controller function from the NFC function by keeping the NFC controller on without 699 * any NFC RF enabled if necessary. 700 * <p>This call is asynchronous, register listener {@link NfcAdapter.ControllerAlwaysOnListener} 701 * by {@link NfcAdapter#registerControllerAlwaysOnListener} to find out when the operation is 702 * complete. 703 * <p> Note: This adds more always on modes on top of existing 704 * {@link NfcAdapter#setControllerAlwaysOn(boolean)} API which can be used to set the NFCC in 705 * only {@link #ENABLE_DEFAULT} and {@link #DISABLE} modes. 706 * @param mode one of {@link ControllerMode} modes 707 * @throws UnsupportedOperationException if 708 * <li> if FEATURE_NFC, FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 709 * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE 710 * are unavailable </li> 711 * <li> if the feature is unavailable @see NfcAdapter#isNfcControllerAlwaysOnSupported() </li> 712 * @hide 713 * @see NfcAdapter#setControllerAlwaysOn(boolean) 714 */ 715 @SystemApi 716 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 717 @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) setControllerAlwaysOnMode(@ontrollerMode int mode)718 public void setControllerAlwaysOnMode(@ControllerMode int mode) { 719 if (!NfcAdapter.sHasNfcFeature && !NfcAdapter.sHasCeFeature) { 720 throw new UnsupportedOperationException(); 721 } 722 NfcAdapter.callService(() -> NfcAdapter.sService.setControllerAlwaysOn(mode)); 723 } 724 725 /** 726 * Triggers NFC initialization. If OEM extension is registered 727 * (indicated via `enable_oem_extension` NFC overlay), the NFC stack initialization at bootup 728 * is delayed until the OEM extension app triggers the initialization via this call. 729 */ 730 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 731 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) triggerInitialization()732 public void triggerInitialization() { 733 NfcAdapter.callService(() -> NfcAdapter.sService.triggerInitialization()); 734 } 735 736 /** 737 * Gets the last user toggle status. 738 * @return true if NFC is set to ON, false otherwise 739 */ 740 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 741 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) hasUserEnabledNfc()742 public boolean hasUserEnabledNfc() { 743 return NfcAdapter.callServiceReturn(() -> NfcAdapter.sService.getSettingStatus(), false); 744 } 745 746 /** 747 * Checks if the tag is present or not. 748 * @return true if the tag is present, false otherwise 749 */ 750 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 751 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) isTagPresent()752 public boolean isTagPresent() { 753 return NfcAdapter.callServiceReturn(() -> NfcAdapter.sService.isTagPresent(), false); 754 } 755 756 /** 757 * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. 758 * In case of {@code timeoutInMs} is zero, polling will be stopped indefinitely. 759 * Use {@link #resumePolling()} to resume the polling. 760 * Use {@link #getMaxPausePollingTimeoutMs()} to check the max timeout value. 761 * @param timeoutInMs the pause polling duration in millisecond. 762 * @return status of the operation 763 * @throws IllegalArgumentException if timeoutInMs value is invalid 764 * (timeoutinMs > max or timeoutInMs < 0). 765 */ 766 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 767 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) pausePolling(@urationMillisLong long timeoutInMs)768 public @PollingStateChangeStatusCode int pausePolling(@DurationMillisLong long timeoutInMs) { 769 return NfcAdapter.callServiceReturn(() -> 770 NfcAdapter.sService.pausePolling(timeoutInMs), 771 POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); 772 } 773 774 /** 775 * Resumes default NFC tag reader mode polling for the current device state if polling is 776 * paused. Calling this while already in polling is a no-op. 777 * @return status of the operation 778 */ 779 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 780 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) resumePolling()781 public @PollingStateChangeStatusCode int resumePolling() { 782 return NfcAdapter.callServiceReturn(() -> 783 NfcAdapter.sService.resumePolling(), 784 POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); 785 } 786 787 /** 788 * Gets the max pause polling timeout value in millisecond. 789 * @return long integer representing the max timeout 790 */ 791 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 792 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) 793 @DurationMillisLong getMaxPausePollingTimeoutMills()794 public long getMaxPausePollingTimeoutMills() { 795 return NfcAdapter.callServiceReturn(() -> 796 NfcAdapter.sService.getMaxPausePollingTimeoutMs(), 0L); 797 } 798 799 /** 800 * Set whether to enable auto routing change or not (enabled by default). 801 * If disabled, routing targets are limited to a single off-host destination. 802 * 803 * @param state status of auto routing change, true if enable, otherwise false 804 */ 805 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 806 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) setAutoChangeEnabled(boolean state)807 public void setAutoChangeEnabled(boolean state) { 808 NfcAdapter.callService(() -> 809 NfcAdapter.sCardEmulationService.setAutoChangeStatus(state)); 810 } 811 812 /** 813 * Check if auto routing change is enabled or not. 814 * 815 * @return true if enabled, otherwise false 816 */ 817 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 818 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) isAutoChangeEnabled()819 public boolean isAutoChangeEnabled() { 820 return NfcAdapter.callServiceReturn(() -> 821 NfcAdapter.sCardEmulationService.isAutoChangeEnabled(), false); 822 } 823 824 /** 825 * Get current routing status 826 * 827 * @return {@link RoutingStatus} indicating the default route, default ISO-DEP 828 * route and default off-host route. 829 */ 830 @NonNull 831 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 832 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) getRoutingStatus()833 public RoutingStatus getRoutingStatus() { 834 List<String> status = NfcAdapter.callServiceReturn(() -> 835 NfcAdapter.sCardEmulationService.getRoutingStatus(), new ArrayList<>()); 836 return new RoutingStatus(routeStringToInt(status.get(0)), 837 routeStringToInt(status.get(1)), 838 routeStringToInt(status.get(2))); 839 } 840 841 /** 842 * Overwrites NFC controller routing table, which includes Protocol Route, Technology Route, 843 * and Empty AID Route. 844 * 845 * The parameter set to 846 * {@link ProtocolAndTechnologyRoute#PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET} 847 * can be used to keep current values for that entry. At least one route should be overridden 848 * when calling this API, otherwise throw {@link IllegalArgumentException}. 849 * 850 * @param protocol ISO-DEP route destination, where the possible inputs are defined in 851 * {@link ProtocolAndTechnologyRoute}. 852 * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs 853 * are defined in 854 * {@link ProtocolAndTechnologyRoute} 855 * @param emptyAid Zero-length AID route destination, where the possible inputs are defined in 856 * {@link ProtocolAndTechnologyRoute} 857 * @param systemCode System Code route destination, where the possible inputs are defined in 858 * {@link ProtocolAndTechnologyRoute} 859 */ 860 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) 861 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) overwriteRoutingTable( @ardEmulation.ProtocolAndTechnologyRoute int protocol, @CardEmulation.ProtocolAndTechnologyRoute int technology, @CardEmulation.ProtocolAndTechnologyRoute int emptyAid, @CardEmulation.ProtocolAndTechnologyRoute int systemCode)862 public void overwriteRoutingTable( 863 @CardEmulation.ProtocolAndTechnologyRoute int protocol, 864 @CardEmulation.ProtocolAndTechnologyRoute int technology, 865 @CardEmulation.ProtocolAndTechnologyRoute int emptyAid, 866 @CardEmulation.ProtocolAndTechnologyRoute int systemCode) { 867 868 String protocolRoute = routeIntToString(protocol); 869 String technologyRoute = routeIntToString(technology); 870 String emptyAidRoute = routeIntToString(emptyAid); 871 String systemCodeRoute = routeIntToString(systemCode); 872 873 NfcAdapter.callService(() -> 874 NfcAdapter.sCardEmulationService.overwriteRoutingTable( 875 mContext.getUser().getIdentifier(), 876 emptyAidRoute, 877 protocolRoute, 878 technologyRoute, 879 systemCodeRoute 880 )); 881 } 882 883 /** 884 * Gets current routing table entries. 885 * @return List of {@link NfcRoutingTableEntry} representing current routing table 886 */ 887 @NonNull 888 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) 889 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) getRoutingTable()890 public List<NfcRoutingTableEntry> getRoutingTable() { 891 List<Entry> entryList = NfcAdapter.callServiceReturn(() -> 892 NfcAdapter.sService.getRoutingTableEntryList(), null); 893 List<NfcRoutingTableEntry> result = new ArrayList<>(); 894 for (Entry entry : entryList) { 895 switch (entry.getType()) { 896 case TYPE_TECHNOLOGY -> result.add( 897 new RoutingTableTechnologyEntry(entry.getNfceeId(), 898 RoutingTableTechnologyEntry.techStringToInt(entry.getEntry()), 899 routeStringToInt(entry.getRoutingType())) 900 ); 901 case TYPE_PROTOCOL -> result.add( 902 new RoutingTableProtocolEntry(entry.getNfceeId(), 903 RoutingTableProtocolEntry.protocolStringToInt(entry.getEntry()), 904 routeStringToInt(entry.getRoutingType())) 905 ); 906 case TYPE_AID -> result.add( 907 new RoutingTableAidEntry(entry.getNfceeId(), entry.getEntry(), 908 routeStringToInt(entry.getRoutingType())) 909 ); 910 case TYPE_SYSTEMCODE -> result.add( 911 new RoutingTableSystemCodeEntry(entry.getNfceeId(), 912 entry.getEntry().getBytes(StandardCharsets.UTF_8), 913 routeStringToInt(entry.getRoutingType())) 914 ); 915 } 916 } 917 return result; 918 } 919 920 /** 921 * API to force a routing table commit. 922 * @return a {@link StatusCode} to indicate if commit routing succeeded or not 923 */ 924 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) 925 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 926 @CommitRoutingStatusCode forceRoutingTableCommit()927 public int forceRoutingTableCommit() { 928 return NfcAdapter.callServiceReturn( 929 () -> NfcAdapter.sService.commitRouting(), COMMIT_ROUTING_STATUS_FAILED); 930 } 931 932 /** @hide */ 933 public final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { 934 935 @Override onTagConnected(boolean connected)936 public void onTagConnected(boolean connected) throws RemoteException { 937 mCallbackMap.forEach((cb, ex) -> 938 handleVoidCallback(connected, cb::onTagConnected, ex)); 939 } 940 941 @Override onCardEmulationActivated(boolean isActivated)942 public void onCardEmulationActivated(boolean isActivated) throws RemoteException { 943 mCardEmulationActivated = isActivated; 944 mCallbackMap.forEach((cb, ex) -> 945 handleVoidCallback(isActivated, cb::onCardEmulationActivated, ex)); 946 } 947 948 @Override onRfFieldDetected(boolean isActive)949 public void onRfFieldDetected(boolean isActive) throws RemoteException { 950 mRfFieldActivated = isActive; 951 mCallbackMap.forEach((cb, ex) -> 952 handleVoidCallback(isActive, cb::onRfFieldDetected, ex)); 953 } 954 955 @Override onRfDiscoveryStarted(boolean isDiscoveryStarted)956 public void onRfDiscoveryStarted(boolean isDiscoveryStarted) throws RemoteException { 957 mRfDiscoveryStarted = isDiscoveryStarted; 958 mCallbackMap.forEach((cb, ex) -> 959 handleVoidCallback(isDiscoveryStarted, cb::onRfDiscoveryStarted, ex)); 960 } 961 962 @Override onEeListenActivated(boolean isActivated)963 public void onEeListenActivated(boolean isActivated) throws RemoteException { 964 mEeListenActivated = isActivated; 965 mCallbackMap.forEach((cb, ex) -> 966 handleVoidCallback(isActivated, cb::onEeListenActivated, ex)); 967 } 968 969 @Override onEeUpdated()970 public void onEeUpdated() throws RemoteException { 971 mCallbackMap.forEach((cb, ex) -> 972 handleVoidCallback(null, (Object input) -> cb.onEeUpdated(), ex)); 973 } 974 975 @Override onStateUpdated(int state)976 public void onStateUpdated(int state) throws RemoteException { 977 mCallbackMap.forEach((cb, ex) -> 978 handleVoidCallback(state, cb::onStateUpdated, ex)); 979 } 980 981 @Override onApplyRouting(ResultReceiver isSkipped)982 public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException { 983 mCallbackMap.forEach((cb, ex) -> 984 handleVoidCallback( 985 new ReceiverWrapper<>(isSkipped), cb::onApplyRouting, ex)); 986 } 987 @Override onNdefRead(ResultReceiver isSkipped)988 public void onNdefRead(ResultReceiver isSkipped) throws RemoteException { 989 mCallbackMap.forEach((cb, ex) -> 990 handleVoidCallback( 991 new ReceiverWrapper<>(isSkipped), cb::onNdefRead, ex)); 992 } 993 @Override onEnable(ResultReceiver isAllowed)994 public void onEnable(ResultReceiver isAllowed) throws RemoteException { 995 mCallbackMap.forEach((cb, ex) -> 996 handleVoidCallback( 997 new ReceiverWrapper<>(isAllowed), cb::onEnableRequested, ex)); 998 } 999 @Override onDisable(ResultReceiver isAllowed)1000 public void onDisable(ResultReceiver isAllowed) throws RemoteException { 1001 mCallbackMap.forEach((cb, ex) -> 1002 handleVoidCallback( 1003 new ReceiverWrapper<>(isAllowed), cb::onDisableRequested, ex)); 1004 } 1005 @Override onBootStarted()1006 public void onBootStarted() throws RemoteException { 1007 mCallbackMap.forEach((cb, ex) -> 1008 handleVoidCallback(null, (Object input) -> cb.onBootStarted(), ex)); 1009 } 1010 @Override onEnableStarted()1011 public void onEnableStarted() throws RemoteException { 1012 mCallbackMap.forEach((cb, ex) -> 1013 handleVoidCallback(null, (Object input) -> cb.onEnableStarted(), ex)); 1014 } 1015 @Override onDisableStarted()1016 public void onDisableStarted() throws RemoteException { 1017 mCallbackMap.forEach((cb, ex) -> 1018 handleVoidCallback(null, (Object input) -> cb.onDisableStarted(), ex)); 1019 } 1020 @Override onBootFinished(int status)1021 public void onBootFinished(int status) throws RemoteException { 1022 mCallbackMap.forEach((cb, ex) -> 1023 handleVoidCallback(status, cb::onBootFinished, ex)); 1024 } 1025 @Override onEnableFinished(int status)1026 public void onEnableFinished(int status) throws RemoteException { 1027 mCallbackMap.forEach((cb, ex) -> 1028 handleVoidCallback(status, cb::onEnableFinished, ex)); 1029 } 1030 @Override onDisableFinished(int status)1031 public void onDisableFinished(int status) throws RemoteException { 1032 mCallbackMap.forEach((cb, ex) -> 1033 handleVoidCallback(status, cb::onDisableFinished, ex)); 1034 } 1035 @Override onTagDispatch(ResultReceiver isSkipped)1036 public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException { 1037 mCallbackMap.forEach((cb, ex) -> 1038 handleVoidCallback( 1039 new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex)); 1040 } 1041 @Override onRoutingChanged(ResultReceiver isSkipped)1042 public void onRoutingChanged(ResultReceiver isSkipped) throws RemoteException { 1043 mCallbackMap.forEach((cb, ex) -> 1044 handleVoidCallback( 1045 new ReceiverWrapper<>(isSkipped), cb::onRoutingChanged, ex)); 1046 } 1047 @Override onHceEventReceived(int action)1048 public void onHceEventReceived(int action) throws RemoteException { 1049 mCallbackMap.forEach((cb, ex) -> 1050 handleVoidCallback(action, cb::onHceEventReceived, ex)); 1051 } 1052 1053 @Override onReaderOptionChanged(boolean enabled)1054 public void onReaderOptionChanged(boolean enabled) throws RemoteException { 1055 mCallbackMap.forEach((cb, ex) -> 1056 handleVoidCallback(enabled, cb::onReaderOptionChanged, ex)); 1057 } 1058 onRoutingTableFull()1059 public void onRoutingTableFull() throws RemoteException { 1060 mCallbackMap.forEach((cb, ex) -> 1061 handleVoidCallback(null, 1062 (Object input) -> cb.onRoutingTableFull(), ex)); 1063 } 1064 1065 @Override onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer)1066 public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer) 1067 throws RemoteException { 1068 mCallbackMap.forEach((cb, ex) -> 1069 handleVoid2ArgCallback(packages, new ReceiverWrapper<>(intentConsumer), 1070 cb::onGetOemAppSearchIntent, ex)); 1071 } 1072 1073 @Override onNdefMessage(Tag tag, NdefMessage message, ResultReceiver hasOemExecutableContent)1074 public void onNdefMessage(Tag tag, NdefMessage message, 1075 ResultReceiver hasOemExecutableContent) throws RemoteException { 1076 mCallbackMap.forEach((cb, ex) -> { 1077 synchronized (mLock) { 1078 final long identity = Binder.clearCallingIdentity(); 1079 try { 1080 ex.execute(() -> cb.onNdefMessage( 1081 tag, message, new ReceiverWrapper<>(hasOemExecutableContent))); 1082 } catch (RuntimeException exception) { 1083 throw exception; 1084 } finally { 1085 Binder.restoreCallingIdentity(identity); 1086 } 1087 } 1088 }); 1089 } 1090 1091 @Override onLaunchHceAppChooserActivity(String selectedAid, List<ApduServiceInfo> services, ComponentName failedComponent, String category)1092 public void onLaunchHceAppChooserActivity(String selectedAid, 1093 List<ApduServiceInfo> services, 1094 ComponentName failedComponent, String category) 1095 throws RemoteException { 1096 mCallbackMap.forEach((cb, ex) -> { 1097 synchronized (mLock) { 1098 final long identity = Binder.clearCallingIdentity(); 1099 try { 1100 ex.execute(() -> cb.onLaunchHceAppChooserActivity( 1101 selectedAid, services, failedComponent, category)); 1102 } catch (RuntimeException exception) { 1103 throw exception; 1104 } finally { 1105 Binder.restoreCallingIdentity(identity); 1106 } 1107 } 1108 }); 1109 } 1110 1111 @Override onLaunchHceTapAgainActivity(ApduServiceInfo service, String category)1112 public void onLaunchHceTapAgainActivity(ApduServiceInfo service, String category) 1113 throws RemoteException { 1114 mCallbackMap.forEach((cb, ex) -> 1115 handleVoid2ArgCallback(service, category, cb::onLaunchHceTapAgainDialog, ex)); 1116 } 1117 1118 @Override onLogEventNotified(OemLogItems item)1119 public void onLogEventNotified(OemLogItems item) throws RemoteException { 1120 mCallbackMap.forEach((cb, ex) -> 1121 handleVoidCallback(item, cb::onLogEventNotified, ex)); 1122 } 1123 1124 @Override onExtractOemPackages(NdefMessage message, ResultReceiver packageConsumer)1125 public void onExtractOemPackages(NdefMessage message, ResultReceiver packageConsumer) 1126 throws RemoteException { 1127 mCallbackMap.forEach((cb, ex) -> 1128 handleVoid2ArgCallback(message, 1129 new ReceiverWrapper<>(packageConsumer), 1130 cb::onExtractOemPackages, ex)); 1131 } 1132 handleVoidCallback( T input, Consumer<T> callbackMethod, Executor executor)1133 private <T> void handleVoidCallback( 1134 T input, Consumer<T> callbackMethod, Executor executor) { 1135 synchronized (mLock) { 1136 final long identity = Binder.clearCallingIdentity(); 1137 try { 1138 executor.execute(() -> callbackMethod.accept(input)); 1139 } catch (RuntimeException ex) { 1140 throw ex; 1141 } finally { 1142 Binder.restoreCallingIdentity(identity); 1143 } 1144 } 1145 } 1146 handleVoid2ArgCallback( T1 input1, T2 input2, BiConsumer<T1, T2> callbackMethod, Executor executor)1147 private <T1, T2> void handleVoid2ArgCallback( 1148 T1 input1, T2 input2, BiConsumer<T1, T2> callbackMethod, Executor executor) { 1149 synchronized (mLock) { 1150 final long identity = Binder.clearCallingIdentity(); 1151 try { 1152 executor.execute(() -> callbackMethod.accept(input1, input2)); 1153 } catch (RuntimeException ex) { 1154 throw ex; 1155 } finally { 1156 Binder.restoreCallingIdentity(identity); 1157 } 1158 } 1159 } 1160 handleNonVoidCallbackWithInput( S defaultValue, T input, Function<T, S> callbackMethod)1161 private <S, T> S handleNonVoidCallbackWithInput( 1162 S defaultValue, T input, Function<T, S> callbackMethod) throws RemoteException { 1163 synchronized (mLock) { 1164 final long identity = Binder.clearCallingIdentity(); 1165 S result = defaultValue; 1166 try { 1167 ExecutorService executor = Executors.newSingleThreadExecutor(); 1168 FutureTask<S> futureTask = new FutureTask<>(() -> callbackMethod.apply(input)); 1169 var unused = executor.submit(futureTask); 1170 try { 1171 result = futureTask.get( 1172 OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS); 1173 } catch (ExecutionException | InterruptedException e) { 1174 e.printStackTrace(); 1175 } catch (TimeoutException e) { 1176 Log.w(TAG, "Callback timed out: " + callbackMethod); 1177 e.printStackTrace(); 1178 } finally { 1179 executor.shutdown(); 1180 } 1181 } finally { 1182 Binder.restoreCallingIdentity(identity); 1183 } 1184 return result; 1185 } 1186 } 1187 handleNonVoidCallbackWithoutInput(T defaultValue, Supplier<T> callbackMethod)1188 private <T> T handleNonVoidCallbackWithoutInput(T defaultValue, Supplier<T> callbackMethod) 1189 throws RemoteException { 1190 synchronized (mLock) { 1191 final long identity = Binder.clearCallingIdentity(); 1192 T result = defaultValue; 1193 try { 1194 ExecutorService executor = Executors.newSingleThreadExecutor(); 1195 FutureTask<T> futureTask = new FutureTask<>(callbackMethod::get); 1196 var unused = executor.submit(futureTask); 1197 try { 1198 result = futureTask.get( 1199 OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS); 1200 } catch (ExecutionException | InterruptedException e) { 1201 e.printStackTrace(); 1202 } catch (TimeoutException e) { 1203 Log.w(TAG, "Callback timed out: " + callbackMethod); 1204 e.printStackTrace(); 1205 } finally { 1206 executor.shutdown(); 1207 } 1208 } finally { 1209 Binder.restoreCallingIdentity(identity); 1210 } 1211 return result; 1212 } 1213 } 1214 } 1215 routeStringToInt(String route)1216 private @CardEmulation.ProtocolAndTechnologyRoute int routeStringToInt(String route) { 1217 if (route.equals("DH")) { 1218 return PROTOCOL_AND_TECHNOLOGY_ROUTE_DH; 1219 } else if (route.startsWith("eSE")) { 1220 return PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE; 1221 } else if (route.startsWith("SIM")) { 1222 return PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC; 1223 } else if (route.startsWith("NDEF-NFCEE")) { 1224 return PROTOCOL_AND_TECHNOLOGY_ROUTE_NDEF_NFCEE; 1225 } else { 1226 throw new IllegalStateException("Unexpected value: " + route); 1227 } 1228 } 1229 1230 private class ReceiverWrapper<T> implements Consumer<T> { 1231 private final ResultReceiver mResultReceiver; 1232 ReceiverWrapper(ResultReceiver resultReceiver)1233 ReceiverWrapper(ResultReceiver resultReceiver) { 1234 mResultReceiver = resultReceiver; 1235 } 1236 1237 @Override accept(T result)1238 public void accept(T result) { 1239 if (result instanceof Boolean) { 1240 mResultReceiver.send((Boolean) result ? 1 : 0, null); 1241 } else if (result instanceof Intent) { 1242 Bundle bundle = new Bundle(); 1243 bundle.putParcelable("intent", (Intent) result); 1244 mResultReceiver.send(0, bundle); 1245 } else if (result instanceof List<?> list) { 1246 if (list.stream().allMatch(String.class::isInstance)) { 1247 Bundle bundle = new Bundle(); 1248 bundle.putStringArray("packageNames", 1249 list.stream().map(pkg -> (String) pkg).toArray(String[]::new)); 1250 mResultReceiver.send(0, bundle); 1251 } 1252 } 1253 } 1254 1255 @Override andThen(Consumer<? super T> after)1256 public Consumer<T> andThen(Consumer<? super T> after) { 1257 return Consumer.super.andThen(after); 1258 } 1259 } 1260 } 1261