Changes for access control.

The package name is now required when using the
NFC extras APIs so the context is stored away
and used to derive the package name to be sent
to the NfcService.

Bug: 4515759
Change-Id: I1a3aba3fc026e0090a914b0686fc4b8dec25b927
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 016af58..0b93ad0 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -32,7 +32,7 @@
 interface INfcAdapter
 {
     INfcTag getNfcTagInterface();
-    INfcAdapterExtras getNfcAdapterExtrasInterface();
+    INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
 
     int getState();
     boolean disable();
diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/core/java/android/nfc/INfcAdapterExtras.aidl
index 0c2a2fd..2b9d4f0 100644
--- a/core/java/android/nfc/INfcAdapterExtras.aidl
+++ b/core/java/android/nfc/INfcAdapterExtras.aidl
@@ -23,10 +23,10 @@
  * {@hide}
  */
 interface INfcAdapterExtras {
-    Bundle open(IBinder b);
-    Bundle close();
-    Bundle transceive(in byte[] data_in);
-    int getCardEmulationRoute();
-    void setCardEmulationRoute(int route);
-    void authenticate(in byte[] token);
+    Bundle open(in String pkg, IBinder b);
+    Bundle close(in String pkg, IBinder b);
+    Bundle transceive(in String pkg, in byte[] data_in);
+    int getCardEmulationRoute(in String pkg);
+    void setCardEmulationRoute(in String pkg, int route);
+    void authenticate(in String pkg, in byte[] token);
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index fe0106d..a9f1685 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,8 @@
 
 package android.nfc;
 
+import java.util.HashMap;
+
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Activity;
@@ -197,15 +199,21 @@
     static INfcTag sTagService;
 
     /**
-     * NfcAdapter is currently a singleton, and does not require a context.
-     * However all the public API's are future-proofed to require a context.
-     * If we start using that then we'll need to keep a HashMap of
-     * Context.getApplicationContext() -> NfcAdapter, such that NfcAdapter
-     * is a singleton within each application context.
+     * The NfcAdapter object for each application context.
+     * There is a 1-1 relationship between application context and
+     * NfcAdapter object.
      */
-    static NfcAdapter sSingleton;  // protected by NfcAdapter.class
+    static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class
+
+    /**
+     * NfcAdapter used with a null context. This ctor was deprecated but we have
+     * to support it for backwards compatibility. New methods that require context
+     * might throw when called on the null-context NfcAdapter.
+     */
+    static NfcAdapter sNullContextNfcAdapter;  // protected by NfcAdapter.class
 
     final NfcActivityManager mNfcActivityManager;
+    final Context mContext;
 
     /**
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
@@ -280,12 +288,12 @@
     }
 
     /**
-     * Returns the singleton, or throws if NFC is not available.
+     * Returns the NfcAdapter for application context,
+     * or throws if NFC is not available.
+     * @hide
      */
-    static synchronized NfcAdapter getSingleton() {
+    public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
-            sIsInitialized = true;
-
             /* is this device meant to have NFC */
             if (!hasNfcFeature()) {
                 Log.v(TAG, "this device does not have NFC support");
@@ -303,12 +311,21 @@
                 Log.e(TAG, "could not retrieve NFC Tag service");
                 throw new UnsupportedOperationException();
             }
-            sSingleton = new NfcAdapter();
+
+            sIsInitialized = true;
         }
-        if (sSingleton == null) {
-            throw new UnsupportedOperationException();
+        if (context == null) {
+            if (sNullContextNfcAdapter == null) {
+                sNullContextNfcAdapter = new NfcAdapter(null);
+            }
+            return sNullContextNfcAdapter;
         }
-        return sSingleton;
+        NfcAdapter adapter = sNfcAdapters.get(context);
+        if (adapter == null) {
+            adapter = new NfcAdapter(context);
+            sNfcAdapters.put(context, adapter);
+        }
+        return adapter;
     }
 
     /** get handle to NFC service interface */
@@ -336,6 +353,10 @@
      * @return the default NFC adapter, or null if no NFC adapter exists
      */
     public static NfcAdapter getDefaultAdapter(Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null");
+        }
+        context = context.getApplicationContext();
         /* use getSystemService() instead of just instantiating to take
          * advantage of the context's cached NfcManager & NfcAdapter */
         NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
@@ -343,25 +364,30 @@
     }
 
     /**
-     * Get a handle to the default NFC Adapter on this Android device.
-     * <p>
-     * Most Android devices will only have one NFC Adapter (NFC Controller).
-     *
-     * @return the default NFC adapter, or null if no NFC adapter exists
+     * Legacy NfcAdapter getter, always use {@link #getDefaultAdapter(Context)} instead.<p>
+     * This method was deprecated at API level 10 (Gingerbread MR1) because a context is required
+     * for many NFC API methods. Those methods will fail when called on an NfcAdapter
+     * object created from this method.<p>
      * @deprecated use {@link #getDefaultAdapter(Context)}
      */
     @Deprecated
     public static NfcAdapter getDefaultAdapter() {
         Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
                 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
-        return getSingleton();
+
+        return NfcAdapter.getNfcAdapter(null);
+    }
+
+    NfcAdapter(Context context) {
+        mContext = context;
+        mNfcActivityManager = new NfcActivityManager(this);
     }
 
     /**
-     * Does not currently need a context.
+     * @hide
      */
-    NfcAdapter() {
-        mNfcActivityManager = new NfcActivityManager(this);
+    public Context getContext() {
+        return mContext;
     }
 
     /**
@@ -875,8 +901,12 @@
      * @hide
      */
     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " NFC extras APIs");
+        }
         try {
-            return sService.getNfcAdapterExtrasInterface();
+            return sService.getNfcAdapterExtrasInterface(mContext.getPackageName());
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return null;
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 300ab45..6ec2e21 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -39,8 +39,9 @@
      */
     public NfcManager(Context context) {
         NfcAdapter adapter;
+        context = context.getApplicationContext();
         try {
-            adapter = NfcAdapter.getSingleton();
+            adapter = NfcAdapter.getNfcAdapter(context);
         } catch (UnsupportedOperationException e) {
             adapter = null;
         }
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
index 99cbb86..62213de 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
@@ -16,8 +16,7 @@
 
 package com.android.nfc_extras;
 
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
 import android.nfc.INfcAdapterExtras;
 import android.nfc.NfcAdapter;
 import android.os.RemoteException;
@@ -60,10 +59,14 @@
     // best effort recovery
     private static NfcAdapter sAdapter;
     private static INfcAdapterExtras sService;
-    private static NfcAdapterExtras sSingleton;
-    private static NfcExecutionEnvironment sEmbeddedEe;
-    private static CardEmulationRoute sRouteOff;
-    private static CardEmulationRoute sRouteOnWhenScreenOn;
+    private static final CardEmulationRoute ROUTE_OFF =
+            new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
+
+    private final NfcExecutionEnvironment mEmbeddedEe;
+    private final CardEmulationRoute mRouteOnWhenScreenOn;
+
+    final Context mContext;
+    final String mPackageName;
 
     /** get service handles */
     private static void initService() {
@@ -84,31 +87,35 @@
      * @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
      */
     public static NfcAdapterExtras get(NfcAdapter adapter) {
-        synchronized(NfcAdapterExtras.class) {
-            if (sSingleton == null) {
+        Context context = adapter.getContext();
+        if (context == null) {
+            throw new UnsupportedOperationException(
+                    "You must pass a context to your NfcAdapter to use the NFC extras APIs");
+        }
+
+        synchronized (NfcAdapterExtras.class) {
+            if (sService == null) {
                 try {
                     sAdapter = adapter;
-                    sSingleton = new NfcAdapterExtras();
-                    sEmbeddedEe = new NfcExecutionEnvironment(sSingleton);
-                    sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
-                    sRouteOnWhenScreenOn = new CardEmulationRoute(
-                            CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe);
                     initService();
                 } finally {
                     if (sService == null) {
-                        sRouteOnWhenScreenOn = null;
-                        sRouteOff = null;
-                        sEmbeddedEe = null;
-                        sSingleton = null;
                         sAdapter = null;
                     }
                 }
             }
-            return sSingleton;
         }
+
+        return new NfcAdapterExtras(context);
     }
 
-    private NfcAdapterExtras() {}
+    private NfcAdapterExtras(Context context) {
+        mContext = context.getApplicationContext();
+        mPackageName = context.getPackageName();
+        mEmbeddedEe = new NfcExecutionEnvironment(this);
+        mRouteOnWhenScreenOn = new CardEmulationRoute(CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON,
+                mEmbeddedEe);
+    }
 
     /**
      * Immutable data class that describes a card emulation route.
@@ -166,18 +173,16 @@
      *
      * <p class="note">
      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
-     *
-     * @return
      */
     public CardEmulationRoute getCardEmulationRoute() {
         try {
-            int route = sService.getCardEmulationRoute();
+            int route = sService.getCardEmulationRoute(mPackageName);
             return route == CardEmulationRoute.ROUTE_OFF ?
-                    sRouteOff :
-                    sRouteOnWhenScreenOn;
+                    ROUTE_OFF :
+                    mRouteOnWhenScreenOn;
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
-            return sRouteOff;
+            return ROUTE_OFF;
         }
     }
 
@@ -189,11 +194,11 @@
      * <p class="note">
      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
      *
-     * @param route a {@link #CardEmulationRoute}
+     * @param route a {@link CardEmulationRoute}
      */
     public void setCardEmulationRoute(CardEmulationRoute route) {
         try {
-            sService.setCardEmulationRoute(route.route);
+            sService.setCardEmulationRoute(mPackageName, route.route);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
@@ -201,7 +206,7 @@
 
     /**
      * Get the {@link NfcExecutionEnvironment} that is embedded with the
-     * {@link NFcAdapter}.
+     * {@link NfcAdapter}.
      *
      * <p class="note">
      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
@@ -209,7 +214,7 @@
      * @return a {@link NfcExecutionEnvironment}, or null if there is no embedded NFC-EE
      */
     public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
-        return sEmbeddedEe;
+        return mEmbeddedEe;
     }
 
     /**
@@ -218,12 +223,12 @@
      * Some implementations of NFC Adapter Extras may require applications
      * to authenticate with a token, before using other methods.
      *
-     * @param a implementation specific token
-     * @throws a {@link java.lang.SecurityException} if authentication failed
+     * @param token a implementation specific token
+     * @throws java.lang.SecurityException if authentication failed
      */
     public void authenticate(byte[] token) {
         try {
-            sService.authenticate(token);
+            sService.authenticate(mPackageName, token);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
index 63c2de2..f47327a 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
@@ -16,20 +16,17 @@
 
 package com.android.nfc_extras;
 
-import java.io.IOException;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.content.Context;
-import android.nfc.INfcAdapterExtras;
-import android.nfc.NfcAdapter;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.RemoteException;
 
+import java.io.IOException;
+
 public class NfcExecutionEnvironment {
     private final NfcAdapterExtras mExtras;
+    private final Binder mToken;
 
     /**
      * Broadcast Action: An ISO-DEP AID was selected.
@@ -115,6 +112,7 @@
 
     NfcExecutionEnvironment(NfcAdapterExtras extras) {
         mExtras = extras;
+        mToken = new Binder();
     }
 
     /**
@@ -133,7 +131,7 @@
      */
     public void open() throws IOException {
         try {
-            Bundle b = mExtras.getService().open(new Binder());
+            Bundle b = mExtras.getService().open(mExtras.mPackageName, mToken);
             throwBundle(b);
         } catch (RemoteException e) {
             mExtras.attemptDeadServiceRecovery(e);
@@ -151,7 +149,7 @@
      */
     public void close() throws IOException {
         try {
-            throwBundle(mExtras.getService().close());
+            throwBundle(mExtras.getService().close(mExtras.mPackageName, mToken));
         } catch (RemoteException e) {
             mExtras.attemptDeadServiceRecovery(e);
             throw new IOException("NFC Service was dead");
@@ -169,7 +167,7 @@
     public byte[] transceive(byte[] in) throws IOException {
         Bundle b;
         try {
-            b = mExtras.getService().transceive(in);
+            b = mExtras.getService().transceive(mExtras.mPackageName, in);
         } catch (RemoteException e) {
             mExtras.attemptDeadServiceRecovery(e);
             throw new IOException("NFC Service was dead, need to re-open");