diff options
-rw-r--r-- | nfc/api/system-current.txt | 4 | ||||
-rw-r--r-- | nfc/java/android/nfc/INfcOemExtensionCallback.aidl | 9 | ||||
-rw-r--r-- | nfc/java/android/nfc/NfcOemExtension.java | 136 |
3 files changed, 140 insertions, 9 deletions
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 94231b0facdb..2db90fe13d4d 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -90,7 +90,11 @@ package android.nfc { method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onEnableFinished(int); method public void onEnableStarted(); + method public void onGetOemAppSearchIntent(@NonNull java.util.List<java.lang.String>, @NonNull java.util.function.Consumer<android.content.Intent>); method public void onHceEventReceived(int); + method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String); + method public void onLaunchHceTapAgainDialog(@NonNull android.nfc.cardemulation.ApduServiceInfo, @NonNull String); + method public void onNdefMessage(@NonNull android.nfc.Tag, @NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onReaderOptionChanged(boolean); method public void onRfDiscoveryStarted(boolean); diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl index e49ef7e80705..48c7ee659266 100644 --- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl +++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl @@ -15,9 +15,14 @@ */ package android.nfc; +import android.content.ComponentName; +import android.nfc.cardemulation.ApduServiceInfo; +import android.nfc.NdefMessage; import android.nfc.Tag; import android.os.ResultReceiver; +import java.util.List; + /** * @hide */ @@ -41,4 +46,8 @@ interface INfcOemExtensionCallback { void onCardEmulationActivated(boolean isActivated); void onRfFieldActivated(boolean isActivated); void onRfDiscoveryStarted(boolean isDiscoveryStarted); + void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer); + void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent); + void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category); + void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category); } diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 6d5c069bca3a..8484dcafa700 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -23,8 +23,12 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.nfc.cardemulation.ApduServiceInfo; import android.os.Binder; +import android.os.Bundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; @@ -306,6 +310,60 @@ public final class NfcOemExtension { * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle. */ void onRfDiscoveryStarted(boolean isDiscoveryStarted); + + /** + * Gets the intent to find the OEM package in the OEM App market. If the consumer returns + * {@code null} or a timeout occurs, the intent from the first available package will be + * used instead. + * + * @param packages the OEM packages name stored in the tag + * @param intentConsumer The {@link Consumer} to be completed. + * The {@link Consumer#accept(Object)} should be called with + * the Intent required. + * + */ + void onGetOemAppSearchIntent(@NonNull List<String> packages, + @NonNull Consumer<Intent> intentConsumer); + + /** + * Checks if the NDEF message contains any specific OEM package executable content + * + * @param tag the {@link android.nfc.Tag Tag} + * @param message NDEF Message to read from tag + * @param hasOemExecutableContent The {@link Consumer} to be completed. If there is + * OEM package executable content, the + * {@link Consumer#accept(Object)} should be called with + * {@link Boolean#TRUE}, otherwise call with + * {@link Boolean#FALSE}. + */ + void onNdefMessage(@NonNull Tag tag, @NonNull NdefMessage message, + @NonNull Consumer<Boolean> hasOemExecutableContent); + + /** + * Callback to indicate the app chooser activity should be launched for handling CE + * transaction. This is invoked for example when there are more than 1 app installed that + * can handle the HCE transaction. OEMs can launch the Activity based + * on their requirement. + * + * @param selectedAid the selected AID from APDU + * @param services {@link ApduServiceInfo} of the service triggering the activity + * @param failedComponent the component failed to be resolved + * @param category the category of the service + */ + void onLaunchHceAppChooserActivity(@NonNull String selectedAid, + @NonNull List<ApduServiceInfo> services, + @NonNull ComponentName failedComponent, + @NonNull String category); + + /** + * Callback to indicate tap again dialog should be launched for handling HCE transaction. + * This is invoked for example when a CE service needs the device to unlocked before + * handling the transaction. OEMs can launch the Activity based on their requirement. + * + * @param service {@link ApduServiceInfo} of the service triggering the dialog + * @param category the category of the service + */ + void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category); } @@ -562,25 +620,25 @@ public final class NfcOemExtension { public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException { mCallbackMap.forEach((cb, ex) -> handleVoidCallback( - new ReceiverWrapper(isSkipped), cb::onApplyRouting, ex)); + new ReceiverWrapper<>(isSkipped), cb::onApplyRouting, ex)); } @Override public void onNdefRead(ResultReceiver isSkipped) throws RemoteException { mCallbackMap.forEach((cb, ex) -> handleVoidCallback( - new ReceiverWrapper(isSkipped), cb::onNdefRead, ex)); + new ReceiverWrapper<>(isSkipped), cb::onNdefRead, ex)); } @Override public void onEnable(ResultReceiver isAllowed) throws RemoteException { mCallbackMap.forEach((cb, ex) -> handleVoidCallback( - new ReceiverWrapper(isAllowed), cb::onEnable, ex)); + new ReceiverWrapper<>(isAllowed), cb::onEnable, ex)); } @Override public void onDisable(ResultReceiver isAllowed) throws RemoteException { mCallbackMap.forEach((cb, ex) -> handleVoidCallback( - new ReceiverWrapper(isAllowed), cb::onDisable, ex)); + new ReceiverWrapper<>(isAllowed), cb::onDisable, ex)); } @Override public void onBootStarted() throws RemoteException { @@ -616,7 +674,7 @@ public final class NfcOemExtension { public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException { mCallbackMap.forEach((cb, ex) -> handleVoidCallback( - new ReceiverWrapper(isSkipped), cb::onTagDispatch, ex)); + new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex)); } @Override public void onRoutingChanged() throws RemoteException { @@ -635,6 +693,59 @@ public final class NfcOemExtension { handleVoidCallback(enabled, cb::onReaderOptionChanged, ex)); } + @Override + public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer) + throws RemoteException { + mCallbackMap.forEach((cb, ex) -> + handleVoid2ArgCallback(packages, new ReceiverWrapper<>(intentConsumer), + cb::onGetOemAppSearchIntent, ex)); + } + + @Override + public void onNdefMessage(Tag tag, NdefMessage message, + ResultReceiver hasOemExecutableContent) throws RemoteException { + mCallbackMap.forEach((cb, ex) -> { + synchronized (mLock) { + final long identity = Binder.clearCallingIdentity(); + try { + ex.execute(() -> cb.onNdefMessage( + tag, message, new ReceiverWrapper<>(hasOemExecutableContent))); + } catch (RuntimeException exception) { + throw exception; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }); + } + + @Override + public void onLaunchHceAppChooserActivity(String selectedAid, + List<ApduServiceInfo> services, + ComponentName failedComponent, String category) + throws RemoteException { + mCallbackMap.forEach((cb, ex) -> { + synchronized (mLock) { + final long identity = Binder.clearCallingIdentity(); + try { + ex.execute(() -> cb.onLaunchHceAppChooserActivity( + selectedAid, services, failedComponent, category)); + } catch (RuntimeException exception) { + throw exception; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }); + } + + @Override + public void onLaunchHceTapAgainActivity(ApduServiceInfo service, String category) + throws RemoteException { + mCallbackMap.forEach((cb, ex) -> + handleVoid2ArgCallback(service, category, cb::onLaunchHceTapAgainDialog, ex)); + } + private <T> void handleVoidCallback( T input, Consumer<T> callbackMethod, Executor executor) { synchronized (mLock) { @@ -718,7 +829,7 @@ public final class NfcOemExtension { } } - private class ReceiverWrapper implements Consumer<Boolean> { + private class ReceiverWrapper<T> implements Consumer<T> { private final ResultReceiver mResultReceiver; ReceiverWrapper(ResultReceiver resultReceiver) { @@ -726,12 +837,19 @@ public final class NfcOemExtension { } @Override - public void accept(Boolean result) { - mResultReceiver.send(result ? 1 : 0, null); + public void accept(T result) { + if (result instanceof Boolean) { + mResultReceiver.send((Boolean) result ? 1 : 0, null); + } else if (result instanceof Intent) { + Bundle bundle = new Bundle(); + bundle.putParcelable("intent", (Intent) result); + mResultReceiver.send(0, bundle); + } + } @Override - public Consumer<Boolean> andThen(Consumer<? super Boolean> after) { + public Consumer<T> andThen(Consumer<? super T> after) { return Consumer.super.andThen(after); } } |