Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0cb319fe authored by Steve Pomeroy's avatar Steve Pomeroy
Browse files

Add callbacks to NFC Event Listener

Bug: 356447790
Test: CTS tests
Flag: android.nfc.Flags.nfcEventListener
Change-Id: I59d4f3b119849360f8258596dd0c5d03ae413f99
parent 2be55aa2
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -228,6 +228,10 @@ package android.nfc.cardemulation {
    field public static final String CATEGORY_PAYMENT = "payment";
    field public static final String EXTRA_CATEGORY = "category";
    field public static final String EXTRA_SERVICE_COMPONENT = "component";
    field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_COMMAND_TIMEOUT = 3; // 0x3
    field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1; // 0x1
    field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2; // 0x2
    field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0; // 0x0
    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3; // 0x3
    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0
    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1
@@ -239,8 +243,13 @@ package android.nfc.cardemulation {
  }

  @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventListener {
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidConflictOccurred(@NonNull String);
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidNotRouted(@NonNull String);
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onInternalErrorReported(int);
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onNfcStateChanged(int);
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onObserveModeStateChanged(boolean);
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onPreferredServiceChanged(boolean);
    method @FlaggedApi("android.nfc.nfc_event_listener") public default void onRemoteFieldChanged(boolean);
  }

  public abstract class HostApduService extends android.app.Service {
+5 −0
Original line number Diff line number Diff line
@@ -8,4 +8,9 @@ import android.nfc.ComponentNameAndUser;
oneway interface INfcEventListener {
    void onPreferredServiceChanged(in ComponentNameAndUser ComponentNameAndUser);
    void onObserveModeStateChanged(boolean isEnabled);
    void onAidConflictOccurred(in String aid);
    void onAidNotRouted(in String aid);
    void onNfcStateChanged(in int nfcState);
    void onRemoteFieldChanged(boolean isDetected);
    void onInternalErrorReported(in int errorType);
}
 No newline at end of file
+132 −11
Original line number Diff line number Diff line
@@ -1144,6 +1144,40 @@ public final class CardEmulation {
        };
    }

    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
    public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0;

    /**
     * This error is reported when the NFC command watchdog restarts the NFC stack.
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
    public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1;

    /**
     * This error is reported when the NFC controller does not respond or there's an NCI transport
     * error.
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
    public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2;

    /**
     * This error is reported when the NFC stack times out while waiting for a response to a command
     * sent to the NFC hardware.
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
    public static final int NFC_INTERNAL_ERROR_COMMAND_TIMEOUT = 3;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
    @IntDef(prefix = "NFC_INTERNAL_ERROR_", value = {
            NFC_INTERNAL_ERROR_UNKNOWN,
            NFC_INTERNAL_ERROR_NFC_CRASH_RESTART,
            NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR,
            NFC_INTERNAL_ERROR_COMMAND_TIMEOUT,
    })
    public @interface NfcInternalErrorType {}

    /** Listener for preferred service state changes. */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
    public interface NfcEventListener {
@@ -1166,6 +1200,57 @@ public final class CardEmulation {
         */
        @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
        default void onObserveModeStateChanged(boolean isEnabled) {}

        /**
         * This method is called when an AID conflict is detected during an NFC transaction. This
         * can happen when multiple services are registered for the same AID.
         *
         * @param aid The AID that is in conflict
         */
        @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
        default void onAidConflictOccurred(@NonNull String aid) {}

        /**
         * This method is called when an AID is not routed to any service during an NFC
         * transaction. This can happen when no service is registered for the given AID.
         *
         * @param aid the AID that was not routed
         */
        @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
        default void onAidNotRouted(@NonNull String aid) {}

        /**
         * This method is called when the NFC state changes.
         *
         * @see NfcAdapter#getAdapterState()
         *
         * @param state The new NFC state
         */
        @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
        default void onNfcStateChanged(@NfcAdapter.AdapterState int state) {}
        /**
         * This method is called when the NFC controller is in card emulation mode and an NFC
         * reader's field is either detected or lost.
         *
         * @param isDetected true if an NFC reader is detected, false if it is lost
         */
        @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
        default void onRemoteFieldChanged(boolean isDetected) {}

        /**
         * This method is called when an internal error is reported by the NFC stack.
         *
         * No action is required in response to these events as the NFC stack will automatically
         * attempt to recover. These errors are reported for informational purposes only.
         *
         * Note that these errors can be reported when performing various internal NFC operations
         * (such as during device shutdown) and cannot always be explicitly correlated with NFC
         * transaction failures.
         *
         * @param errorType The type of the internal error
         */
        @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
        default void onInternalErrorReported(@NfcInternalErrorType int errorType) {}
    }

    private final ArrayMap<NfcEventListener, Executor> mNfcEventListeners = new ArrayMap<>();
@@ -1185,24 +1270,60 @@ public final class CardEmulation {
                                            mContext.getPackageName(),
                                            componentNameAndUser.getComponentName()
                                                    .getPackageName());
                    synchronized (mNfcEventListeners) {
                        mNfcEventListeners.forEach(
                                (listener, executor) -> {
                                    executor.execute(
                                            () -> listener.onPreferredServiceChanged(isPreferred));
                                });
                    }
                    callListeners(listener -> listener.onPreferredServiceChanged(isPreferred));
                }

                public void onObserveModeStateChanged(boolean isEnabled) {
                    if (!android.nfc.Flags.nfcEventListener()) {
                        return;
                    }
                    callListeners(listener -> listener.onObserveModeStateChanged(isEnabled));
                }

                public void onAidConflictOccurred(String aid) {
                    if (!android.nfc.Flags.nfcEventListener()) {
                        return;
                    }
                    callListeners(listener -> listener.onAidConflictOccurred(aid));
                }

                public void onAidNotRouted(String aid) {
                    if (!android.nfc.Flags.nfcEventListener()) {
                        return;
                    }
                    callListeners(listener -> listener.onAidNotRouted(aid));
                }

                public void onNfcStateChanged(int state) {
                    if (!android.nfc.Flags.nfcEventListener()) {
                        return;
                    }
                    callListeners(listener -> listener.onNfcStateChanged(state));
                }

                public void onRemoteFieldChanged(boolean isDetected) {
                    if (!android.nfc.Flags.nfcEventListener()) {
                        return;
                    }
                    callListeners(listener -> listener.onRemoteFieldChanged(isDetected));
                }

                public void onInternalErrorReported(@NfcInternalErrorType int errorType) {
                    if (!android.nfc.Flags.nfcEventListener()) {
                        return;
                    }
                    callListeners(listener -> listener.onInternalErrorReported(errorType));
                }

                interface ListenerCall {
                    void invoke(NfcEventListener listener);
                }

                private void callListeners(ListenerCall listenerCall) {
                    synchronized (mNfcEventListeners) {
                        mNfcEventListeners.forEach(
                            (listener, executor) -> {
                                    executor.execute(
                                            () -> listener.onObserveModeStateChanged(isEnabled));
                                executor.execute(() -> listenerCall.invoke(listener));
                            });
                    }
                }