Make getSystemService(NFC_SERVICE) the NFC entry point.

This gives NFC service a handle to the application context.

Deprecate NfcAdapter.getDefaultAdapter(), it does not provide a context.
Using this method will print a warning, and will later throw an exception
if a method that requires a context is called. No 2.3 API's will fail, but
new API's that do require a context might fail.

Also add helper NfcAdapter.getDefaultAdapter(Context).

Change-Id: I9a6378de4ef4b61ad922f8d53e64e2a1a1d5d60c
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5998074..7e7cd7a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -72,6 +72,7 @@
 import android.net.Uri;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiManager;
+import android.nfc.NfcManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DropBoxManager;
@@ -201,6 +202,7 @@
     private DevicePolicyManager mDevicePolicyManager = null;
     private UiModeManager mUiModeManager = null;
     private DownloadManager mDownloadManager = null;
+    private NfcManager mNfcManager = null;
 
     private final Object mSync = new Object();
 
@@ -977,6 +979,8 @@
             return getUiModeManager();
         } else if (DOWNLOAD_SERVICE.equals(name)) {
             return getDownloadManager();
+        } else if (NFC_SERVICE.equals(name)) {
+            return getNfcManager();
         }
 
         return null;
@@ -1204,6 +1208,15 @@
         return mDownloadManager;
     }
 
+    private NfcManager getNfcManager() {
+        synchronized (mSync) {
+            if (mNfcManager == null) {
+                mNfcManager = new NfcManager(this);
+            }
+        }
+        return mNfcManager;
+    }
+
     @Override
     public int checkPermission(String permission, int pid, int uid) {
         if (permission == null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a370b33..85c29b8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1551,6 +1551,14 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.nfc.NfcManager} for using NFC.
+     *
+     * @see #getSystemService
+     */
+    public static final String NFC_SERVICE = "nfc";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.net.sip.SipManager} for accessing the SIP related service.
      *
      * @see #getSystemService
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index a1c22bf..d71fdd5 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -19,6 +19,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.ActivityThread;
+import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.os.IBinder;
@@ -29,11 +30,12 @@
 /**
  * Represents the device's local NFC adapter.
  * <p>
- * Use the static {@link #getDefaultAdapter} method to get the default NFC
- * Adapter for this Android device. Most Android devices will have only one NFC
- * Adapter, and {@link #getDefaultAdapter} returns the singleton object.
+ * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC
+ * adapter for this Android device.
  */
 public final class NfcAdapter {
+    private static final String TAG = "NFC";
+
     /**
      * Intent to start an activity when a tag is discovered.
      */
@@ -161,30 +163,18 @@
      */
     private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
 
-    private static final String TAG = "NFC";
 
-    // Both guarded by NfcAdapter.class:
+    // Guarded by NfcAdapter.class
     private static boolean sIsInitialized = false;
-    private static NfcAdapter sAdapter;
 
-    // Final after construction, except for attemptDeadServiceRecovery()
-    // when NFC crashes.
-    // Not locked - we accept a best effort attempt when NFC crashes.
-    /*package*/ INfcAdapter mService;
+    // Final after first constructor, except for
+    // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
+    // recovery
+    private static INfcAdapter sService;
 
-    private NfcAdapter(INfcAdapter service) {
-        mService = service;
-    }
+    private final Context mContext;
 
     /**
-     * Returns the binder interface to the service.
-     * @hide
-     */
-    public INfcAdapter getService() {
-        return mService;
-    }
-    
-    /**
      * Helper to check if this device has FEATURE_NFC, but without using
      * a context.
      * Equivalent to
@@ -204,8 +194,27 @@
         }
     }
 
+    private static synchronized INfcAdapter setupService() {
+        if (!sIsInitialized) {
+            sIsInitialized = true;
+
+            /* is this device meant to have NFC */
+            if (!hasNfcFeature()) {
+                Log.v(TAG, "this device does not have NFC support");
+                return null;
+            }
+
+            sService = getServiceInterface();
+            if (sService == null) {
+                Log.e(TAG, "could not retrieve NFC service");
+                return null;
+            }
+        }
+        return sService;
+    }
+
     /** get handle to NFC service interface */
