• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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