| /* |
| * 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.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.nfc.technology.TagTechnology; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.util.Log; |
| |
| /** |
| * Represents the device's local NFC adapter. |
| * <p> |
| * 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 with NDEF payload is discovered. |
| * If the tag has and NDEF payload this intent is started before |
| * {@link #ACTION_TECHNOLOGY_DISCOVERED}. |
| * |
| * If any activities respond to this intent neither |
| * {@link #ACTION_TECHNOLOGY_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; |
| |
| /** |
| * Intent to started when a tag is discovered. The data URI is formated as |
| * {@code vnd.android.nfc://tag/} with the path having a directory entry for each technology |
| * in the {@link Tag#getTechnologyList()} is ascending order. |
| * |
| * This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before |
| * {@link #ACTION_TAG_DISCOVERED} |
| * |
| * If any activities respond to this intent {@link #ACTION_TAG_DISCOVERED} will not be started. |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_TECHNOLOGY_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; |
| |
| /** |
| * Intent to start an activity when a tag is discovered. |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; |
| |
| /** |
| * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED |
| * @hide |
| */ |
| public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST"; |
| |
| /** |
| * Mandatory Tag extra for the ACTION_TAG intents. |
| */ |
| public static final String EXTRA_TAG = "android.nfc.extra.TAG"; |
| |
| /** |
| * Optional NdefMessage[] extra for the ACTION_TAG intents. |
| */ |
| public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; |
| |
| /** |
| * Optional byte[] extra for the tag identifier. |
| */ |
| public static final String EXTRA_ID = "android.nfc.extra.ID"; |
| |
| /** |
| * Broadcast Action: a transaction with a secure element has been detected. |
| * <p> |
| * Always contains the extra field |
| * {@link android.nfc.NfcAdapter#EXTRA_AID} |
| * @hide |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| public static final String ACTION_TRANSACTION_DETECTED = |
| "android.nfc.action.TRANSACTION_DETECTED"; |
| |
| /** |
| * Broadcast Action: an RF field ON has been detected. |
| * @hide |
| */ |
| public static final String ACTION_RF_FIELD_ON_DETECTED = |
| "android.nfc.action.RF_FIELD_ON_DETECTED"; |
| |
| /** |
| * Broadcast Action: an RF Field OFF has been detected. |
| * @hide |
| */ |
| public static final String ACTION_RF_FIELD_OFF_DETECTED = |
| "android.nfc.action.RF_FIELD_OFF_DETECTED"; |
| |
| /** |
| * Broadcast Action: an adapter's state changed between enabled and disabled. |
| * |
| * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains |
| * whether it's enabled or disabled, not including any information about whether it's |
| * actively enabling or disabling. |
| * |
| * @hide |
| */ |
| public static final String ACTION_ADAPTER_STATE_CHANGE = |
| "android.nfc.action.ADAPTER_STATE_CHANGE"; |
| |
| /** |
| * The Intent extra for ACTION_ADAPTER_STATE_CHANGE, saying what the new state is. |
| * |
| * @hide |
| */ |
| public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled"; |
| |
| /** |
| * Mandatory byte array extra field in |
| * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}. |
| * <p> |
| * Contains the AID of the applet involved in the transaction. |
| * @hide |
| */ |
| public static final String EXTRA_AID = "android.nfc.extra.AID"; |
| |
| /** |
| * LLCP link status: The LLCP link is activated. |
| * @hide |
| */ |
| public static final int LLCP_LINK_STATE_ACTIVATED = 0; |
| |
| /** |
| * LLCP link status: The LLCP link is deactivated. |
| * @hide |
| */ |
| public static final int LLCP_LINK_STATE_DEACTIVATED = 1; |
| |
| /** |
| * Broadcast Action: the LLCP link state changed. |
| * <p> |
| * Always contains the extra field |
| * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}. |
| * @hide |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| public static final String ACTION_LLCP_LINK_STATE_CHANGED = |
| "android.nfc.action.LLCP_LINK_STATE_CHANGED"; |
| |
| /** |
| * Used as int extra field in |
| * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}. |
| * <p> |
| * It contains the new state of the LLCP link. |
| * @hide |
| */ |
| public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE"; |
| |
| /** |
| * Tag Reader Discovery mode |
| * @hide |
| */ |
| private static final int DISCOVERY_MODE_TAG_READER = 0; |
| |
| /** |
| * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an |
| * NFC-IP1 communication. Implementations should not assume that the |
| * controller will end up behaving as an NFC-IP1 target or initiator and |
| * should handle both cases, depending on the type of the remote peer type. |
| * @hide |
| */ |
| private static final int DISCOVERY_MODE_NFCIP1 = 1; |
| |
| /** |
| * Card Emulation mode Enables the manager to act as an NFC tag. Provided |
| * that a Secure Element (an UICC for instance) is connected to the NFC |
| * controller through its SWP interface, it can be exposed to the outside |
| * NFC world and be addressed by external readers the same way they would |
| * with a tag. |
| * <p> |
| * Which Secure Element is exposed is implementation-dependent. |
| * |
| * @hide |
| */ |
| private static final int DISCOVERY_MODE_CARD_EMULATION = 2; |
| |
| |
| // Guarded by NfcAdapter.class |
| private static boolean sIsInitialized = false; |
| |
| // Final after first constructor, except for |
| // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort |
| // recovery |
| private static INfcAdapter sService; |
| private static INfcTag sTagService; |
| |
| private final Context mContext; |
| |
| /** |
| * Helper to check if this device has FEATURE_NFC, but without using |
| * a context. |
| * Equivalent to |
| * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) |
| */ |
| private static boolean hasNfcFeature() { |
| IPackageManager pm = ActivityThread.getPackageManager(); |
| if (pm == null) { |
| Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); |
| return false; |
| } |
| try { |
| return pm.hasSystemFeature(PackageManager.FEATURE_NFC); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); |
| return false; |
| } |
| } |
| |
| 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; |
| } |
| try { |
| sTagService = sService.getNfcTagInterface(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "could not retrieve NFC Tag service"); |
| return null; |
| } |
| } |
| return sService; |
| } |
| |
| /** get handle to NFC service interface */ |
| private static INfcAdapter getServiceInterface() { |
| /* get a handle to NFC service */ |
| IBinder b = ServiceManager.getService("nfc"); |
| if (b == null) { |
| return null; |
| } |
| return INfcAdapter.Stub.asInterface(b); |
| } |
| |
| /** |
| * 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() { |
| Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + |
| "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); |
| return new NfcAdapter(null); |
| } |
| |
| /*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; |
| } |
| |
| /** |
| * Returns the binder interface to the tag service. |
| * @hide |
| */ |
| public INfcTag getTagService() { |
| return sTagService; |
| } |
| |
| /** |
| * NFC service dead - attempt best effort recovery |
| * @hide |
| */ |
| public void attemptDeadServiceRecovery(Exception e) { |
| Log.e(TAG, "NFC service dead - attempting to recover", e); |
| INfcAdapter service = getServiceInterface(); |
| if (service == null) { |
| Log.e(TAG, "could not retrieve NFC service during service recovery"); |
| // nothing more can be done now, sService is still stale, we'll hit |
| // this recovery path again later |
| return; |
| } |
| // assigning to sService is not thread-safe, but this is best-effort code |
| // and on a well-behaved system should never happen |
| sService = service; |
| try { |
| sTagService = service.getNfcTagInterface(); |
| } catch (RemoteException ee) { |
| Log.e(TAG, "could not retrieve NFC tag service during service recovery"); |
| // nothing more can be done now, sService is still stale, we'll hit |
| // this recovery path again later |
| } |
| |
| return; |
| } |
| |
| /** |
| * Return true if this NFC Adapter has any features enabled. |
| * <p> |
| * If this method returns false, then applications should request the user |
| * turn on NFC tag discovery in Settings. |
| * <p> |
| * If this method returns false, the NFC hardware is guaranteed not to |
| * perform or respond to any NFC communication. |
| * |
| * @return true if this NFC Adapter is enabled to discover new tags |
| */ |
| public boolean isEnabled() { |
| try { |
| return sService.isEnabled(); |
| } catch (RemoteException e) { |
| attemptDeadServiceRecovery(e); |
| return false; |
| } |
| } |
| |
| /** |
| * Enable NFC hardware. |
| * <p> |
| * NOTE: may block for ~second or more. Poor API. Avoid |
| * calling from the UI thread. |
| * |
| * @hide |
| */ |
| public boolean enable() { |
| try { |
| return sService.enable(); |
| } catch (RemoteException e) { |
| attemptDeadServiceRecovery(e); |
| return false; |
| } |
| } |
| |
| /** |
| * Disable NFC hardware. |
| * No NFC features will work after this call, and the hardware |
| * will not perform or respond to any NFC communication. |
| * <p> |
| * NOTE: may block for ~second or more. Poor API. Avoid |
| * calling from the UI thread. |
| * |
| * @hide |
| */ |
| public boolean disable() { |
| try { |
| return sService.disable(); |
| } catch (RemoteException e) { |
| attemptDeadServiceRecovery(e); |
| return false; |
| } |
| } |
| |
| /** |
| * Retrieve a TagTechnology object used to interact with a Tag that is |
| * in field. |
| * <p> |
| * @return TagTechnology object, or null if not present |
| */ |
| public TagTechnology getTechnology(Tag tag, int tech) { |
| return tag.getTechnology(NfcAdapter.this, tech); |
| } |
| |
| /** |
| * Set the NDEF Message that this NFC adapter should appear as to Tag |
| * readers. |
| * <p> |
| * Any Tag reader can read the contents of the local tag when it is in |
| * proximity, without any further user confirmation. |
| * <p> |
| * The implementation of this method must either |
| * <ul> |
| * <li>act as a passive tag containing this NDEF message |
| * <li>provide the NDEF message on over LLCP to peer NFC adapters |
| * </ul> |
| * The NDEF message is preserved across reboot. |
| * <p>Requires {@link android.Manifest.permission#NFC} permission. |
| * |
| * @param message NDEF message to make public |
| * @hide |
| */ |
| public void setLocalNdefMessage(NdefMessage message) { |
| try { |
| sService.localSet(message); |
| } catch (RemoteException e) { |
| attemptDeadServiceRecovery(e); |
| } |
| } |
| |
| /** |
| * Get the NDEF Message that this adapter appears as to Tag readers. |
| * <p>Requires {@link android.Manifest.permission#NFC} permission. |
| * |
| * @return NDEF Message that is publicly readable |
| * @hide |
| */ |
| public NdefMessage getLocalNdefMessage() { |
| try { |
| return sService.localGet(); |
| } catch (RemoteException e) { |
| attemptDeadServiceRecovery(e); |
| return null; |
| } |
| } |
| |
| /** |
| * Create an Nfc Secure Element Connection |
| * @hide |
| */ |
| public NfcSecureElement createNfcSecureElementConnection() { |
| try { |
| return new NfcSecureElement(sService.getNfcSecureElementInterface()); |
| } catch (RemoteException e) { |
| Log.e(TAG, "createNfcSecureElementConnection failed", e); |
| return null; |
| } |
| } |
| } |