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

Commit 9a644bdc authored by Steve Pomeroy's avatar Steve Pomeroy Committed by Gerrit Code Review
Browse files

Merge "Add callbacks to NFC Event Listener" into main

parents d6c1a232 0cb319fe
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));
                            });
                    }
                }