NfcAdapter.java revision 7ea5c45e8d89f59065f088d4e11cceeeed9d64d1
1/*
2 * Copyright (C) 2010 The Android Open Source Project Licensed under the Apache
3 * License, Version 2.0 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
6 * or agreed to in writing, software distributed under the License is
7 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8 * KIND, either express or implied. See the License for the specific language
9 * governing permissions and limitations under the License.
10 */
11
12package android.nfc;
13
14import java.lang.UnsupportedOperationException;
15
16import android.annotation.SdkConstant;
17import android.annotation.SdkConstant.SdkConstantType;
18import android.app.ActivityThread;
19import android.content.Context;
20import android.content.pm.IPackageManager;
21import android.content.pm.PackageManager;
22import android.nfc.INfcAdapter;
23import android.os.IBinder;
24import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.util.Log;
27
28/**
29 * Represents the device's local NFC adapter.
30 * <p>
31 * Use the static {@link #getDefaultAdapter} method to get the default NFC
32 * Adapter for this Android device. Most Android devices will have only one NFC
33 * Adapter, and {@link #getDefaultAdapter} returns the singleton object.
34 * <p>
35 * {@link NfcAdapter} can be used to create {@link RawTagConnection} or
36 * {@link NdefTagConnection} connections to modify or perform low level access
37 * to NFC Tags.
38 * <p class="note">
39 * <strong>Note:</strong> Some methods require the
40 * {@link android.Manifest.permission#NFC} permission.
41 */
42public final class NfcAdapter {
43    /**
44     * Intent to start an activity when a non-NDEF tag is discovered.
45     * TODO(npelly) finalize decision on using CATEGORY or DATA URI to provide a
46     * hint for applications to filter the tag type.
47     * TODO(npelly) probably combine these two intents since tags aren't that simple
48     */
49    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
50    public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
51
52    /**
53     * Intent to start an activity when a NDEF tag is discovered. TODO(npelly)
54     * finalize decision on using CATEGORY or DATA URI to provide a hint for
55     * applications to filter the tag type.
56     */
57    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
58    public static final String ACTION_NDEF_TAG_DISCOVERED =
59            "android.nfc.action.NDEF_TAG_DISCOVERED";
60
61    /**
62     * Mandatory Tag extra for the ACTION_TAG and ACTION_NDEF_TAG intents.
63     */
64    public static final String EXTRA_TAG = "android.nfc.extra.TAG";
65
66    /**
67     * Broadcast Action: a transaction with a secure element has been detected.
68     * <p>
69     * Always contains the extra field
70     * {@link android.nfc.NfcAdapter#EXTRA_AID}
71     * @hide
72     */
73    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
74    public static final String ACTION_TRANSACTION_DETECTED =
75            "android.nfc.action.TRANSACTION_DETECTED";
76
77    /**
78     * Broadcast Action: an adapter's state changed between enabled and disabled.
79     *
80     * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains
81     * whether it's enabled or disabled, not including any information about whether it's
82     * actively enabling or disabling.
83     *
84     * @hide
85     */
86    public static final String ACTION_ADAPTER_STATE_CHANGE =
87            "android.nfc.action.ADAPTER_STATE_CHANGE";
88
89    /**
90     * The Intent extra for ACTION_ADAPTER_STATE_CHANGE, saying what the new state is.
91     *
92     * @hide
93     */
94    public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled";
95
96    /**
97     * Mandatory byte array extra field in
98     * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}.
99     * <p>
100     * Contains the AID of the applet involved in the transaction.
101     * @hide
102     */
103    public static final String EXTRA_AID = "android.nfc.extra.AID";
104
105    /**
106     * LLCP link status: The LLCP link is activated.
107     * @hide
108     */
109    public static final int LLCP_LINK_STATE_ACTIVATED = 0;
110
111    /**
112     * LLCP link status: The LLCP link is deactivated.
113     * @hide
114     */
115    public static final int LLCP_LINK_STATE_DEACTIVATED = 1;
116
117    /**
118     * Broadcast Action: the LLCP link state changed.
119     * <p>
120     * Always contains the extra field
121     * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}.
122     * @hide
123     */
124    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
125    public static final String ACTION_LLCP_LINK_STATE_CHANGED =
126            "android.nfc.action.LLCP_LINK_STATE_CHANGED";
127
128    /**
129     * Used as int extra field in
130     * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}.
131     * <p>
132     * It contains the new state of the LLCP link.
133     * @hide
134     */
135    public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE";
136
137    /**
138     * Tag Reader Discovery mode
139     * @hide
140     */
141    private static final int DISCOVERY_MODE_TAG_READER = 0;
142
143    /**
144     * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an
145     * NFC-IP1 communication. Implementations should not assume that the
146     * controller will end up behaving as an NFC-IP1 target or initiator and
147     * should handle both cases, depending on the type of the remote peer type.
148     * @hide
149     */
150    private static final int DISCOVERY_MODE_NFCIP1 = 1;
151
152    /**
153     * Card Emulation mode Enables the manager to act as an NFC tag. Provided
154     * that a Secure Element (an UICC for instance) is connected to the NFC
155     * controller through its SWP interface, it can be exposed to the outside
156     * NFC world and be addressed by external readers the same way they would
157     * with a tag.
158     * <p>
159     * Which Secure Element is exposed is implementation-dependent.
160     *
161     * @hide
162     */
163    private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
164
165    private static final String TAG = "NFC";
166
167    // Both guarded by NfcAdapter.class:
168    private static boolean sIsInitialized = false;
169    private static NfcAdapter sAdapter;
170
171    private final INfcAdapter mService;
172
173    private NfcAdapter(INfcAdapter service) {
174        mService = service;
175    }
176
177    /**
178     * Helper to check if this device has FEATURE_NFC, but without using
179     * a context.
180     * Equivalent to
181     * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
182     */
183    private static boolean hasNfcFeature() {
184        IPackageManager pm = ActivityThread.getPackageManager();
185        if (pm == null) {
186            Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
187            return false;
188        }
189        try {
190            return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
191        } catch (RemoteException e) {
192            Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
193            return false;
194        }
195    }
196
197    /**
198     * Get a handle to the default NFC Adapter on this Android device.
199     * <p>
200     * Most Android devices will only have one NFC Adapter (NFC Controller).
201     *
202     * @return the default NFC adapter, or null if no NFC adapter exists
203     */
204    public static NfcAdapter getDefaultAdapter() {
205        synchronized (NfcAdapter.class) {
206            if (sIsInitialized) {
207                return sAdapter;
208            }
209            sIsInitialized = true;
210
211            /* is this device meant to have NFC */
212            if (!hasNfcFeature()) {
213                Log.v(TAG, "this device does not have NFC support");
214                return null;
215            }
216
217            /* get a handle to NFC service */
218            IBinder b = ServiceManager.getService("nfc");
219            if (b == null) {
220                Log.e(TAG, "could not retrieve NFC service");
221                return null;
222            }
223
224            sAdapter = new NfcAdapter(INfcAdapter.Stub.asInterface(b));
225            return sAdapter;
226        }
227    }
228
229    /**
230     * Return true if this NFC Adapter has any features enabled.
231     * <p>
232     * If this method returns false, then applications should request the user
233     * turn on NFC tag discovery in Settings.
234     * <p>
235     * If this method returns false, the NFC hardware is guaranteed not to
236     * perform or respond to any NFC communication.
237     *
238     * @return true if this NFC Adapter is enabled to discover new tags
239     */
240    public boolean isEnabled() {
241        try {
242            return mService.isEnabled();
243        } catch (RemoteException e) {
244            Log.e(TAG, "RemoteException in isEnabled()", e);
245            return false;
246        }
247    }
248
249    /**
250     * Enable NFC hardware.
251     * <p>
252     * NOTE: may block for ~second or more.  Poor API.  Avoid
253     * calling from the UI thread.
254     *
255     * @hide
256     */
257    public boolean enable() {
258        try {
259            return mService.enable();
260        } catch (RemoteException e) {
261            Log.e(TAG, "RemoteException in enable()", e);
262            return false;
263        }
264    }
265
266    /**
267     * Disable NFC hardware.
268     * No NFC features will work after this call, and the hardware
269     * will not perform or respond to any NFC communication.
270     * <p>
271     * NOTE: may block for ~second or more.  Poor API.  Avoid
272     * calling from the UI thread.
273     *
274     * @hide
275     */
276    public boolean disable() {
277        try {
278            return mService.disable();
279        } catch (RemoteException e) {
280            Log.e(TAG, "RemoteException in disable()", e);
281            return false;
282        }
283    }
284
285    /**
286     * Set the NDEF Message that this NFC adapter should appear as to Tag
287     * readers.
288     * <p>
289     * Any Tag reader can read the contents of the local tag when it is in
290     * proximity, without any further user confirmation.
291     * <p>
292     * The implementation of this method must either
293     * <ul>
294     * <li>act as a passive tag containing this NDEF message
295     * <li>provide the NDEF message on over LLCP to peer NFC adapters
296     * </ul>
297     * The NDEF message is preserved across reboot.
298     * <p>Requires {@link android.Manifest.permission#NFC} permission.
299     *
300     * @param message NDEF message to make public
301     */
302    public void setLocalNdefMessage(NdefMessage message) {
303        try {
304            mService.localSet(message);
305        } catch (RemoteException e) {
306            Log.e(TAG, "NFC service died", e);
307        }
308    }
309
310    /**
311     * Get the NDEF Message that this adapter appears as to Tag readers.
312     * <p>Requires {@link android.Manifest.permission#NFC} permission.
313     *
314     * @return NDEF Message that is publicly readable
315     */
316    public NdefMessage getLocalNdefMessage() {
317        try {
318            return mService.localGet();
319        } catch (RemoteException e) {
320            Log.e(TAG, "NFC service died", e);
321            return null;
322        }
323    }
324
325    /**
326     * Create a raw tag connection to the default Target
327     * <p>Requires {@link android.Manifest.permission#NFC} permission.
328     */
329    public RawTagConnection createRawTagConnection(Tag tag) {
330        try {
331            return new RawTagConnection(mService, tag);
332        } catch (RemoteException e) {
333            Log.e(TAG, "NFC service died", e);
334            return null;
335        }
336    }
337
338    /**
339     * Create a raw tag connection to the specified Target
340     * <p>Requires {@link android.Manifest.permission#NFC} permission.
341     */
342    public RawTagConnection createRawTagConnection(Tag tag, String target) {
343        try {
344            return new RawTagConnection(mService, tag, target);
345        } catch (RemoteException e) {
346            Log.e(TAG, "NFC service died", e);
347            return null;
348        }
349    }
350
351    /**
352     * Create an NDEF tag connection to the default Target
353     * <p>Requires {@link android.Manifest.permission#NFC} permission.
354     */
355    public NdefTagConnection createNdefTagConnection(NdefTag tag) {
356        try {
357            return new NdefTagConnection(mService, tag);
358        } catch (RemoteException e) {
359            Log.e(TAG, "NFC service died", e);
360            return null;
361        }
362    }
363
364    /**
365     * Create an NDEF tag connection to the specified Target
366     * <p>Requires {@link android.Manifest.permission#NFC} permission.
367     */
368    public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) {
369        try {
370            return new NdefTagConnection(mService, tag, target);
371        } catch (RemoteException e) {
372            Log.e(TAG, "NFC service died", e);
373            return null;
374        }
375    }
376}
377