Merge "Implementation of HCE for NFC-F."

am: e168012ff2

* commit 'e168012ff21408f8be85040bb4ac977061519f62':
  Implementation of HCE for NFC-F.
This commit is contained in:
Martijn Coenen
2016-01-25 09:56:20 +00:00
committed by android-build-merger
12 changed files with 1145 additions and 0 deletions

View File

@ -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 \

View File

@ -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";
@ -23866,6 +23867,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);

View File

@ -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";
@ -25811,6 +25812,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);

View File

@ -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

View File

@ -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();

View File

@ -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<NfcFServiceInfo> getNfcFServices(int userHandle);
int getMaxNumOfRegisterableSystemCodes();
}

View File

@ -294,6 +294,7 @@ public final class NfcAdapter {
static INfcAdapter sService;
static INfcTag sTagService;
static INfcCardEmulation sCardEmulationService;
static INfcFCardEmulation sNfcFCardEmulationService;
/**
* The NfcAdapter object for each application context.
@ -452,6 +453,13 @@ public final class NfcAdapter {
throw new UnsupportedOperationException();
}
try {
sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
} catch (RemoteException e) {
Log.e(TAG, "could not retrieve NFC-F card emulation service");
throw new UnsupportedOperationException();
}
sIsInitialized = true;
}
if (context == null) {
@ -570,6 +578,15 @@ public final class NfcAdapter {
return sCardEmulationService;
}
/**
* Returns the binder interface to the NFC-F card emulation service.
* @hide
*/
public INfcFCardEmulation getNfcFCardEmulationService() {
isEnabled();
return sNfcFCardEmulationService;
}
/**
* NFC service dead - attempt best effort recovery
* @hide
@ -601,6 +618,12 @@ public final class NfcAdapter {
Log.e(TAG, "could not retrieve NFC card emulation service during service recovery");
}
try {
sNfcFCardEmulationService = service.getNfcFCardEmulationInterface();
} catch (RemoteException ee) {
Log.e(TAG, "could not retrieve NFC-F card emulation service during service recovery");
}
return;
}

View File

@ -0,0 +1,210 @@
/*
* 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.cardemulation;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/**
* <p>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.
*
* <p>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.");
}
}
/**
* <p>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.
*
* <p class="note">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:
* <li>The NFC link has been lost
* @param reason {@link #DEACTIVATION_LINK_LOSS}
*/
public abstract void onDeactivated(int reason);
}

View File

@ -0,0 +1,470 @@
/*
* 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.cardemulation;
import android.app.Activity;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.nfc.INfcFCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import java.util.HashMap;
import java.util.List;
/**
* This class can be used to query the state of
* NFC-F card emulation services.
*
* For a general introduction into NFC card emulation,
* please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
* NFC card emulation developer guide</a>.</p>
*
* <p class="note">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<Context, NfcFCardEmulation> sCardEmus = new HashMap<Context, NfcFCardEmulation>();
static INfcFCardEmulation sService;
final Context mContext;
private NfcFCardEmulation(Context context, INfcFCardEmulation service) {
mContext = context.getApplicationContext();
sService = service;
}
/**
* Helper to get an instance of this class.
*
* @param adapter A reference to an NfcAdapter object.
* @return
*/
public static synchronized NfcFCardEmulation getInstance(NfcAdapter adapter) {
if (adapter == null) throw new NullPointerException("NfcAdapter is null");
Context context = adapter.getContext();
if (context == null) {
Log.e(TAG, "NfcAdapter context is null.");
throw new UnsupportedOperationException();
}
if (!sIsInitialized) {
IPackageManager pm = ActivityThread.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get PackageManager");
throw new UnsupportedOperationException();
}
try {
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
Log.e(TAG, "This device does not support NFC-F card emulation");
throw new UnsupportedOperationException();
}
} catch (RemoteException e) {
Log.e(TAG, "PackageManager query failed.");
throw new UnsupportedOperationException();
}
sIsInitialized = true;
}
NfcFCardEmulation manager = sCardEmus.get(context);
if (manager == null) {
// Get card emu service
INfcFCardEmulation service = adapter.getNfcFCardEmulationService();
if (service == null) {
Log.e(TAG, "This device does not implement the INfcFCardEmulation interface.");
throw new UnsupportedOperationException();
}
manager = new NfcFCardEmulation(context, service);
sCardEmus.put(context, manager);
}
return manager;
}
/**
* Retrieves the current System Code for the specified service.
*
* <p>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.
*
* <p>The System Code must be in range from "4000" to "4FFF" (excluding "4*FF").
*
* <p>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.
*
* <p>Even if the same System Code is already registered for another service,
* this method succeeds in registering the System Code.
*
* <p>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.
*
* <p>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.
*
* <p>The NFCID2 must be in range from "02FE000000000000" to "02FEFFFFFFFFFFFF".
*
* <p>If a NFCID2 was previously set for this service
* (either statically through the manifest, or dynamically by using this API),
* it will be replaced.
*
* <p>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.
*
* <p>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.
*
* <p>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}.
*
* <p>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.
*
* <p>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<NfcFServiceInfo> getNfcFServices() {
try {
return sService.getNfcFServices(UserHandle.myUserId());
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return null;
}
try {
return sService.getNfcFServices(UserHandle.myUserId());
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return null;
}
}
}
/**
* @hide
*/
public int getMaxNumOfRegisterableSystemCodes() {
try {
return sService.getMaxNumOfRegisterableSystemCodes();
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return -1;
}
try {
return sService.getMaxNumOfRegisterableSystemCodes();
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return -1;
}
}
}
/**
* @hide
*/
public static boolean isValidSystemCode(String systemCode) {
if (systemCode == null) {
return false;
}
if (systemCode.length() != 4) {
Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
return false;
}
// check if the value is between "4000" and "4FFF" (excluding "4*FF")
if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) {
Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
return false;
}
try {
Integer.valueOf(systemCode, 16);
} catch (NumberFormatException e) {
Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
return false;
}
return true;
}
/**
* @hide
*/
public static boolean isValidNfcid2(String nfcid2) {
if (nfcid2 == null) {
return false;
}
if (nfcid2.length() != 16) {
Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
return false;
}
// check if the the value starts with "02FE"
if (!nfcid2.toUpperCase().startsWith("02FE")) {
Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
return false;
}
try {
Long.valueOf(nfcid2, 16);
} catch (NumberFormatException e) {
Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
return false;
}
return true;
}
void recoverService() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
sService = adapter.getNfcFCardEmulationService();
}
}

