Merge "Implementation of HCE for NFC-F."
am: e168012ff2
* commit 'e168012ff21408f8be85040bb4ac977061519f62':
Implementation of HCE for NFC-F.
This commit is contained in:
@ -202,6 +202,7 @@ LOCAL_SRC_FILES += \
|
|||||||
core/java/android/nfc/INfcAdapterExtras.aidl \
|
core/java/android/nfc/INfcAdapterExtras.aidl \
|
||||||
core/java/android/nfc/INfcTag.aidl \
|
core/java/android/nfc/INfcTag.aidl \
|
||||||
core/java/android/nfc/INfcCardEmulation.aidl \
|
core/java/android/nfc/INfcCardEmulation.aidl \
|
||||||
|
core/java/android/nfc/INfcFCardEmulation.aidl \
|
||||||
core/java/android/nfc/INfcUnlockHandler.aidl \
|
core/java/android/nfc/INfcUnlockHandler.aidl \
|
||||||
core/java/android/os/IBatteryPropertiesListener.aidl \
|
core/java/android/os/IBatteryPropertiesListener.aidl \
|
||||||
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
|
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
|
||||||
|
@ -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_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 = "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 = "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_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_PRINTING = "android.software.print";
|
||||||
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
|
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";
|
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 {
|
public abstract class OffHostApduService extends android.app.Service {
|
||||||
ctor public OffHostApduService();
|
ctor public OffHostApduService();
|
||||||
method public abstract android.os.IBinder onBind(android.content.Intent);
|
method public abstract android.os.IBinder onBind(android.content.Intent);
|
||||||
|
@ -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_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 = "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 = "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_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_PRINTING = "android.software.print";
|
||||||
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
|
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";
|
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 {
|
public abstract class OffHostApduService extends android.app.Service {
|
||||||
ctor public OffHostApduService();
|
ctor public OffHostApduService();
|
||||||
method public abstract android.os.IBinder onBind(android.content.Intent);
|
method public abstract android.os.IBinder onBind(android.content.Intent);
|
||||||
|
@ -1293,6 +1293,14 @@ public abstract class PackageManager {
|
|||||||
@SdkConstant(SdkConstantType.FEATURE)
|
@SdkConstant(SdkConstantType.FEATURE)
|
||||||
public static final String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
|
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
|
* Feature for {@link #getSystemAvailableFeatures} and
|
||||||
* {@link #hasSystemFeature}: The device supports the OpenGL ES
|
* {@link #hasSystemFeature}: The device supports the OpenGL ES
|
||||||
|
@ -26,6 +26,7 @@ import android.nfc.IAppCallback;
|
|||||||
import android.nfc.INfcAdapterExtras;
|
import android.nfc.INfcAdapterExtras;
|
||||||
import android.nfc.INfcTag;
|
import android.nfc.INfcTag;
|
||||||
import android.nfc.INfcCardEmulation;
|
import android.nfc.INfcCardEmulation;
|
||||||
|
import android.nfc.INfcFCardEmulation;
|
||||||
import android.nfc.INfcUnlockHandler;
|
import android.nfc.INfcUnlockHandler;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ interface INfcAdapter
|
|||||||
{
|
{
|
||||||
INfcTag getNfcTagInterface();
|
INfcTag getNfcTagInterface();
|
||||||
INfcCardEmulation getNfcCardEmulationInterface();
|
INfcCardEmulation getNfcCardEmulationInterface();
|
||||||
|
INfcFCardEmulation getNfcFCardEmulationInterface();
|
||||||
INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
|
INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
|
||||||
|
|
||||||
int getState();
|
int getState();
|
||||||
|
36
core/java/android/nfc/INfcFCardEmulation.aidl
Normal file
36
core/java/android/nfc/INfcFCardEmulation.aidl
Normal 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();
|
||||||
|
}
|
@ -294,6 +294,7 @@ public final class NfcAdapter {
|
|||||||
static INfcAdapter sService;
|
static INfcAdapter sService;
|
||||||
static INfcTag sTagService;
|
static INfcTag sTagService;
|
||||||
static INfcCardEmulation sCardEmulationService;
|
static INfcCardEmulation sCardEmulationService;
|
||||||
|
static INfcFCardEmulation sNfcFCardEmulationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The NfcAdapter object for each application context.
|
* The NfcAdapter object for each application context.
|
||||||
@ -452,6 +453,13 @@ public final class NfcAdapter {
|
|||||||
throw new UnsupportedOperationException();
|
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;
|
sIsInitialized = true;
|
||||||
}
|
}
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
@ -570,6 +578,15 @@ public final class NfcAdapter {
|
|||||||
return sCardEmulationService;
|
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
|
* NFC service dead - attempt best effort recovery
|
||||||
* @hide
|
* @hide
|
||||||
@ -601,6 +618,12 @@ public final class NfcAdapter {
|
|||||||
Log.e(TAG, "could not retrieve NFC card emulation service during service recovery");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
210
core/java/android/nfc/cardemulation/HostNfcFService.java
Normal file
210
core/java/android/nfc/cardemulation/HostNfcFService.java
Normal 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);
|
||||||
|
}
|
470
core/java/android/nfc/cardemulation/NfcFCardEmulation.java
Normal file
470
core/java/android/nfc/cardemulation/NfcFCardEmulation.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
19
core/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
Normal file
19
core/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
Normal 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;
|
304
core/java/android/nfc/cardemulation/NfcFServiceInfo.java
Normal file
304
core/java/android/nfc/cardemulation/NfcFServiceInfo.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3315,6 +3315,32 @@ i
|
|||||||
<attr name="name" />
|
<attr name="name" />
|
||||||
</declare-styleable>
|
</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">
|
<declare-styleable name="ActionMenuItemView">
|
||||||
<attr name="minWidth" />
|
<attr name="minWidth" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
Reference in New Issue
Block a user