blob: 0b30550198639763c87bd54c7c48b691cc3a590b [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 Centria research and development
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNfc module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qnearfieldmanager_android_p.h"
#include "qnearfieldtarget_android_p.h"
#include "qndeffilter.h"
#include "qndefmessage.h"
#include "qndefrecord.h"
#include "qbytearray.h"
#include "qcoreapplication.h"
#include "qdebug.h"
#include "qlist.h"
#include <QScopedPointer>
#include <QtCore/QMetaType>
#include <QtCore/QMetaMethod>
#include <QtCore/private/qjnihelpers_p.h>
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QAndroidJniObject, broadcastReceiver)
Q_GLOBAL_STATIC(QList<QNearFieldManagerPrivateImpl *>, broadcastListener)
extern "C"
{
JNIEXPORT void JNICALL Java_org_qtproject_qt5_android_nfc_QtNfcBroadcastReceiver_jniOnReceive(
JNIEnv */*env*/, jobject /*javaObject*/, jint state)
{
QNearFieldManager::AdapterState adapterState = static_cast<QNearFieldManager::AdapterState>((int) state);
for (const auto listener : qAsConst(*broadcastListener)) {
Q_EMIT listener->adapterStateChanged(adapterState);
}
}
}
QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl() :
m_detecting(false), m_handlerID(0)
{
qRegisterMetaType<QAndroidJniObject>("QAndroidJniObject");
qRegisterMetaType<QNdefMessage>("QNdefMessage");
if (!broadcastReceiver->isValid()) {
*broadcastReceiver = QAndroidJniObject("org/qtproject/qt5/android/nfc/QtNfcBroadcastReceiver",
"(Landroid/content/Context;)V", QtAndroidPrivate::context());
}
broadcastListener->append(this);
connect(this, &QNearFieldManagerPrivateImpl::targetDetected, this, &QNearFieldManagerPrivateImpl::handlerTargetDetected);
connect(this, &QNearFieldManagerPrivateImpl::targetLost, this, &QNearFieldManagerPrivateImpl::handlerTargetLost);
}
QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl()
{
broadcastListener->removeOne(this);
if (broadcastListener->isEmpty()) {
broadcastReceiver->callMethod<void>("unregisterReceiver");
*broadcastReceiver = QAndroidJniObject();
}
}
void QNearFieldManagerPrivateImpl::handlerTargetDetected(QNearFieldTarget *target)
{
if (ndefMessageHandlers.count() == 0 && ndefFilterHandlers.count() == 0) // if no handler is registered
return;
if (target->hasNdefMessage()) {
connect(reinterpret_cast<NearFieldTarget *>(target), &NearFieldTarget::ndefMessageRead,
this, &QNearFieldManagerPrivateImpl::handlerNdefMessageRead);
connect(target, &QNearFieldTarget::requestCompleted,
this, &QNearFieldManagerPrivateImpl::handlerRequestCompleted);
connect(target, &QNearFieldTarget::error,
this, &QNearFieldManagerPrivateImpl::handlerError);
QNearFieldTarget::RequestId id = target->readNdefMessages();
m_idToTarget.insert(id, target);
}
}
void QNearFieldManagerPrivateImpl::handlerTargetLost(QNearFieldTarget *target)
{
disconnect(reinterpret_cast<NearFieldTarget *>(target), &NearFieldTarget::ndefMessageRead,
this, &QNearFieldManagerPrivateImpl::handlerNdefMessageRead);
disconnect(target, &QNearFieldTarget::requestCompleted,
this, &QNearFieldManagerPrivateImpl::handlerRequestCompleted);
disconnect(target, &QNearFieldTarget::error,
this, &QNearFieldManagerPrivateImpl::handlerError);
m_idToTarget.remove(m_idToTarget.key(target));
}
struct VerifyRecord
{
QNdefFilter::Record filterRecord;
unsigned int count;
};
void QNearFieldManagerPrivateImpl::handlerNdefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id)
{
QNearFieldTarget *target = m_idToTarget.value(id);
//For message handlers without filters
for (int i = 0; i < ndefMessageHandlers.count(); i++) {
ndefMessageHandlers.at(i).second.invoke(ndefMessageHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target));
}
//For message handlers that specified a filter
for (int i = 0; i < ndefFilterHandlers.count(); ++i) {
bool matched = true;
QNdefFilter filter = ndefFilterHandlers.at(i).second.first;
QList<VerifyRecord> filterRecords;
for (int j = 0; j < filter.recordCount(); ++j) {
VerifyRecord vr;
vr.count = 0;
vr.filterRecord = filter.recordAt(j);
filterRecords.append(vr);
}
for (const QNdefRecord &record : message) {
for (int j = 0; matched && (j < filterRecords.count()); ++j) {
VerifyRecord &vr = filterRecords[j];
if (vr.filterRecord.typeNameFormat == record.typeNameFormat() &&
( vr.filterRecord.type == record.type() ||
vr.filterRecord.type.isEmpty()) ) {
++vr.count;
break;
} else {
if (filter.orderMatch()) {
if (vr.filterRecord.minimum <= vr.count &&
vr.count <= vr.filterRecord.maximum) {
continue;
} else {
matched = false;
}
}
}
}
}
for (int j = 0; matched && (j < filterRecords.count()); ++j) {
const VerifyRecord &vr = filterRecords.at(j);
if (vr.filterRecord.minimum <= vr.count && vr.count <= vr.filterRecord.maximum)
continue;
else
matched = false;
}
if (matched) {
ndefFilterHandlers.at(i).second.second.invoke(ndefFilterHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target));
}
}
}
void QNearFieldManagerPrivateImpl::handlerRequestCompleted(const QNearFieldTarget::RequestId &id)
{
m_idToTarget.remove(id);
}
void QNearFieldManagerPrivateImpl::handlerError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id)
{
Q_UNUSED(error);
m_idToTarget.remove(id);
}
bool QNearFieldManagerPrivateImpl::isAvailable() const
{
return AndroidNfc::isAvailable();
}
bool QNearFieldManagerPrivateImpl::isSupported() const
{
return AndroidNfc::isSupported();
}
bool QNearFieldManagerPrivateImpl::startTargetDetection()
{
if (m_detecting)
return false; // Already detecting targets
m_detecting = true;
updateReceiveState();
return true;
}
void QNearFieldManagerPrivateImpl::stopTargetDetection()
{
m_detecting = false;
updateReceiveState();
}
// FIXME This is supposed to be a platform registration. A message that
// matches the given NDEF filter should restart the current application.
// The implementation below only works as long as the current application
// is running. It is not a platform wide registration on Android.
int QNearFieldManagerPrivateImpl::registerNdefMessageHandler(QObject *object, const QMetaMethod &method)
{
ndefMessageHandlers.append(QPair<QPair<int, QObject *>, QMetaMethod>(QPair<int, QObject *>(m_handlerID, object), method));
updateReceiveState();
//Returns the handler ID and increments it afterwards
return m_handlerID++;
}
// FIXME see above
int QNearFieldManagerPrivateImpl::registerNdefMessageHandler(const QNdefFilter &filter,
QObject *object, const QMetaMethod &method)
{
//If no record is set in the filter, we ignore the filter
if (filter.recordCount()==0)
return registerNdefMessageHandler(object, method);
ndefFilterHandlers.append(QPair<QPair<int, QObject*>, QPair<QNdefFilter, QMetaMethod> >
(QPair<int, QObject*>(m_handlerID, object), QPair<QNdefFilter, QMetaMethod>(filter, method)));
updateReceiveState();
return m_handlerID++;
}
bool QNearFieldManagerPrivateImpl::unregisterNdefMessageHandler(int handlerId)
{
for (int i=0; i<ndefMessageHandlers.count(); ++i) {
if (ndefMessageHandlers.at(i).first.first == handlerId) {
ndefMessageHandlers.removeAt(i);
updateReceiveState();
return true;
}
}
for (int i=0; i<ndefFilterHandlers.count(); ++i) {
if (ndefFilterHandlers.at(i).first.first == handlerId) {
ndefFilterHandlers.removeAt(i);
updateReceiveState();
return true;
}
}
return false;
}
void QNearFieldManagerPrivateImpl::requestAccess(QNearFieldManager::TargetAccessModes accessModes)
{
Q_UNUSED(accessModes);
//Do nothing, because we dont have access modes for the target
}
void QNearFieldManagerPrivateImpl::releaseAccess(QNearFieldManager::TargetAccessModes accessModes)
{
Q_UNUSED(accessModes);
//Do nothing, because we dont have access modes for the target
}
void QNearFieldManagerPrivateImpl::newIntent(QAndroidJniObject intent)
{
// This function is called from different thread and is used to move intent to main thread.
QMetaObject::invokeMethod(this, "onTargetDiscovered", Qt::QueuedConnection, Q_ARG(QAndroidJniObject, intent));
}
QByteArray QNearFieldManagerPrivateImpl::getUid(const QAndroidJniObject &intent)
{
if (!intent.isValid())
return QByteArray();
QAndroidJniEnvironment env;
QAndroidJniObject tag = AndroidNfc::getTag(intent);
return getUidforTag(tag);
}
void QNearFieldManagerPrivateImpl::onTargetDiscovered(QAndroidJniObject intent)
{
// Only intents with a tag are relevant
if (!AndroidNfc::getTag(intent).isValid())
return;
// Getting UID
QByteArray uid = getUid(intent);
// Accepting all targets but only sending signal of requested types.
NearFieldTarget *&target = m_detectedTargets[uid];
if (target) {
target->setIntent(intent); // Updating existing target
} else {
target = new NearFieldTarget(intent, uid, this);
connect(target, &NearFieldTarget::targetDestroyed, this, &QNearFieldManagerPrivateImpl::onTargetDestroyed);
connect(target, &NearFieldTarget::targetLost, this, &QNearFieldManagerPrivateImpl::targetLost);
}
emit targetDetected(target);
}
void QNearFieldManagerPrivateImpl::onTargetDestroyed(const QByteArray &uid)
{
m_detectedTargets.remove(uid);
}
QByteArray QNearFieldManagerPrivateImpl::getUidforTag(const QAndroidJniObject &tag)
{
if (!tag.isValid())
return QByteArray();
QAndroidJniEnvironment env;
QAndroidJniObject tagId = tag.callObjectMethod("getId", "()[B");
QByteArray uid;
jsize len = env->GetArrayLength(tagId.object<jbyteArray>());
uid.resize(len);
env->GetByteArrayRegion(tagId.object<jbyteArray>(), 0, len, reinterpret_cast<jbyte*>(uid.data()));
return uid;
}
void QNearFieldManagerPrivateImpl::updateReceiveState()
{
if (m_detecting) {
AndroidNfc::registerListener(this);
} else {
if (ndefMessageHandlers.count() || ndefFilterHandlers.count()) {
AndroidNfc::registerListener(this);
} else {
AndroidNfc::unregisterListener(this);
}
}
}
QT_END_NAMESPACE