blob: 26595d54b57934a756fd8945c0e53bc99b66fb02 [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;
import android.nfc.technology.NfcA;
import android.nfc.technology.NfcB;
import android.nfc.technology.NfcF;
import android.nfc.technology.IsoDep;
import android.nfc.technology.TagTechnology;
import android.nfc.technology.Ndef;
import android.nfc.NdefMessage;
import android.os.Bundle;
import android.util.Log;
/**
* Native interface to the NFC tag functions
*/
public class NativeNfcTag {
static final boolean DBG = false;
private int mHandle;
private int[] mTechList;
private int[] mTechHandles;
private Bundle[] mTechExtras;
private byte[][] mTechPollBytes;
private byte[][] mTechActBytes;
private byte[] mUid;
private int mConnectedTechnology; // Index in mTechList
private final String TAG = "NativeNfcTag";
private PresenceCheckWatchdog mWatchdog;
class PresenceCheckWatchdog extends Thread {
private boolean isPresent = true;
private boolean isRunning = true;
public void reset() {
this.interrupt();
}
public void end() {
isRunning = false;
this.interrupt();
}
@Override
public void run() {
if (DBG) Log.d(TAG, "Starting background presence check");
while (isPresent && isRunning) {
try {
Thread.sleep(1000);
isPresent = doPresenceCheck();
} catch (InterruptedException e) {
// Activity detected, loop
}
}
// Restart the polling loop if the tag is not here any more
if (!isPresent) {
Log.d(TAG, "Tag lost, restarting polling loop");
doDisconnect();
}
if (DBG) Log.d(TAG, "Stopping background presence check");
}
}
private native boolean doConnect(int handle);
public synchronized boolean connect(int technology) {
boolean isSuccess = false;
for (int i = 0; i < mTechList.length; i++) {
if (mTechList[i] == technology) {
// Get the handle and connect
isSuccess = doConnect(mTechHandles[i]);
if (isSuccess) {
mConnectedTechnology = i;
mWatchdog = new PresenceCheckWatchdog();
mWatchdog.start();
}
break;
}
}
return isSuccess;
}
public synchronized boolean connect() {
// Just take the first handle in the list
boolean isSuccess = false;
if ((mTechHandles.length > 0) && (mTechList.length > 0)) {
int handle = mTechHandles[0];
isSuccess = doConnect(handle);
if (isSuccess) {
mConnectedTechnology = 0;
mWatchdog = new PresenceCheckWatchdog();
mWatchdog.start();
}
}
return isSuccess;
}
native boolean doDisconnect();
public synchronized boolean disconnect() {
if (mWatchdog != null) {
mWatchdog.end();
}
mConnectedTechnology = -1;
return doDisconnect();
}
native boolean doReconnect();
public synchronized boolean reconnect() {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doReconnect();
}
private native byte[] doTransceive(byte[] data, boolean raw);
public synchronized byte[] transceive(byte[] data, boolean raw) {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doTransceive(data, raw);
}
private native boolean doCheckNdef(int[] ndefinfo);
public synchronized boolean checkNdef(int[] ndefinfo) {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doCheckNdef(ndefinfo);
}
private native byte[] doRead();
public synchronized byte[] read() {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doRead();
}
private native boolean doWrite(byte[] buf);
public synchronized boolean write(byte[] buf) {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doWrite(buf);
}
native boolean doPresenceCheck();
public synchronized boolean presenceCheck() {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doPresenceCheck();
}
native boolean doNdefFormat(byte[] key);
public synchronized boolean formatNdef(byte[] key) {
if (mWatchdog != null) {
mWatchdog.reset();
}
return doNdefFormat(key);
}
private NativeNfcTag() {
}
public int getHandle() {
return mHandle;
}
public byte[] getUid() {
return mUid;
}
public int[] getTechList() {
return mTechList;
}
// This method exists to "patch in" the ndef technologies,
// which is done inside Java instead of the native JNI code.
// To not create some nasty dependencies on the order on which things
// are called (most notably getTechExtras()), it needs some additional
// checking.
public void addNdefTechnology(NdefMessage msg, int maxLength, int cardState) {
synchronized (this) {
int[] mNewTechList = new int[mTechList.length + 1];
System.arraycopy(mTechList, 0, mNewTechList, 0, mTechList.length);
mNewTechList[mTechList.length] = TagTechnology.NDEF;
mTechList = mNewTechList;
Bundle extras = new Bundle();
extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg);
extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength);
extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState);
if (mTechExtras == null) {
// This will build the tech extra's for the first time,
// including a NULL ref for the NDEF tech we generated above.
Bundle[] builtTechExtras = getTechExtras();
builtTechExtras[builtTechExtras.length - 1] = extras;
}
else {
// Tech extras were built before, patch the NDEF one in
Bundle[] oldTechExtras = getTechExtras();
Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1];
System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length);
newTechExtras[oldTechExtras.length] = extras;
mTechExtras = newTechExtras;
}
}
}
private boolean hasTech(int tech) {
boolean hasTech = false;
for (int i = 0; i < mTechList.length; i++) {
if (mTechList[i] == tech) {
hasTech = true;
break;
}
}
return hasTech;
}
public Bundle[] getTechExtras() {
synchronized (this) {
if (mTechExtras != null) return mTechExtras;
mTechExtras = new Bundle[mTechList.length];
for (int i = 0; i < mTechList.length; i++) {
Bundle extras = new Bundle();
switch (mTechList[i]) {
case TagTechnology.NFC_A: {
byte[] actBytes = mTechActBytes[i];
if ((actBytes != null) && (actBytes.length > 0)) {
extras.putShort(NfcA.EXTRA_SAK, (short) (actBytes[0] & (short) 0xFF));
} else {
// Unfortunately Jewel doesn't have act bytes,
// ignore this case.
}
extras.putByteArray(NfcA.EXTRA_ATQA, mTechPollBytes[i]);
break;
}
case TagTechnology.NFC_B: {
extras.putByteArray(NfcB.EXTRA_ATQB, mTechPollBytes[i]);
break;
}
case TagTechnology.NFC_F: {
byte[] pmm = new byte[8];
byte[] sc = new byte[2];
if (mTechPollBytes[i].length >= 8) {
// At least pmm is present
System.arraycopy(mTechPollBytes[i], 0, pmm, 0, 8);
extras.putByteArray(NfcF.EXTRA_PMM, pmm);
}
if (mTechPollBytes[i].length == 10) {
System.arraycopy(mTechPollBytes[i], 8, sc, 0, 2);
extras.putByteArray(NfcF.EXTRA_SC, sc);
}
break;
}
case TagTechnology.ISO_DEP: {
if (hasTech(TagTechnology.NFC_A)) {
extras.putByteArray(IsoDep.EXTRA_HIST_BYTES, mTechActBytes[i]);
}
else {
extras.putByteArray(IsoDep.EXTRA_ATTRIB, mTechActBytes[i]);
}
break;
}
default: {
// Leave the entry in the array null
continue;
}
}
mTechExtras[i] = extras;
}
return mTechExtras;
}
}
}