nfc(api): Add OEM extension surface

This is added as a separate class to avoid cluttering the public NFC
API surface.

Bug: 330376108
Test: Compiles
Merged-In: Ia74d9e25b69fe3550b167ec83896c01a1fc569b9

Change-Id: Ia74d9e25b69fe3550b167ec83896c01a1fc569b9
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index ece8851..79373a5 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -9,6 +9,7 @@
     method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public android.nfc.NfcOemExtension getNfcOemExtension();
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
@@ -46,6 +47,16 @@
     method @FlaggedApi("android.nfc.nfc_vendor_cmd") public void onVendorNciResponse(@IntRange(from=0, to=15) int, int, @NonNull byte[]);
   }
 
+  @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
+  }
+
+  public static interface NfcOemExtension.Callback {
+    method public void onTagConnected(boolean, @NonNull android.nfc.Tag);
+  }
+
 }
 
 package android.nfc.cardemulation {
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 8fea5af..bde63c3 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -28,6 +28,7 @@
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcFCardEmulation;
+import android.nfc.INfcOemExtensionCallback;
 import android.nfc.INfcUnlockHandler;
 import android.nfc.ITagRemovedCallback;
 import android.nfc.INfcDta;
@@ -91,4 +92,7 @@
     int sendVendorNciMessage(int mt, int gid, int oid, in byte[] payload);
     void registerVendorExtensionCallback(in INfcVendorNciCallback callbacks);
     void unregisterVendorExtensionCallback(in INfcVendorNciCallback callbacks);
+    void registerOemExtensionCallback(INfcOemExtensionCallback callbacks);
+    void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks);
+    void clearPreference();
 }
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
new file mode 100644
index 0000000..6c9096d
--- /dev/null
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcOemExtensionCallback {
+   void onTagConnected(boolean connected, in Tag tag);
+}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 40bbe74..7e0a111 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -556,6 +556,7 @@
     final Context mContext;
     final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers;
     final Object mLock;
+    final NfcOemExtension mNfcOemExtension;
 
     ITagRemovedCallback mTagRemovedListener; // protected by mLock
 
@@ -864,6 +865,7 @@
         mLock = new Object();
         mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService());
         mNfcVendorNciCallbackListener = new NfcVendorNciCallbackListener(getService());
+        mNfcOemExtension = new NfcOemExtension(mContext, this);
     }
 
     /**
@@ -2919,4 +2921,19 @@
         void onVendorNciNotification(
                 @IntRange(from = 9, to = 15) int gid, int oid, @NonNull byte[] payload);
     }
+
+    /**
+     * Returns an instance of {@link NfcOemExtension} associated with {@link NfcAdapter} instance.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @NonNull public NfcOemExtension getNfcOemExtension() {
+        synchronized (sLock) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        return mNfcOemExtension;
+    }
 }
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
new file mode 100644
index 0000000..1eff58c
--- /dev/null
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Used for OEM extension APIs.
+ * This class holds all the APIs and callbacks defined for OEMs/vendors to extend the NFC stack
+ * for their proprietary features.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public final class NfcOemExtension {
+    private static final String TAG = "NfcOemExtension";
+    private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000;
+    private final NfcAdapter mAdapter;
+    private final NfcOemExtensionCallback mOemNfcExtensionCallback;
+    private final Context mContext;
+    private Executor mExecutor = null;
+    private Callback mCallback = null;
+    private final Object mLock = new Object();
+
+    /**
+     * Interface for Oem extensions for NFC.
+     */
+    public interface Callback {
+        /**
+         * Notify Oem to tag is connected or not
+         * ex - if tag is connected  notify cover and Nfctest app if app is in testing mode
+         *
+         * @param connected status of the tag true if tag is connected otherwise false
+         * @param tag Tag details
+         */
+        void onTagConnected(boolean connected, @NonNull Tag tag);
+    }
+
+
+    /**
+     * Constructor to be used only by {@link NfcAdapter}.
+     * @hide
+     */
+    public NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) {
+        mContext = context;
+        mAdapter = adapter;
+        mOemNfcExtensionCallback = new NfcOemExtensionCallback();
+    }
+
+    /**
+     * Register an {@link Callback} to listen for UWB oem extension callbacks
+     * <p>The provided callback will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback oem implementation of {@link Callback}
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Callback callback) {
+        synchronized (mLock) {
+            if (mCallback != null) {
+                Log.e(TAG, "Callback already registered. Unregister existing callback before"
+                        + "registering");
+                throw new IllegalArgumentException();
+            }
+            try {
+                NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
+                mCallback = callback;
+                mExecutor = executor;
+            } catch (RemoteException e) {
+                mAdapter.attemptDeadServiceRecovery(e);
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link Callback}
+     *
+     * <p>The same {@link Callback} object used when calling
+     * {@link #registerCallback(Executor, Callback)} must be used.
+     *
+     * <p>Callbacks are automatically unregistered when an application process goes away
+     *
+     * @param callback oem implementation of {@link Callback}
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void unregisterCallback(@NonNull Callback callback) {
+        synchronized (mLock) {
+            if (mCallback == null || mCallback != callback) {
+                Log.e(TAG, "Callback not registered");
+                throw new IllegalArgumentException();
+            }
+            try {
+                NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
+                mCallback = null;
+                mExecutor = null;
+            } catch (RemoteException e) {
+                mAdapter.attemptDeadServiceRecovery(e);
+            }
+        }
+    }
+
+    /**
+     * Clear NfcService preference, interface method to clear NFC preference values on OEM specific
+     * events. For ex: on soft reset, Nfc default values needs to be overridden by OEM defaults.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void clearPreference() {
+        try {
+            NfcAdapter.sService.clearPreference();
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
+    private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
+        @Override
+        public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
+            synchronized (mLock) {
+                if (mCallback == null || mExecutor == null) {
+                    return;
+                }
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onTagConnected(connected, tag));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+    }
+}
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 44924ae..5879595 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -76,3 +76,11 @@
     description: "Enable NFC vendor command support"
     bug: "289879306"
 }
+
+flag {
+    name: "nfc_oem_extension"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable NFC OEM extension support"
+    bug: "331206243"
+}