summaryrefslogtreecommitdiffstats
path: root/src/nfc/qnearfieldtarget_android.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/nfc/qnearfieldtarget_android.cpp')
-rw-r--r--src/nfc/qnearfieldtarget_android.cpp448
1 files changed, 448 insertions, 0 deletions
diff --git a/src/nfc/qnearfieldtarget_android.cpp b/src/nfc/qnearfieldtarget_android.cpp
new file mode 100644
index 00000000..891ce3fc
--- /dev/null
+++ b/src/nfc/qnearfieldtarget_android.cpp
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Centria research and development
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldtarget_android_p.h"
+#include "android/androidjninfc_p.h"
+#include "qdebug.h"
+
+#define NDEFTECHNOLOGY "android.nfc.tech.Ndef"
+#define NDEFFORMATABLETECHNOLOGY "android.nfc.tech.NdefFormatable"
+#define NFCATECHNOLOGY "android.nfc.tech.NfcA"
+#define NFCBTECHNOLOGY "android.nfc.tech.NfcB"
+#define NFCFTECHNOLOGY "android.nfc.tech.NfcF"
+#define NFCVTECHNOLOGY "android.nfc.tech.NfcV"
+#define MIFARECLASSICTECHNOLOGY "android.nfc.tech.MifareClassic"
+#define MIFARECULTRALIGHTTECHNOLOGY "android.nfc.tech.MifareUltralight"
+
+#define MIFARETAG "com.nxp.ndef.mifareclassic"
+#define NFCTAGTYPE1 "org.nfcforum.ndef.type1"
+#define NFCTAGTYPE2 "org.nfcforum.ndef.type2"
+#define NFCTAGTYPE3 "org.nfcforum.ndef.type3"
+#define NFCTAGTYPE4 "org.nfcforum.ndef.type4"
+
+NearFieldTarget::NearFieldTarget(QAndroidJniObject intent, const QByteArray uid, QObject *parent) :
+ QNearFieldTarget(parent),
+ m_intent(intent),
+ m_uid(uid)
+{
+ updateTechList();
+ updateType();
+ setupTargetCheckTimer();
+}
+
+NearFieldTarget::~NearFieldTarget()
+{
+ releaseIntent();
+ emit targetDestroyed(m_uid);
+}
+
+QByteArray NearFieldTarget::uid() const
+{
+ return m_uid;
+}
+
+QNearFieldTarget::Type NearFieldTarget::type() const
+{
+ return m_type;
+}
+
+QNearFieldTarget::AccessMethods NearFieldTarget::accessMethods() const
+{
+ AccessMethods result = NdefAccess;
+ return result;
+}
+
+bool NearFieldTarget::hasNdefMessage()
+{
+ return m_techList.contains(QStringLiteral(NDEFTECHNOLOGY));
+}
+
+QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
+{
+ // Making sure that target has NDEF messages
+ if (!hasNdefMessage())
+ return QNearFieldTarget::RequestId();
+
+ // Making sure that target is still in range
+ QNearFieldTarget::RequestId requestId(new QNearFieldTarget::RequestIdPrivate);
+ if (!m_intent.isValid()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Getting Ndef technology object
+ QAndroidJniObject ndef = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY));
+ if (!ndef.isValid()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnsupportedError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Connect
+ ndef.callMethod<void>("connect");
+ if (catchJavaExceptions()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Get NdefMessage object
+ QAndroidJniObject ndefMessage = ndef.callObjectMethod("getNdefMessage", "()Landroid/nfc/NdefMessage;");
+ if (catchJavaExceptions())
+ ndefMessage = QAndroidJniObject();
+ if (!ndefMessage.isValid()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefReadError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Convert to byte array
+ QAndroidJniObject ndefMessageBA = ndefMessage.callObjectMethod("toByteArray", "()[B");
+ QByteArray ndefMessageQBA = jbyteArrayToQByteArray(ndefMessageBA.object<jbyteArray>());
+
+ // Closing connection
+ ndef.callMethod<void>("close");
+ catchJavaExceptions(); // IOException at this point does not matter anymore.
+
+ // Sending QNdefMessage, requestCompleted and exit.
+ QNdefMessage qNdefMessage = QNdefMessage::fromByteArray(ndefMessageQBA);
+ QMetaObject::invokeMethod(this, "ndefMessageRead", Qt::QueuedConnection,
+ Q_ARG(const QNdefMessage&, qNdefMessage));
+ QMetaObject::invokeMethod(this, "requestCompleted", Qt::QueuedConnection,
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ QMetaObject::invokeMethod(this, "ndefMessageRead", Qt::QueuedConnection,
+ Q_ARG(const QNdefMessage&, qNdefMessage),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+}
+
+
+QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &command)
+{
+ Q_UNUSED(command);
+ Q_EMIT QNearFieldTarget::error(QNearFieldTarget::UnsupportedError, QNearFieldTarget::RequestId());
+ return QNearFieldTarget::RequestId();
+
+ //Not supported for now
+ /*if (command.size() == 0) {
+ Q_EMIT QNearFieldTarget::error(QNearFieldTarget::InvalidParametersError, QNearFieldTarget::RequestId());
+ return QNearFieldTarget::RequestId();
+ }
+
+ AndroidNfc::AttachedJNIEnv aenv;
+ JNIEnv *env = aenv.jniEnv;
+
+ jobject tagTech;
+ if (m_techList.contains(QStringLiteral(NFCATECHNOLOGY))) {
+ tagTech = getTagTechnology(QStringLiteral(NFCATECHNOLOGY));
+ } else if (m_techList.contains(QStringLiteral(NFCBTECHNOLOGY))) {
+ tagTech = getTagTechnology(QStringLiteral(NFCBTECHNOLOGY));
+ } else if (m_techList.contains(QStringLiteral(NFCFTECHNOLOGY))) {
+ tagTech = getTagTechnology(QStringLiteral(NFCFTECHNOLOGY));
+ } else if (m_techList.contains(QStringLiteral(NFCVTECHNOLOGY))) {
+ tagTech = getTagTechnology(QStringLiteral(NFCVTECHNOLOGY));
+ } else {
+ Q_EMIT QNearFieldTarget::error(QNearFieldTarget::UnsupportedError, QNearFieldTarget::RequestId());
+ return QNearFieldTarget::RequestId();
+ }
+
+ QByteArray ba(ba);
+
+ jclass techClass = env->GetObjectClass(tagTech);
+ jmethodID tranceiveMID = env->GetMethodID(techClass, "tranceive", "([B)[B");
+ Q_ASSERT_X(tranceiveMID != 0, "sendCommand", "could not find tranceive method");
+
+ jbyteArray jba = env->NewByteArray(ba.size());
+ env->SetByteArrayRegion(jba, 0, ba.size(), reinterpret_cast<jbyte*>(ba.data()));
+
+ jbyteArray rsp = reinterpret_cast<jbyteArray>(env->CallObjectMethod(tagTech, tranceiveMID, jba));
+
+ jsize len = env->GetArrayLength(rsp);
+ QByteArray rspQBA;
+ rspQBA.resize(len);
+
+ env->GetByteArrayRegion(rsp, 0, len, reinterpret_cast<jbyte*>(rspQBA.data()));
+
+ qDebug() << "Send command returned QBA size: " << rspQBA.size();
+
+
+ env->DeleteLocalRef(jba);
+
+
+ return QNearFieldTarget::RequestId();*/
+}
+
+QNearFieldTarget::RequestId NearFieldTarget::sendCommands(const QList<QByteArray> &commands)
+{
+ QNearFieldTarget::RequestId requestId;
+ for (int i=0; i < commands.size(); i++){
+ requestId = sendCommand(commands.at(i));
+ }
+ return requestId;
+}
+
+QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QList<QNdefMessage> &messages)
+{
+ if (messages.size() == 0)
+ return QNearFieldTarget::RequestId();
+
+ if (messages.size() > 1)
+ qWarning("QNearFieldTarget::writeNdefMessages: Android supports writing only one NDEF message per tag.");
+
+ QAndroidJniEnvironment env;
+ const char *writeMethod;
+ QAndroidJniObject tagTechnology;
+
+ // Getting write method
+ if (m_techList.contains(QStringLiteral(NDEFFORMATABLETECHNOLOGY))) {
+ tagTechnology = getTagTechnology(QStringLiteral(NDEFFORMATABLETECHNOLOGY));
+ writeMethod = "format";
+ } else if (m_techList.contains(QStringLiteral(NDEFTECHNOLOGY))) {
+ tagTechnology = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY));
+ writeMethod = "writeNdefMessage";
+ } else {
+ // An invalid request id will be returned if the target does not support writing NDEF messages.
+ return QNearFieldTarget::RequestId();
+ }
+
+ // Connecting
+ QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
+ tagTechnology.callMethod<void>("connect");
+ if (catchJavaExceptions()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Making NdefMessage object
+ const QNdefMessage &message = messages.first();
+ QByteArray ba = message.toByteArray();
+ QAndroidJniObject jba = env->NewByteArray(ba.size());
+ env->SetByteArrayRegion(jba.object<jbyteArray>(), 0, ba.size(), reinterpret_cast<jbyte*>(ba.data()));
+ QAndroidJniObject jmessage = QAndroidJniObject("android/nfc/NdefMessage", "([B)V", jba.object<jbyteArray>());
+ if (catchJavaExceptions()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Writing
+ tagTechnology.callMethod<void>(writeMethod, "(Landroid/nfc/NdefMessage;)V", jmessage.object<jobject>());
+ if (catchJavaExceptions()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefWriteError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+
+ // Closing connection, sending signal and exit
+ tagTechnology.callMethod<void>("close");
+ catchJavaExceptions(); // IOException at this point does not matter anymore.
+ QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection);
+ return requestId;
+}
+
+void NearFieldTarget::setIntent(QAndroidJniObject intent)
+{
+ if (m_intent == intent)
+ return;
+
+ releaseIntent();
+ m_intent = intent;
+ if (m_intent.isValid()) {
+ // Updating tech list and type in case of there is another tag with same UID as one before.
+ updateTechList();
+ updateType();
+ m_targetCheckTimer->start();
+ }
+}
+
+void NearFieldTarget::checkIsTargetLost()
+{
+ if (!m_intent.isValid() || m_techList.isEmpty()) {
+ handleTargetLost();
+ return;
+ }
+ // Using first available technology to check connection
+ QString techStr = m_techList.first();
+ QAndroidJniObject tagTech = getTagTechnology(techStr);
+ tagTech.callMethod<void>("connect");
+ if (catchJavaExceptions()) {
+ handleTargetLost();
+ return;
+ }
+ tagTech.callMethod<void>("close");
+ if (catchJavaExceptions())
+ handleTargetLost();
+}
+
+void NearFieldTarget::releaseIntent()
+{
+ m_targetCheckTimer->stop();
+
+ m_intent = QAndroidJniObject();
+}
+
+void NearFieldTarget::updateTechList()
+{
+ if (!m_intent.isValid())
+ return;
+
+ // Getting tech list
+ QAndroidJniEnvironment env;
+ QAndroidJniObject tag = AndroidNfc::getTag(m_intent);
+ QAndroidJniObject techListArray = tag.callObjectMethod("getTechList", "()[Ljava/lang/String;");
+ if (!techListArray.isValid()) {
+ handleTargetLost();
+ return;
+ }
+
+ // Converting tech list array to QStringList.
+ m_techList.clear();
+ jsize techCount = env->GetArrayLength(techListArray.object<jobjectArray>());
+ for (jsize i = 0; i < techCount; ++i) {
+ QAndroidJniObject tech = env->GetObjectArrayElement(techListArray.object<jobjectArray>(), i);
+ m_techList.append(tech.callObjectMethod<jstring>("toString").toString());
+ }
+}
+
+void NearFieldTarget::updateType()
+{
+ m_type = getTagType();
+}
+
+QNearFieldTarget::Type NearFieldTarget::getTagType() const
+{
+ QAndroidJniEnvironment env;
+
+ if (m_techList.contains(QStringLiteral(NDEFTECHNOLOGY))) {
+ QAndroidJniObject ndef = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY));
+ QString qtype = ndef.callObjectMethod("getType", "()Ljava/lang/String;").toString();
+
+ if (qtype.compare(QStringLiteral(MIFARETAG)) == 0)
+ return MifareTag;
+ if (qtype.compare(QStringLiteral(NFCTAGTYPE1)) == 0)
+ return NfcTagType1;
+ if (qtype.compare(QStringLiteral(NFCTAGTYPE2)) == 0)
+ return NfcTagType2;
+ if (qtype.compare(QStringLiteral(NFCTAGTYPE3)) == 0)
+ return NfcTagType3;
+ if (qtype.compare(QStringLiteral(NFCTAGTYPE4)) == 0)
+ return NfcTagType4;
+ return ProprietaryTag;
+ } else if (m_techList.contains(QStringLiteral(NFCATECHNOLOGY))) {
+ if (m_techList.contains(QStringLiteral(MIFARECLASSICTECHNOLOGY)))
+ return MifareTag;
+
+ // Checking ATQA/SENS_RES
+ // xxx0 0000 xxxx xxxx: Identifies tag Type 1 platform
+ QAndroidJniObject nfca = getTagTechnology(QStringLiteral(NFCATECHNOLOGY));
+ QAndroidJniObject atqaBA = nfca.callObjectMethod("getAtqa", "()[B");
+ QByteArray atqaQBA = jbyteArrayToQByteArray(atqaBA.object<jbyteArray>());
+ if (atqaQBA.isEmpty())
+ return ProprietaryTag;
+ if ((atqaQBA[0] & 0x1F) == 0x00)
+ return NfcTagType1;
+
+ // Checking SAK/SEL_RES
+ // xxxx xxxx x00x x0xx: Identifies tag Type 2 platform
+ // xxxx xxxx x01x x0xx: Identifies tag Type 4 platform
+ jshort sakS = nfca.callMethod<jshort>("getSak");
+ if ((sakS & 0x0064) == 0x0000)
+ return NfcTagType2;
+ else if ((sakS & 0x0064) == 0x0020)
+ return NfcTagType4;
+ return ProprietaryTag;
+ } else if (m_techList.contains(QStringLiteral(NFCBTECHNOLOGY))) {
+ return NfcTagType4;
+ } else if (m_techList.contains(QStringLiteral(NFCFTECHNOLOGY))) {
+ return NfcTagType3;
+ }
+
+ return ProprietaryTag;
+}
+
+void NearFieldTarget::setupTargetCheckTimer()
+{
+ m_targetCheckTimer = new QTimer(this);
+ m_targetCheckTimer->setInterval(1000);
+ connect(m_targetCheckTimer, SIGNAL(timeout()), this, SLOT(checkIsTargetLost()));
+ m_targetCheckTimer->start();
+}
+
+void NearFieldTarget::handleTargetLost()
+{
+ releaseIntent();
+ emit targetLost(this);
+}
+
+QAndroidJniObject NearFieldTarget::getTagTechnology(const QString &tech) const
+{
+ QString techClass(tech);
+ techClass.replace(QLatin1Char('.'), QLatin1Char('/'));
+
+ // Getting requested technology
+ QAndroidJniObject tag = AndroidNfc::getTag(m_intent);
+ const QString sig = QString::fromUtf8("(Landroid/nfc/Tag;)L%1;");
+ QAndroidJniObject tagtech = QAndroidJniObject::callStaticObjectMethod(techClass.toUtf8().constData(), "get",
+ sig.arg(techClass).toUtf8().constData(), tag.object<jobject>());
+ return tagtech;
+}
+
+QByteArray NearFieldTarget::jbyteArrayToQByteArray(const jbyteArray &byteArray) const
+{
+ QAndroidJniEnvironment env;
+ QByteArray resultArray;
+ jsize len = env->GetArrayLength(byteArray);
+ resultArray.resize(len);
+ env->GetByteArrayRegion(byteArray, 0, len, reinterpret_cast<jbyte*>(resultArray.data()));
+ return resultArray;
+}
+
+bool NearFieldTarget::catchJavaExceptions() const
+{
+ QAndroidJniEnvironment env;
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+}