blob: 38d39e8d65fb34b20eda9a6c5b8c67e890479cc0 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.nfc.dhimpl;
import android.content.Context;
import android.nfc.cardemulation.HostApduService;
import android.nfc.tech.Ndef;
import android.nfc.tech.TagTechnology;
import android.os.Bundle;
import android.util.Log;
import com.android.nfc.DeviceHost;
import com.android.nfc.NfcDiscoveryParameters;
import com.android.nfc.NfcService;
import com.android.nfc.NfcVendorNciResponse;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
/** Native interface to the NFC Manager functions */
public class NativeNfcManager implements DeviceHost {
private static final String TAG = "NativeNfcManager";
static final String PREF = "NciDeviceHost";
static final String DRIVER_NAME = "android-nci";
static {
System.loadLibrary("nfc_nci_jni");
}
/* Native structure */
private long mNative;
private int mIsoDepMaxTransceiveLength;
private final DeviceHostListener mListener;
private final Context mContext;
private final Object mLock = new Object();
private final HashMap<Integer, byte[]> mT3tIdentifiers = new HashMap<Integer, byte[]>();
private static final int MIN_POLLING_FRAME_TLV_SIZE = 5;
private static final int TAG_FIELD_CHANGE = 0;
private static final int TAG_NFC_A = 1;
private static final int TAG_NFC_B = 2;
private static final int TAG_NFC_F = 3;
private static final int TAG_NFC_UNKNOWN = 7;
private static final int NCI_HEADER_MIN_LEN = 3;
private static final int NCI_GID_INDEX = 0;
private static final int NCI_OID_INDEX = 1;
private static final int OP_CODE_INDEX = 3;
public NativeNfcManager(Context context, DeviceHostListener listener) {
mListener = listener;
initializeNativeStructure();
mContext = context;
}
public native boolean initializeNativeStructure();
private native boolean doDownload();
@Override
public boolean checkFirmware() {
return doDownload();
}
private native boolean doInitialize();
private native int getIsoDepMaxTransceiveLength();
@Override
public boolean initialize() {
boolean ret = doInitialize();
mIsoDepMaxTransceiveLength = getIsoDepMaxTransceiveLength();
return ret;
}
private native void doEnableDtaMode();
@Override
public void enableDtaMode() {
doEnableDtaMode();
}
private native void doDisableDtaMode();
@Override
public void disableDtaMode() {
Log.d(TAG, "disableDtaMode : entry");
doDisableDtaMode();
}
private native void doFactoryReset();
@Override
public void factoryReset() {
doFactoryReset();
}
private native boolean doSetPowerSavingMode(boolean flag);
@Override
public boolean setPowerSavingMode(boolean flag) {
return doSetPowerSavingMode(flag);
}
private native void doShutdown();
@Override
public void shutdown() {
doShutdown();
}
private native boolean doDeinitialize();
@Override
public boolean deinitialize() {
return doDeinitialize();
}
@Override
public String getName() {
return DRIVER_NAME;
}
@Override
public native boolean sendRawFrame(byte[] data);
@Override
public native boolean routeAid(byte[] aid, int route, int aidInfo, int power);
@Override
public native boolean unrouteAid(byte[] aid);
@Override
public native boolean commitRouting();
public native int doRegisterT3tIdentifier(byte[] t3tIdentifier);
@Override
public boolean isObserveModeSupported() {
if (!android.nfc.Flags.nfcObserveMode()) {
return false;
}
return mContext.getResources().getBoolean(
com.android.nfc.R.bool.nfc_observe_mode_supported);
}
@Override
public native boolean setObserveMode(boolean enabled);
@Override
public void registerT3tIdentifier(byte[] t3tIdentifier) {
synchronized (mLock) {
int handle = doRegisterT3tIdentifier(t3tIdentifier);
if (handle != 0xffff) {
mT3tIdentifiers.put(Integer.valueOf(handle), t3tIdentifier);
}
}
}
public native void doDeregisterT3tIdentifier(int handle);
@Override
public void deregisterT3tIdentifier(byte[] t3tIdentifier) {
synchronized (mLock) {
Iterator<Integer> it = mT3tIdentifiers.keySet().iterator();
while (it.hasNext()) {
int handle = it.next().intValue();
byte[] value = mT3tIdentifiers.get(handle);
if (Arrays.equals(value, t3tIdentifier)) {
doDeregisterT3tIdentifier(handle);
mT3tIdentifiers.remove(handle);
break;
}
}
}
}
@Override
public void clearT3tIdentifiersCache() {
synchronized (mLock) {
mT3tIdentifiers.clear();
}
}
@Override
public native int getLfT3tMax();
@Override
public native void doSetScreenState(int screen_state_mask);
@Override
public native int getNciVersion();
private native void doEnableDiscovery(
int techMask,
boolean enableLowPowerPolling,
boolean enableReaderMode,
boolean enableHostRouting,
boolean enableP2p,
boolean restart);
@Override
public void enableDiscovery(NfcDiscoveryParameters params, boolean restart) {
doEnableDiscovery(
params.getTechMask(),
params.shouldEnableLowPowerDiscovery(),
params.shouldEnableReaderMode(),
params.shouldEnableHostRouting(),
params.shouldEnableP2p(),
restart);
}
@Override
public native void disableDiscovery();
private native void doResetTimeouts();
@Override
public void resetTimeouts() {
doResetTimeouts();
}
@Override
public native void doAbort(String msg);
private native boolean doSetTimeout(int tech, int timeout);
@Override
public boolean setTimeout(int tech, int timeout) {
return doSetTimeout(tech, timeout);
}
private native int doGetTimeout(int tech);
@Override
public int getTimeout(int tech) {
return doGetTimeout(tech);
}
@Override
public boolean canMakeReadOnly(int ndefType) {
return (ndefType == Ndef.TYPE_1 || ndefType == Ndef.TYPE_2);
}
@Override
public int getMaxTransceiveLength(int technology) {
switch (technology) {
case (TagTechnology.NFC_A):
case (TagTechnology.MIFARE_CLASSIC):
case (TagTechnology.MIFARE_ULTRALIGHT):
return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC
case (TagTechnology.NFC_B):
/////////////////////////////////////////////////////////////////
// Broadcom: Since BCM2079x supports this, set NfcB max size.
// return 0; // PN544 does not support transceive of raw NfcB
return 253; // PN544 does not support transceive of raw NfcB
case (TagTechnology.NFC_V):
return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC
case (TagTechnology.ISO_DEP):
return mIsoDepMaxTransceiveLength;
case (TagTechnology.NFC_F):
return 255;
default:
return 0;
}
}
public native int getAidTableSize();
@Override
public boolean getExtendedLengthApdusSupported() {
/* 261 is the default size if extended length frames aren't supported */
if (getMaxTransceiveLength(TagTechnology.ISO_DEP) > 261) return true;
return false;
}
private native void doDump(FileDescriptor fd);
@Override
public void dump(FileDescriptor fd) {
doDump(fd);
}
private native boolean doSetNfcSecure(boolean enable);
@Override
public boolean setNfcSecure(boolean enable) {
return doSetNfcSecure(enable);
}
private native void doStartStopPolling(boolean start);
@Override
public void startStopPolling(boolean start) {
doStartStopPolling(start);
}
private native void doSetNfceePowerAndLinkCtrl(boolean enable);
@Override
public void setNfceePowerAndLinkCtrl(boolean enable) {
doSetNfceePowerAndLinkCtrl(enable);
}
@Override
public native byte[] getRoutingTable();
@Override
public native int getMaxRoutingTableSize();
private native NfcVendorNciResponse nativeSendRawVendorCmd(
int mt, int gid, int oid, byte[] payload);
@Override
public NfcVendorNciResponse sendRawVendorCmd(int mt, int gid, int oid, byte[] payload) {
NfcVendorNciResponse res= nativeSendRawVendorCmd(mt, gid, oid, payload);
return res;
}
/** Notifies Ndef Message (TODO: rename into notifyTargetDiscovered) */
private void notifyNdefMessageListeners(NativeNfcTag tag) {
mListener.onRemoteEndpointDiscovered(tag);
}
private void notifyHostEmuActivated(int technology) {
mListener.onHostCardEmulationActivated(technology);
}
private void notifyHostEmuData(int technology, byte[] data) {
mListener.onHostCardEmulationData(technology, data);
}
private void notifyHostEmuDeactivated(int technology) {
mListener.onHostCardEmulationDeactivated(technology);
}
private void notifyRfFieldActivated() {
mListener.onRemoteFieldActivated();
}
private void notifyRfFieldDeactivated() {
mListener.onRemoteFieldDeactivated();
}
private void notifyTransactionListeners(byte[] aid, byte[] data, String evtSrc) {
mListener.onNfcTransactionEvent(aid, data, evtSrc);
}
private void notifyEeUpdated() {
mListener.onEeUpdated();
}
private void notifyHwErrorReported() {
mListener.onHwErrorReported();
}
private void notifyPollingLoopFrame(int data_len, byte[] p_data) {
if (data_len < MIN_POLLING_FRAME_TLV_SIZE) {
return;
}
Bundle frame = new Bundle();
final int header_len = 2;
int pos = header_len;
final int TLV_len_offset = 0;
final int TLV_type_offset = 2;
final int TLV_timestamp_offset = 3;
final int TLV_gain_offset = 7;
final int TLV_data_offset = 8;
while (pos + TLV_len_offset < data_len) {
int type = p_data[pos + TLV_type_offset];
int length = p_data[pos + TLV_len_offset];
if (pos + length + 1 > data_len) {
// Frame is bigger than buffer.
Log.e(TAG, "Polling frame data is longer than buffer data length.");
break;
}
switch (type) {
case TAG_FIELD_CHANGE:
frame.putChar(
HostApduService.POLLING_LOOP_TYPE_KEY,
p_data[pos + TLV_data_offset] != 0x00
? HostApduService.POLLING_LOOP_TYPE_ON
: HostApduService.POLLING_LOOP_TYPE_OFF);
break;
case TAG_NFC_A:
frame.putChar(HostApduService.POLLING_LOOP_TYPE_KEY,
HostApduService.POLLING_LOOP_TYPE_A);
break;
case TAG_NFC_B:
frame.putChar(HostApduService.POLLING_LOOP_TYPE_KEY,
HostApduService.POLLING_LOOP_TYPE_B);
break;
case TAG_NFC_F:
frame.putChar(HostApduService.POLLING_LOOP_TYPE_KEY,
HostApduService.POLLING_LOOP_TYPE_F);
break;
case TAG_NFC_UNKNOWN:
frame.putChar(
HostApduService.POLLING_LOOP_TYPE_KEY,
HostApduService.POLLING_LOOP_TYPE_UNKNOWN);
frame.putByteArray(
HostApduService.POLLING_LOOP_DATA_KEY,
Arrays.copyOfRange(
p_data, pos + TLV_data_offset, pos + TLV_timestamp_offset + length));
break;
default:
Log.e(TAG, "Unknown polling loop tag type.");
}
if (pos + TLV_gain_offset <= data_len) {
byte gain = p_data[pos + TLV_gain_offset];
frame.putByte(HostApduService.POLLING_LOOP_GAIN_KEY, gain);
}
if (pos + TLV_timestamp_offset + 3 < data_len) {
int timestamp = ByteBuffer.wrap(p_data, pos + TLV_timestamp_offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
frame.putInt(HostApduService.POLLING_LOOP_TIMESTAMP_KEY, timestamp);
}
pos += (length + 2);
}
mListener.onPollingLoopDetected(frame);
}
private void notifyVendorSpecificEvent(int event, int dataLen, byte[] pData) {
if (pData.length < NCI_HEADER_MIN_LEN || dataLen != pData.length) {
Log.e(TAG, "Invalid data");
return;
}
if (android.nfc.Flags.nfcVendorCmd()) {
mListener.onVendorSpecificEvent(pData[NCI_GID_INDEX], pData[NCI_OID_INDEX],
Arrays.copyOfRange(pData, OP_CODE_INDEX, pData.length));
}
}
@Override
public native void setDiscoveryTech(int pollTech, int listenTech);
@Override
public native void resetDiscoveryTech();
@Override
public native void clearRoutingEntry(int clearFlags);
@Override
public native void setIsoDepProtocolRoute(int route);
@Override
public native void setTechnologyABRoute(int route);
private void notifyCommandTimeout() {
NfcService.getInstance().storeNativeCrashLogs();
}
}