-    private static synchronized INfcAdapter getServiceInterface() {
+    private static INfcAdapter getServiceInterface() {
         /* get a handle to NFC service */
         IBinder b = ServiceManager.getService("nfc");
         if (b == null) {
@@ -215,34 +224,54 @@
     }
 
     /**
+     * Helper to get the default NFC Adapter.
+     * <p>
+     * Most Android devices will only have one NFC Adapter (NFC Controller).
+     * <p>
+     * This helper is the equivalent of:
+     * <pre>{@code
+     * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+     * NfcAdapter adapter = manager.getDefaultAdapter();
+     * }</pre>
+     * @param context the calling application's context
+     *
+     * @return the default NFC adapter, or null if no NFC adapter exists
+     */
+    public static NfcAdapter getDefaultAdapter(Context context) {
+        /* use getSystemService() instead of just instantiating to take
+         * advantage of the context's cached NfcManager & NfcAdapter */
+        NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+        return manager.getDefaultAdapter();
+    }
+
+    /**
      * 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
+     * @deprecated use {@link #getDefaultAdapter(Context)}
      */
+    @Deprecated
     public static NfcAdapter getDefaultAdapter() {
-        synchronized (NfcAdapter.class) {
-            if (sIsInitialized) {
-                return sAdapter;
-            }
-            sIsInitialized = true;
+        Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
+                "NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
+        return new NfcAdapter(null);
+    }
 
-            /* is this device meant to have NFC */
-            if (!hasNfcFeature()) {
-                Log.v(TAG, "this device does not have NFC support");
-                return null;
-            }
-
-            INfcAdapter service = getServiceInterface();
-            if (service == null) {
-                Log.e(TAG, "could not retrieve NFC service");
-                return null;
-            }
-
-            sAdapter = new NfcAdapter(service);
-            return sAdapter;
+    /*package*/ NfcAdapter(Context context) {
+        if (setupService() == null) {
+            throw new UnsupportedOperationException();
         }
+        mContext = context;
+    }
+
+    /**
+     * Returns the binder interface to the service.
+     * @hide
+     */
+    public INfcAdapter getService() {
+        return sService;
     }
 
     /**
@@ -256,9 +285,9 @@
             Log.e(TAG, "could not retrieve NFC service during service recovery");
             return;
         }
-        /* assigning to mService is not thread-safe, but this is best-effort code
+        /* assigning to sService is not thread-safe, but this is best-effort code
          * and on a well-behaved system should never happen */
-        mService = service;
+        sService = service;
         return;
     }
 
@@ -275,7 +304,7 @@
      */
     public boolean isEnabled() {
         try {
-            return mService.isEnabled();
+            return sService.isEnabled();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return false;
@@ -292,7 +321,7 @@
      */
     public boolean enable() {
         try {
-            return mService.enable();
+            return sService.enable();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return false;
@@ -311,7 +340,7 @@
      */
     public boolean disable() {
         try {
-            return mService.disable();
+            return sService.disable();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return false;
@@ -338,7 +367,7 @@
      */
     public void setLocalNdefMessage(NdefMessage message) {
         try {
-            mService.localSet(message);
+            sService.localSet(message);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
@@ -353,7 +382,7 @@
      */
     public NdefMessage getLocalNdefMessage() {
         try {
-            return mService.localGet();
+            return sService.localGet();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return null;
@@ -366,7 +395,7 @@
      */
     public NfcSecureElement createNfcSecureElementConnection() {
         try {
-            return new NfcSecureElement(mService.getNfcSecureElementInterface());
+            return new NfcSecureElement(sService.getNfcSecureElementInterface());
         } catch (RemoteException e) {
             Log.e(TAG, "createNfcSecureElementConnection failed", e);
             return null;
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
new file mode 100644
index 0000000..5fa6483
--- /dev/null
+++ b/core/java/android/nfc/NfcManager.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 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.content.Context;
+
+/**
+ * High level manager used to obtain an instance of an {@link NfcAdapter}.
+ * <p>
+ * Use {@link android.content.Context#getSystemService(java.lang.String)}
+ * with {@link Context#NFC_SERVICE} to create an {@link NfcManager},
+ * then call {@link #getDefaultAdapter} to obtain the {@link NfcAdapter}.
+ * <p>
+ * Alternately, you can just call the static helper
+ * {@link NfcAdapter#getDefaultAdapter(android.content.Context)}.
+ *
+ * @see Context#getSystemService
+ * @see NfcAdapter#getDefaultAdapter(android.content.Context)
+ */
+public final class NfcManager {
+    private final NfcAdapter mAdapter;
+
+    /**
+     * @hide
+     */
+    public NfcManager(Context context) {
+        NfcAdapter adapter;
+        try {
+            adapter = new NfcAdapter(context);
+        } catch (UnsupportedOperationException e) {
+            adapter = null;
+        }
+        mAdapter = adapter;
+    }
+
+    /**
+     * Get the default NFC Adapter for this device.
+     *
+     * @return the default NFC Adapter
+     */
+    public NfcAdapter getDefaultAdapter() {
+        return mAdapter;
+    }
+}
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 36de915..d042634 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -127,7 +127,7 @@
     /**
      * Returns the technology, or null if not present
      */
-    public TagTechnology getTechnology(int tech) {
+    public TagTechnology getTechnology(NfcAdapter adapter, int tech) {
         int pos = -1;
         for (int idx = 0; idx < mTechList.length; idx++) {
           if (mTechList[idx] == tech) {
@@ -140,7 +140,6 @@
         }
 
         Bundle extras = mTechExtras[pos];
-        NfcAdapter adapter = NfcAdapter.getDefaultAdapter();
         try {
             switch (tech) {
                 case TagTechnology.NFC_A: {
diff --git a/core/java/android/nfc/package.html b/core/java/android/nfc/package.html
index b054d1c..97bb29d 100644
--- a/core/java/android/nfc/package.html
+++ b/core/java/android/nfc/package.html
@@ -6,9 +6,13 @@
 <p>Here's a summary of the classes:</p>
 
 <dl>
+  <dt>{@link android.nfc.NfcManager}</dt>
+  <dd>This is the high level manager, used to obtain this device's {@link android.nfc.NfcAdapter}. You can
+acquire an instance using {@link android.content.Context#getSystemService}.</dd>
   <dt>{@link android.nfc.NfcAdapter}</dt>
   <dd>This represents the device's NFC adapter, which is your entry-point to performing NFC
-operations. You can acquire an instance with {@link android.nfc.NfcAdapter#getDefaultAdapter}.</dd>
+operations. You can acquire an instance with {@link android.nfc.NfcManager#getDefaultAdapter}, or
+{@link android.nfc.NfcAdapter#getDefaultAdapter(android.content.Context)}.</dd>
   <dt>{@link android.nfc.NdefMessage}</dt>
   <dd>Represents an NDEF data message, which is the standard format in which "records"
 carrying data are transmitted between devices and tags. Your application can receive these
diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java
index ba3a425..bd20808 100644
--- a/core/java/android/nfc/technology/MifareClassic.java
+++ b/core/java/android/nfc/technology/MifareClassic.java
@@ -74,7 +74,7 @@
         super(adapter, tag, TagTechnology.MIFARE_CLASSIC);
 
         // Check if this could actually be a Mifare
-        NfcA a = (NfcA) tag.getTechnology(TagTechnology.NFC_A);
+        NfcA a = (NfcA) tag.getTechnology(adapter, TagTechnology.NFC_A);
         //short[] ATQA = getATQA(tag);
 
         mIsEmulated = false;
diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java
index dd1dae9..6681688 100644
--- a/core/java/android/nfc/technology/MifareUltralight.java
+++ b/core/java/android/nfc/technology/MifareUltralight.java
@@ -16,13 +16,13 @@
 
 package android.nfc.technology;
 
+import java.io.IOException;
+
 import android.nfc.NfcAdapter;
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
 
-import java.io.IOException;
-
 /**
  * Concrete class for TagTechnology.MIFARE_ULTRALIGHT
  *
@@ -39,7 +39,7 @@
     public static final int TYPE_ULTRALIGHT_C = 2;
     public static final int TYPE_UNKNOWN = 10;
 
-		private static final int NXP_MANUFACTURER_ID = 0x04;
+    private static final int NXP_MANUFACTURER_ID = 0x04;
 
     private int mType;
 
@@ -47,13 +47,13 @@
         super(adapter, tag, TagTechnology.MIFARE_ULTRALIGHT);
 
         // Check if this could actually be a Mifare
-        NfcA a = (NfcA) tag.getTechnology(TagTechnology.NFC_A);
+        NfcA a = (NfcA) tag.getTechnology(adapter, TagTechnology.NFC_A);
 
         mType = TYPE_UNKNOWN;
 
         if( a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID ) {
-					// could be UL or UL-C
-					mType = TYPE_ULTRALIGHT;
+            // could be UL or UL-C
+            mType = TYPE_ULTRALIGHT;
         }
     }
 
@@ -73,9 +73,9 @@
     /**
      * @throws IOException
      */
-/*
+    /*
     public byte[] readOTP();
     public void writePage(int block, byte[] data);
     public void writeBlock(int block, byte[] data);
-*/
+     */
 }