diff --git a/Android.mk b/Android.mk
index 5e405d365f84c..c34d38e6cadc9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -202,6 +202,7 @@ LOCAL_SRC_FILES += \
core/java/android/nfc/INfcAdapterExtras.aidl \
core/java/android/nfc/INfcTag.aidl \
core/java/android/nfc/INfcCardEmulation.aidl \
+ core/java/android/nfc/INfcFCardEmulation.aidl \
core/java/android/nfc/INfcUnlockHandler.aidl \
core/java/android/os/IBatteryPropertiesListener.aidl \
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
diff --git a/api/current.txt b/api/current.txt
index 41a7d5b54982d..fa7f49f443213 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9336,6 +9336,7 @@ package android.content.pm {
field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
+ field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
field public static final java.lang.String FEATURE_PRINTING = "android.software.print";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
@@ -23873,6 +23874,28 @@ package android.nfc.cardemulation {
field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
+ public abstract class HostNfcFService extends android.app.Service {
+ ctor public HostNfcFService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onDeactivated(int);
+ method public abstract byte[] processNfcFPacket(byte[], android.os.Bundle);
+ method public final void sendResponsePacket(byte[]);
+ field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+ field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_NFCF_SERVICE";
+ field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.host_nfcf_service";
+ }
+
+ public final class NfcFCardEmulation {
+ method public boolean disableNfcFForegroundService(android.app.Activity);
+ method public boolean enableNfcFForegroundService(android.app.Activity, android.content.ComponentName);
+ method public static synchronized android.nfc.cardemulation.NfcFCardEmulation getInstance(android.nfc.NfcAdapter);
+ method public java.lang.String getNfcid2ForService(android.content.ComponentName);
+ method public java.lang.String getSystemCodeForService(android.content.ComponentName);
+ method public boolean registerSystemCodeForService(android.content.ComponentName, java.lang.String);
+ method public boolean removeSystemCodeForService(android.content.ComponentName);
+ method public boolean setNfcid2ForService(android.content.ComponentName, java.lang.String);
+ }
+
public abstract class OffHostApduService extends android.app.Service {
ctor public OffHostApduService();
method public abstract android.os.IBinder onBind(android.content.Intent);
diff --git a/api/system-current.txt b/api/system-current.txt
index 7db60749a4a92..a2c904044e682 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9630,6 +9630,7 @@ package android.content.pm {
field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
+ field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
field public static final java.lang.String FEATURE_PRINTING = "android.software.print";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
@@ -25818,6 +25819,28 @@ package android.nfc.cardemulation {
field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
+ public abstract class HostNfcFService extends android.app.Service {
+ ctor public HostNfcFService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onDeactivated(int);
+ method public abstract byte[] processNfcFPacket(byte[], android.os.Bundle);
+ method public final void sendResponsePacket(byte[]);
+ field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+ field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_NFCF_SERVICE";
+ field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.host_nfcf_service";
+ }
+
+ public final class NfcFCardEmulation {
+ method public boolean disableNfcFForegroundService(android.app.Activity);
+ method public boolean enableNfcFForegroundService(android.app.Activity, android.content.ComponentName);
+ method public static synchronized android.nfc.cardemulation.NfcFCardEmulation getInstance(android.nfc.NfcAdapter);
+ method public java.lang.String getNfcid2ForService(android.content.ComponentName);
+ method public java.lang.String getSystemCodeForService(android.content.ComponentName);
+ method public boolean registerSystemCodeForService(android.content.ComponentName, java.lang.String);
+ method public boolean removeSystemCodeForService(android.content.ComponentName);
+ method public boolean setNfcid2ForService(android.content.ComponentName, java.lang.String);
+ }
+
public abstract class OffHostApduService extends android.app.Service {
ctor public OffHostApduService();
method public abstract android.os.IBinder onBind(android.content.Intent);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 054dafe727506..3029b119f27bd 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1293,6 +1293,14 @@ public abstract class PackageManager {
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports host-
+ * based NFC-F card emulation.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
+
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports the OpenGL ES
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 961a3f41cd5eb..940565f09c03c 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -26,6 +26,7 @@ import android.nfc.IAppCallback;
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
+import android.nfc.INfcFCardEmulation;
import android.nfc.INfcUnlockHandler;
import android.os.Bundle;
@@ -36,6 +37,7 @@ interface INfcAdapter
{
INfcTag getNfcTagInterface();
INfcCardEmulation getNfcCardEmulationInterface();
+ INfcFCardEmulation getNfcFCardEmulationInterface();
INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
int getState();
diff --git a/core/java/android/nfc/INfcFCardEmulation.aidl b/core/java/android/nfc/INfcFCardEmulation.aidl
new file mode 100644
index 0000000000000..124bfac4f0d09
--- /dev/null
+++ b/core/java/android/nfc/INfcFCardEmulation.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.ComponentName;
+import android.nfc.cardemulation.NfcFServiceInfo;
+
+/**
+ * @hide
+ */
+interface INfcFCardEmulation
+{
+ String getSystemCodeForService(int userHandle, in ComponentName service);
+ boolean registerSystemCodeForService(int userHandle, in ComponentName service, String systemCode);
+ boolean removeSystemCodeForService(int userHandle, in ComponentName service);
+ String getNfcid2ForService(int userHandle, in ComponentName service);
+ boolean setNfcid2ForService(int userHandle, in ComponentName service, String nfcid2);
+ boolean enableNfcFForegroundService(in ComponentName service);
+ boolean disableNfcFForegroundService();
+ List HostNfcFService is a convenience {@link Service} class that can be
+ * extended to emulate an NFC-F card inside an Android service component.
+ */
+public abstract class HostNfcFService extends Service {
+ /**
+ * The {@link Intent} action that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.nfc.cardemulation.action.HOST_NFCF_SERVICE";
+
+ /**
+ * The name of the meta-data element that contains
+ * more information about this service.
+ */
+ public static final String SERVICE_META_DATA =
+ "android.nfc.cardemulation.host_nfcf_service";
+
+ /**
+ * Reason for {@link #onDeactivated(int)}.
+ * Indicates deactivation was due to the NFC link
+ * being lost.
+ */
+ public static final int DEACTIVATION_LINK_LOSS = 0;
+
+ static final String TAG = "NfcFService";
+
+ /**
+ * MSG_COMMAND_PACKET is sent by NfcService when
+ * a NFC-F command packet has been received.
+ *
+ * @hide
+ */
+ public static final int MSG_COMMAND_PACKET = 0;
+
+ /**
+ * MSG_RESPONSE_PACKET is sent to NfcService to send
+ * a response packet back to the remote device.
+ *
+ * @hide
+ */
+ public static final int MSG_RESPONSE_PACKET = 1;
+
+ /**
+ * MSG_DEACTIVATED is sent by NfcService when
+ * the current session is finished; because
+ * the NFC link was deactivated.
+ *
+ * @hide
+ */
+ public static final int MSG_DEACTIVATED = 2;
+
+ /**
+ * @hide
+ */
+ public static final String KEY_DATA = "data";
+
+ /**
+ * @hide
+ */
+ public static final String KEY_MESSENGER = "messenger";
+
+ /**
+ * Messenger interface to NfcService for sending responses.
+ * Only accessed on main thread by the message handler.
+ *
+ * @hide
+ */
+ Messenger mNfcService = null;
+
+ final Messenger mMessenger = new Messenger(new MsgHandler());
+
+ final class MsgHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_COMMAND_PACKET:
+ Bundle dataBundle = msg.getData();
+ if (dataBundle == null) {
+ return;
+ }
+ if (mNfcService == null) mNfcService = msg.replyTo;
+
+ byte[] packet = dataBundle.getByteArray(KEY_DATA);
+ if (packet != null) {
+ byte[] responsePacket = processNfcFPacket(packet, null);
+ if (responsePacket != null) {
+ if (mNfcService == null) {
+ Log.e(TAG, "Response not sent; service was deactivated.");
+ return;
+ }
+ Message responseMsg = Message.obtain(null, MSG_RESPONSE_PACKET);
+ Bundle responseBundle = new Bundle();
+ responseBundle.putByteArray(KEY_DATA, responsePacket);
+ responseMsg.setData(responseBundle);
+ responseMsg.replyTo = mMessenger;
+ try {
+ mNfcService.send(responseMsg);
+ } catch (RemoteException e) {
+ Log.e("TAG", "Response not sent; RemoteException calling into " +
+ "NfcService.");
+ }
+ }
+ } else {
+ Log.e(TAG, "Received MSG_COMMAND_PACKET without data.");
+ }
+ break;
+ case MSG_RESPONSE_PACKET:
+ if (mNfcService == null) {
+ Log.e(TAG, "Response not sent; service was deactivated.");
+ return;
+ }
+ try {
+ msg.replyTo = mMessenger;
+ mNfcService.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling into NfcService.");
+ }
+ break;
+ case MSG_DEACTIVATED:
+ // Make sure we won't call into NfcService again
+ mNfcService = null;
+ onDeactivated(msg.arg1);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ /**
+ * Sends a response packet back to the remote device.
+ *
+ * Note: this method may be called from any thread and will not block.
+ * @param responsePacket A byte-array containing the response packet.
+ */
+ public final void sendResponsePacket(byte[] responsePacket) {
+ Message responseMsg = Message.obtain(null, MSG_RESPONSE_PACKET);
+ Bundle dataBundle = new Bundle();
+ dataBundle.putByteArray(KEY_DATA, responsePacket);
+ responseMsg.setData(dataBundle);
+ try {
+ mMessenger.send(responseMsg);
+ } catch (RemoteException e) {
+ Log.e("TAG", "Local messenger has died.");
+ }
+ }
+
+ /**
+ * This method will be called when a NFC-F packet has been received
+ * from a remote device. A response packet can be provided directly
+ * by returning a byte-array in this method. Note that in general
+ * response packets must be sent as quickly as possible, given the fact
+ * that the user is likely holding his device over an NFC reader
+ * when this method is called.
+ *
+ * This method is running on the main thread of your application.
+ * If you cannot return a response packet immediately, return null
+ * and use the {@link #sendResponsePacket(byte[])} method later.
+ *
+ * @param commandPacket The NFC-F packet that was received from the remote device
+ * @param extras A bundle containing extra data. May be null.
+ * @return a byte-array containing the response packet, or null if no
+ * response packet can be sent at this point.
+ */
+ public abstract byte[] processNfcFPacket(byte[] commandPacket, Bundle extras);
+
+ /**
+ * This method will be called in following possible scenarios:
+ *
Use of this class requires the
+ * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION_NFCF}
+ * to be present on the device.
+ */
+public final class NfcFCardEmulation {
+ static final String TAG = "NfcFCardEmulation";
+
+ static boolean sIsInitialized = false;
+ static HashMap Before calling {@link #registerSystemCodeForService(ComponentName, String)},
+ * the System Code contained in the Manifest file is returned. After calling
+ * {@link #registerSystemCodeForService(ComponentName, String)}, the System Code
+ * registered there is returned. After calling
+ * {@link #removeSystemCodeForService(ComponentName)}, "null" is returned.
+ *
+ * @param service The component name of the service
+ * @return the current System Code
+ */
+ public String getSystemCodeForService(ComponentName service) {
+ if (service == null) {
+ throw new NullPointerException("service is null");
+ }
+ try {
+ return sService.getSystemCodeForService(UserHandle.myUserId(), service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return null;
+ }
+ try {
+ return sService.getSystemCodeForService(UserHandle.myUserId(), service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Registers a System Code for the specified service.
+ *
+ * The System Code must be in range from "4000" to "4FFF" (excluding "4*FF").
+ *
+ * If a System Code was previously registered for this service
+ * (either statically through the manifest, or dynamically by using this API),
+ * it will be replaced with this one.
+ *
+ * Even if the same System Code is already registered for another service,
+ * this method succeeds in registering the System Code.
+ *
+ * Note that you can only register a System Code for a service that
+ * is running under the same UID as the caller of this API. Typically
+ * this means you need to call this from the same
+ * package as the service itself, though UIDs can also
+ * be shared between packages using shared UIDs.
+ *
+ * @param service The component name of the service
+ * @param systemCode The System Code to be registered
+ * @return whether the registration was successful.
+ */
+ public boolean registerSystemCodeForService(ComponentName service, String systemCode) {
+ if (service == null || systemCode == null) {
+ throw new NullPointerException("service or systemCode is null");
+ }
+ try {
+ return sService.registerSystemCodeForService(UserHandle.myUserId(),
+ service, systemCode);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.registerSystemCodeForService(UserHandle.myUserId(),
+ service, systemCode);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Removes a registered System Code for the specified service.
+ *
+ * @param service The component name of the service
+ * @return whether the System Code was successfully removed.
+ */
+ public boolean removeSystemCodeForService(ComponentName service) {
+ if (service == null) {
+ throw new NullPointerException("service is null");
+ }
+ try {
+ return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current NFCID2 for the specified service.
+ *
+ * Before calling {@link #setNfcid2ForService(ComponentName, String)},
+ * the NFCID2 contained in the Manifest file is returned. If "random" is specified
+ * in the Manifest file, a random number assigned by the system at installation time
+ * is returned. After setting an NFCID2
+ * with {@link #setNfcid2ForService(ComponentName, String)}, this NFCID2 is returned.
+ *
+ * @param service The component name of the service
+ * @return the current NFCID2
+ */
+ public String getNfcid2ForService(ComponentName service) {
+ if (service == null) {
+ throw new NullPointerException("service is null");
+ }
+ try {
+ return sService.getNfcid2ForService(UserHandle.myUserId(), service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return null;
+ }
+ try {
+ return sService.getNfcid2ForService(UserHandle.myUserId(), service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Set a NFCID2 for the specified service.
+ *
+ * The NFCID2 must be in range from "02FE000000000000" to "02FEFFFFFFFFFFFF".
+ *
+ * If a NFCID2 was previously set for this service
+ * (either statically through the manifest, or dynamically by using this API),
+ * it will be replaced.
+ *
+ * Note that you can only set the NFCID2 for a service that
+ * is running under the same UID as the caller of this API. Typically
+ * this means you need to call this from the same
+ * package as the service itself, though UIDs can also
+ * be shared between packages using shared UIDs.
+ *
+ * @param service The component name of the service
+ * @param nfcid2 The NFCID2 to be registered
+ * @return whether the setting was successful.
+ */
+ public boolean setNfcid2ForService(ComponentName service, String nfcid2) {
+ if (service == null || nfcid2 == null) {
+ throw new NullPointerException("service or nfcid2 is null");
+ }
+ try {
+ return sService.setNfcid2ForService(UserHandle.myUserId(),
+ service, nfcid2);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setNfcid2ForService(UserHandle.myUserId(),
+ service, nfcid2);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Allows a foreground application to specify which card emulation service
+ * should be enabled while a specific Activity is in the foreground.
+ *
+ * The specified HCE-F service is only enabled when the corresponding application is
+ * in the foreground and this method has been called. When the application is moved to
+ * the background, {@link #disableNfcFForegroundService(Activity)} is called, or
+ * NFCID2 or System Code is replaced, the HCE-F service is disabled.
+ *
+ * The specified Activity must currently be in resumed state. A good
+ * paradigm is to call this method in your {@link Activity#onResume}, and to call
+ * {@link #disableNfcFForegroundService(Activity)} in your {@link Activity#onPause}.
+ *
+ * Note that this preference is not persisted by the OS, and hence must be
+ * called every time the Activity is resumed.
+ *
+ * @param activity The activity which prefers this service to be invoked
+ * @param service The service to be preferred while this activity is in the foreground
+ * @return whether the registration was successful
+ */
+ public boolean enableNfcFForegroundService(Activity activity, ComponentName service) {
+ if (activity == null || service == null) {
+ throw new NullPointerException("activity or service is null");
+ }
+ // Verify the activity is in the foreground before calling into NfcService
+ if (!activity.isResumed()) {
+ throw new IllegalArgumentException("Activity must be resumed.");
+ }
+ try {
+ return sService.enableNfcFForegroundService(service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.enableNfcFForegroundService(service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Disables the service for the specified Activity.
+ *
+ * Note that the specified Activity must still be in resumed
+ * state at the time of this call. A good place to call this method
+ * is in your {@link Activity#onPause} implementation.
+ *
+ * @param activity The activity which the service was registered for
+ * @return true when successful
+ */
+ public boolean disableNfcFForegroundService(Activity activity) {
+ if (activity == null) {
+ throw new NullPointerException("activity is null");
+ }
+ if (!activity.isResumed()) {
+ throw new IllegalArgumentException("Activity must be resumed.");
+ }
+ try {
+ return sService.disableNfcFForegroundService();
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.disableNfcFForegroundService();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public List