View File

@ -0,0 +1,19 @@
/*
* 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.cardemulation;
parcelable NfcFServiceInfo;

View File

@ -0,0 +1,304 @@
/*
* 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.cardemulation;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @hide
*/
public final class NfcFServiceInfo implements Parcelable {
static final String TAG = "NfcFServiceInfo";
/**
* The service that implements this
*/
final ResolveInfo mService;
/**
* Description of the service
*/
final String mDescription;
/**
* System Code of the service
*/
final String mSystemCode;
/**
* System Code of the service registered by API
*/
String mDynamicSystemCode;
/**
* NFCID2 of the service
*/
final String mNfcid2;
/**
* NFCID2 of the service registered by API
*/
String mDynamicNfcid2;
/**
* The uid of the package the service belongs to
*/
final int mUid;
/**
* @hide
*/
public NfcFServiceInfo(ResolveInfo info, String description,
String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2,
int uid) {
this.mService = info;
this.mDescription = description;
this.mSystemCode = systemCode;
this.mDynamicSystemCode = dynamicSystemCode;
this.mNfcid2 = nfcid2;
this.mDynamicNfcid2 = dynamicNfcid2;
this.mUid = uid;
}
public NfcFServiceInfo(PackageManager pm, ResolveInfo info)
throws XmlPullParserException, IOException {
ServiceInfo si = info.serviceInfo;
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, HostNfcFService.SERVICE_META_DATA);
if (parser == null) {
throw new XmlPullParserException("No " + HostNfcFService.SERVICE_META_DATA +
" meta-data");
}
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
eventType != XmlPullParser.END_DOCUMENT) {
eventType = parser.next();
}
String tagName = parser.getName();
if (!"host-nfcf-service".equals(tagName)) {
throw new XmlPullParserException(
"Meta-data does not start with <host-nfcf-service> tag");
}
Resources res = pm.getResourcesForApplication(si.applicationInfo);
AttributeSet attrs = Xml.asAttributeSet(parser);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.HostNfcFService);
mService = info;
mDescription = sa.getString(
com.android.internal.R.styleable.HostNfcFService_description);
mDynamicSystemCode = null;
mDynamicNfcid2 = null;
sa.recycle();
String systemCode = null;
String nfcid2 = null;
final int depth = parser.getDepth();
while (((eventType = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && eventType != XmlPullParser.END_DOCUMENT) {
tagName = parser.getName();
if (eventType == XmlPullParser.START_TAG &&
"system-code-filter".equals(tagName) && systemCode == null) {
final TypedArray a = res.obtainAttributes(attrs,
com.android.internal.R.styleable.SystemCodeFilter);
systemCode = a.getString(
com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase();
if (!NfcFCardEmulation.isValidSystemCode(systemCode) &&
!systemCode.equalsIgnoreCase("NULL")) {
Log.e(TAG, "Invalid System Code: " + systemCode);
systemCode = null;
}
a.recycle();
} else if (eventType == XmlPullParser.START_TAG &&
"nfcid2-filter".equals(tagName) && nfcid2 == null) {
final TypedArray a = res.obtainAttributes(attrs,
com.android.internal.R.styleable.Nfcid2Filter);
nfcid2 = a.getString(
com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase();
if (!nfcid2.equalsIgnoreCase("RANDOM") &&
!nfcid2.equalsIgnoreCase("NULL") &&
!NfcFCardEmulation.isValidNfcid2(nfcid2)) {
Log.e(TAG, "Invalid NFCID2: " + nfcid2);
nfcid2 = null;
}
a.recycle();
}
}
mSystemCode = (systemCode == null ? "NULL" : systemCode);
mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2);
} catch (NameNotFoundException e) {
throw new XmlPullParserException("Unable to create context for: " + si.packageName);
} finally {
if (parser != null) parser.close();
}
// Set uid
mUid = si.applicationInfo.uid;
}
public ComponentName getComponent() {
return new ComponentName(mService.serviceInfo.packageName,
mService.serviceInfo.name);
}
public String getSystemCode() {
return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode);
}
public void setOrReplaceDynamicSystemCode(String systemCode) {
mDynamicSystemCode = systemCode;
}
public String getNfcid2() {
return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2);
}
public void setOrReplaceDynamicNfcid2(String nfcid2) {
mDynamicNfcid2 = nfcid2;
}
public String getDescription() {
return mDescription;
}
public int getUid() {
return mUid;
}
public CharSequence loadLabel(PackageManager pm) {
return mService.loadLabel(pm);
}
public Drawable loadIcon(PackageManager pm) {
return mService.loadIcon(pm);
}
@Override
public String toString() {
StringBuilder out = new StringBuilder("NfcFService: ");
out.append(getComponent());
out.append(", description: " + mDescription);
out.append(", System Code: " + mSystemCode);
if (mDynamicSystemCode != null) {
out.append(", dynamic System Code: " + mDynamicSystemCode);
}
out.append(", NFCID2: " + mNfcid2);
if (mDynamicNfcid2 != null) {
out.append(", dynamic NFCID2: " + mDynamicNfcid2);
}
return out.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NfcFServiceInfo)) return false;
NfcFServiceInfo thatService = (NfcFServiceInfo) o;
if (!thatService.getComponent().equals(this.getComponent())) return false;
if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
return true;
}
@Override
public int hashCode() {
return getComponent().hashCode();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
mService.writeToParcel(dest, flags);
dest.writeString(mDescription);
dest.writeString(mSystemCode);
dest.writeInt(mDynamicSystemCode != null ? 1 : 0);
if (mDynamicSystemCode != null) {
dest.writeString(mDynamicSystemCode);
}
dest.writeString(mNfcid2);
dest.writeInt(mDynamicNfcid2 != null ? 1 : 0);
if (mDynamicNfcid2 != null) {
dest.writeString(mDynamicNfcid2);
}
dest.writeInt(mUid);
};
public static final Parcelable.Creator<NfcFServiceInfo> CREATOR =
new Parcelable.Creator<NfcFServiceInfo>() {
@Override
public NfcFServiceInfo createFromParcel(Parcel source) {
ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
String description = source.readString();
String systemCode = source.readString();
String dynamicSystemCode = null;
if (source.readInt() != 0) {
dynamicSystemCode = source.readString();
}
String nfcid2 = source.readString();
String dynamicNfcid2 = null;
if (source.readInt() != 0) {
dynamicNfcid2 = source.readString();
}
int uid = source.readInt();
NfcFServiceInfo service = new NfcFServiceInfo(info, description,
systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid);
return service;
}
@Override
public NfcFServiceInfo[] newArray(int size) {
return new NfcFServiceInfo[size];
}
};
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(" " + getComponent() +
" (Description: " + getDescription() + ")");
pw.println(" System Code: " + getSystemCode());
pw.println(" NFCID2: " + getNfcid2());
}
}

View File

@ -3315,6 +3315,32 @@ i
<attr name="name" />
</declare-styleable>
<!-- Use <code>host-nfcf-service</code> as the root tag of the XML resource that
describes an {@link android.nfc.cardemulation.HostNfcFService} service, which
is referenced from its {@link android.nfc.cardemulation.HostNfcFService#SERVICE_META_DATA}
entry. -->
<declare-styleable name="HostNfcFService">
<!-- Short description of the functionality the service implements. This attribute
is mandatory.-->
<attr name="description" />
</declare-styleable>
<!-- Specify one or more <code>system-code-filter</code> elements inside a
<code>host-nfcf-service</code> element to specify a System Code
your service can handle. -->
<declare-styleable name="SystemCodeFilter">
<!-- The System Code. This attribute is mandatory. -->
<attr name="name" />
</declare-styleable>
<!-- Specify one or more <code>nfcid2-filter</code> elements inside a
<code>host-nfcf-service</code> element to specify a NFCID2
your service can handle. -->
<declare-styleable name="Nfcid2Filter">
<!-- The NFCID2. This attribute is mandatory. -->
<attr name="name" />
</declare-styleable>
<declare-styleable name="ActionMenuItemView">
<attr name="minWidth" />
</declare-styleable>