Add NFC reader-mode API.

Allows applications to temporarily force the NFC controller
to only do tag discovery. This will allow Android applications
to read and interact with devices that employ HCE.

Bug: 10360259
Change-Id: I709ead9a26f8e6ae8582cc295d82bd896e7c5bba
diff --git a/api/current.txt b/api/current.txt
index 42ba6b8..d1fc562 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15042,8 +15042,10 @@
   public final class NfcAdapter {
     method public void disableForegroundDispatch(android.app.Activity);
     method public deprecated void disableForegroundNdefPush(android.app.Activity);
+    method public void disableReaderMode(android.app.Activity);
     method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]);
     method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
+    method public void enableReaderMode(android.app.Activity, int);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
     method public boolean isEnabled();
     method public boolean isNdefPushEnabled();
@@ -15060,6 +15062,12 @@
     field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
     field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
     field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
+    field public static final int FLAG_READER_KOVIO = 16; // 0x10
+    field public static final int FLAG_READER_NFC_A = 1; // 0x1
+    field public static final int FLAG_READER_NFC_B = 2; // 0x2
+    field public static final int FLAG_READER_NFC_F = 4; // 0x4
+    field public static final int FLAG_READER_NFC_V = 8; // 0x8
+    field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80
     field public static final int STATE_OFF = 1; // 0x1
     field public static final int STATE_ON = 3; // 0x3
     field public static final int STATE_TURNING_OFF = 4; // 0x4
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9c97659..15d0475 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -48,5 +48,6 @@
 
     void dispatch(in Tag tag);
 
+    void setReaderMode (IBinder b, int flags);
     void setP2pModes(int initatorModes, int targetModes);
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 10183c0..d0d943c 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.Application;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Log;
@@ -111,6 +112,9 @@
         NfcAdapter.CreateBeamUrisCallback uriCallback = null;
         Uri[] uris = null;
         int flags = 0;
+        int readerModeFlags = 0;
+        Binder token;
+
         public NfcActivityState(Activity activity) {
             if (activity.getWindow().isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
@@ -120,6 +124,7 @@
             resumed = activity.isResumed();
 
             this.activity = activity;
+            this.token = new Binder();
             registerApplication(activity.getApplication());
         }
         public void destroy() {
@@ -131,6 +136,8 @@
             onNdefPushCompleteCallback = null;
             uriCallback = null;
             uris = null;
+            readerModeFlags = 0;
+            token = null;
         }
         @Override
         public String toString() {
@@ -190,6 +197,44 @@
         mDefaultEvent = new NfcEvent(mAdapter);
     }
 
+    public void enableReaderMode(Activity activity, int flags) {
+        boolean isResumed;
+        Binder token;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.readerModeFlags = flags;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            setReaderMode(token, flags);
+        }
+    }
+
+    public void disableReaderMode(Activity activity) {
+        boolean isResumed;
+        Binder token;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.readerModeFlags = 0;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            setReaderMode(token, 0);
+        }
+
+    }
+
+    public void setReaderMode(Binder token, int flags) {
+        if (DBG) Log.d(TAG, "Setting reader mode");
+        try {
+            NfcAdapter.sService.setReaderMode(token, flags);
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
     public void setNdefPushContentUri(Activity activity, Uri[] uris) {
         boolean isResumed;
         synchronized (NfcActivityManager.this) {
@@ -341,11 +386,18 @@
     /** Callback from Activity life-cycle, on main thread */
     @Override
     public void onActivityResumed(Activity activity) {
+        int readerModeFlags = 0;
+        Binder token;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findActivityState(activity);
             if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
             if (state == null) return;
             state.resumed = true;
+            token = state.token;
+            readerModeFlags = state.readerModeFlags;
+        }
+        if (readerModeFlags != 0) {
+            setReaderMode(token, readerModeFlags);
         }
         requestNfcServiceCallback();
     }
@@ -353,11 +405,19 @@
     /** Callback from Activity life-cycle, on main thread */
     @Override
     public void onActivityPaused(Activity activity) {
+        boolean readerModeFlagsSet;
+        Binder token;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findActivityState(activity);
             if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
             if (state == null) return;
             state.resumed = false;
+            token = state.token;
+            readerModeFlagsSet = state.readerModeFlags != 0;
+        }
+        if (readerModeFlagsSet) {
+            // Restore default p2p modes
+            setReaderMode(token, 0);
         }
     }
 
@@ -381,5 +441,4 @@
             }
         }
     }
-
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 2a4f93c..fa0c1f6 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -195,6 +195,50 @@
     public static final int STATE_ON = 3;
     public static final int STATE_TURNING_OFF = 4;
 
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, int)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-A technology.
+     */
+    public static final int FLAG_READER_NFC_A = 0x1;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, int)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-B technology.
+     */
+    public static final int FLAG_READER_NFC_B = 0x2;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, int)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-F technology.
+     */
+    public static final int FLAG_READER_NFC_F = 0x4;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, int)}.
+     * <p>
+     * Setting this flag enables polling for Nfc-V (ISO15693) technology.
+     */
+    public static final int FLAG_READER_NFC_V = 0x8;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, int)}.
+     * <p>
+     * Setting this flag enables polling for Kovio technology.
+     */
+    public static final int FLAG_READER_KOVIO = 0x10;
+
+    /**
+     * Flag for use with {@link #enableReaderMode(Activity, int)}.
+     * <p>
+     * Setting this flag allows the caller to prevent the
+     * platform from performing an NDEF check on the tags it
+     * finds.
+     */
+    public static final int FLAG_READER_SKIP_NDEF_CHECK = 0x80;
+
     /** @hide */
     public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
 
@@ -1112,6 +1156,44 @@
     }
 
     /**
+     * Limit the NFC controller to reader mode while this Activity is in the foreground.
+     *
+     * <p>In this mode the NFC controller will only act as an NFC tag reader/writer,
+     * thus disabling any peer-to-peer (Android Beam) and card-emulation modes of
+     * the NFC adapter on this device.
+     *
+     * <p>Use {@link #FLAG_READER_SKIP_NDEF_CHECK} to prevent the platform from
+     * performing any NDEF checks in reader mode. Note that this will prevent the
+     * {@link Ndef} tag technology from being enumerated on the tag, and that
+     * NDEF-based tag dispatch will not be functional.
+     *
+     * <p>It is recommended to combine this method with
+     * {@link #enableForegroundDispatch(Activity, PendingIntent, IntentFilter[], String[][])
+     * to ensure that tags are delivered to this activity.
+     *
+     * <p>For interacting with tags that are emulated on another Android device
+     * using Android's host-based card-emulation, the recommended flags are
+     * {@link #FLAG_READER_NFC_A} and {@link #FLAG_READER_SKIP_NDEF_CHECK}.
+     *
+     * @param activity the Activity that requests the adapter to be in reader mode
+     * @param flags Flags indicating poll technologies and other optional parameters
+     */
+    public void enableReaderMode(Activity activity, int flags) {
+        mNfcActivityManager.enableReaderMode(activity, flags);
+    }
+
+    /**
+     * Restore the NFC adapter to normal mode of operation: supporting
+     * peer-to-peer (Android Beam), card emulation, and polling for
+     * all supported tag technologies.
+     *
+     * @param activity the Activity that currently has reader mode enabled
+     */
+    public void disableReaderMode(Activity activity) {
+        mNfcActivityManager.disableReaderMode(activity);
+    }
+
+    /**
      * Enable NDEF message push over NFC while this Activity is in the foreground.
      *
      * <p>You must explicitly call this method every time the activity is