diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/android/android.pro | 1 | ||||
-rw-r--r-- | src/android/nfc/AndroidManifest.xml | 7 | ||||
-rw-r--r-- | src/android/nfc/bundledjar.pro | 3 | ||||
-rw-r--r-- | src/android/nfc/distributedjar.pro | 2 | ||||
-rw-r--r-- | src/android/nfc/nfc.pri | 13 | ||||
-rw-r--r-- | src/android/nfc/nfc.pro | 3 | ||||
-rw-r--r-- | src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java | 152 | ||||
-rw-r--r-- | src/nfc/android/androidjninfc.cpp | 185 | ||||
-rw-r--r-- | src/nfc/android/androidjninfc_p.h | 83 | ||||
-rw-r--r-- | src/nfc/android/androidmainnewintentlistener.cpp | 88 | ||||
-rw-r--r-- | src/nfc/android/androidmainnewintentlistener_p.h | 68 | ||||
-rw-r--r-- | src/nfc/nfc.pro | 37 | ||||
-rw-r--r-- | src/nfc/qllcpserver_android_p.cpp | 129 | ||||
-rw-r--r-- | src/nfc/qllcpserver_android_p.h | 83 | ||||
-rw-r--r-- | src/nfc/qllcpsocket_android_p.cpp | 326 | ||||
-rw-r--r-- | src/nfc/qllcpsocket_android_p.h | 125 | ||||
-rw-r--r-- | src/nfc/qnearfieldmanager.cpp | 2 | ||||
-rw-r--r-- | src/nfc/qnearfieldmanager_android.cpp | 299 | ||||
-rw-r--r-- | src/nfc/qnearfieldmanager_android_p.h | 103 | ||||
-rw-r--r-- | src/nfc/qnearfieldtarget_android.cpp | 499 | ||||
-rw-r--r-- | src/nfc/qnearfieldtarget_android_p.h | 111 |
21 files changed, 2315 insertions, 4 deletions
diff --git a/src/android/android.pro b/src/android/android.pro index 07feb78f..f8f5c05e 100644 --- a/src/android/android.pro +++ b/src/android/android.pro @@ -1,2 +1,3 @@ TEMPLATE = subdirs qtHaveModule(bluetooth): SUBDIRS += bluetooth +qtHaveModule(nfc): SUBDIRS += nfc diff --git a/src/android/nfc/AndroidManifest.xml b/src/android/nfc/AndroidManifest.xml new file mode 100644 index 00000000..2ba062ae --- /dev/null +++ b/src/android/nfc/AndroidManifest.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.qtproject.qt5.android.nfc" + android:versionCode="1" + android:versionName="1.0"> + <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> +</manifest> diff --git a/src/android/nfc/bundledjar.pro b/src/android/nfc/bundledjar.pro new file mode 100644 index 00000000..37f7fc82 --- /dev/null +++ b/src/android/nfc/bundledjar.pro @@ -0,0 +1,3 @@ +TARGET = QtNfc-bundled +CONFIG += bundled_jar_file +include(nfc.pri) diff --git a/src/android/nfc/distributedjar.pro b/src/android/nfc/distributedjar.pro new file mode 100644 index 00000000..ed65c50a --- /dev/null +++ b/src/android/nfc/distributedjar.pro @@ -0,0 +1,2 @@ +TARGET = QtNfc +include(nfc.pri) diff --git a/src/android/nfc/nfc.pri b/src/android/nfc/nfc.pri new file mode 100644 index 00000000..65f197ca --- /dev/null +++ b/src/android/nfc/nfc.pri @@ -0,0 +1,13 @@ +CONFIG += java +DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar +API_VERSION = android-10 + +PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/nfc + +JAVACLASSPATH += $$PWD/src/ +JAVASOURCES += \ + $$PWD/src/org/qtproject/qt5/android/nfc/QtNfc.java + +# install +target.path = $$[QT_INSTALL_PREFIX]/jar +INSTALLS += target diff --git a/src/android/nfc/nfc.pro b/src/android/nfc/nfc.pro new file mode 100644 index 00000000..70373fe1 --- /dev/null +++ b/src/android/nfc/nfc.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS += bundledjar.pro distributedjar.pro + diff --git a/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java new file mode 100644 index 00000000..b838fac1 --- /dev/null +++ b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java @@ -0,0 +1,152 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android.nfc; + +import java.lang.Thread; +import java.lang.Runnable; + +import android.os.Parcelable; +import android.os.Looper; +import android.content.Context; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.content.IntentFilter.MalformedMimeTypeException; +import android.os.Bundle; +//import android.util.Log; +import android.content.BroadcastReceiver; + +public class QtNfc +{ + /* static final QtNfc m_nfc = new QtNfc(); */ + static private final String TAG = "QtNfc"; + static public NfcAdapter m_adapter = null; + static public PendingIntent m_pendingIntent = null; + static public IntentFilter[] m_filters; + static public Activity m_activity; + + static public void setActivity(Activity activity, Object activityDelegate) + { + //Log.d(TAG, "setActivity " + activity); + m_activity = activity; + m_adapter = NfcAdapter.getDefaultAdapter(m_activity); + if (m_adapter == null) { + //Log.e(TAG, "No NFC available"); + return; + } + m_pendingIntent = PendingIntent.getActivity( + m_activity, + 0, + new Intent(m_activity, m_activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), + 0); + + //Log.d(TAG, "Pending intent:" + m_pendingIntent); + + IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); + + m_filters = new IntentFilter[]{ + filter + }; + + try { + filter.addDataType("*/*"); + } catch(MalformedMimeTypeException e) { + throw new RuntimeException("Fail", e); + } + + //Log.d(TAG, "Thread:" + Thread.currentThread().getId()); + } + + static public void start() + { + if (m_adapter == null) return; + m_activity.runOnUiThread(new Runnable() { + public void run() { + //Log.d(TAG, "Enabling NFC"); + IntentFilter[] filters = new IntentFilter[2]; + filters[0] = new IntentFilter(); + filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED); + filters[0].addCategory(Intent.CATEGORY_DEFAULT); + try { + filters[0].addDataType("*/*"); + } catch (MalformedMimeTypeException e) { + throw new RuntimeException("Check your mime type."); + } + // some tags will report as tech, even if they are ndef formated/formatable. + filters[1] = new IntentFilter(); + filters[1].addAction(NfcAdapter.ACTION_TECH_DISCOVERED); + String[][] techList = new String[][]{ + {"android.nfc.tech.Ndef"}, + {"android.nfc.tech.NdefFormatable"} + }; + try { + m_adapter.enableForegroundDispatch(m_activity, m_pendingIntent, filters, techList); + } catch(IllegalStateException e) { + throw new RuntimeException("Fail", e); + } + } + }); + } + + static public void stop() + { + if (m_adapter == null) return; + m_activity.runOnUiThread(new Runnable() { + public void run() { + //Log.d(TAG, "Disabling NFC"); + m_adapter.disableForegroundDispatch(m_activity); + } + }); + } + + static public boolean isAvailable() + { + m_adapter = NfcAdapter.getDefaultAdapter(m_activity); + if (m_adapter == null) { + //Log.e(TAG, "No NFC available (Adapter is null)"); + return false; + } + return m_adapter.isEnabled(); + } +} diff --git a/src/nfc/android/androidjninfc.cpp b/src/nfc/android/androidjninfc.cpp new file mode 100644 index 00000000..424db381 --- /dev/null +++ b/src/nfc/android/androidjninfc.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidjninfc_p.h" + +#include <android/log.h> + +#include "androidmainnewintentlistener_p.h" + +#include "qglobal.h" +#include "qbytearray.h" +#include "qdebug.h" + +static JavaVM *javaVM = 0; +static jclass nfcClass; + +static jmethodID startDiscoveryId; +static jmethodID stopDiscoveryId; +static jmethodID isAvailableId; + +static AndroidNfc::MainNfcNewIntentListener mainListener; + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +AttachedJNIEnv::AttachedJNIEnv() +{ + Q_ASSERT_X(javaVM != 0, "constructor", "javaVM pointer is null"); + attached = false; + if (javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) < 0) { + if (javaVM->AttachCurrentThread(&jniEnv, NULL) < 0) { + __android_log_print(ANDROID_LOG_ERROR, "Qt", "AttachCurrentThread failed"); + jniEnv = 0; + return; + } + attached = true; + } +} + +AttachedJNIEnv::~AttachedJNIEnv() +{ + if (attached) + javaVM->DetachCurrentThread(); +} + +bool startDiscovery() +{ + //__android_log_print(ANDROID_LOG_INFO, "Qt", "QtNfc::startDiscovery()"); + AttachedJNIEnv aenv; + if (!aenv.jniEnv) + return false; + + aenv.jniEnv->CallStaticObjectMethod(nfcClass, startDiscoveryId); + return true; +} + +bool isAvailable() +{ + //__android_log_print(ANDROID_LOG_INFO, "Qt", "QtNfc::isAvailable()"); + AttachedJNIEnv aenv; + if (!aenv.jniEnv) + return false; + + return aenv.jniEnv->CallStaticBooleanMethod(nfcClass, isAvailableId); +} + +bool stopDiscovery() +{ + AttachedJNIEnv aenv; + if (!aenv.jniEnv) + return false; + + aenv.jniEnv->CallStaticObjectMethod(nfcClass, stopDiscoveryId); + return true; +} + +bool registerListener(AndroidNfcListenerInterface *listener) +{ + return mainListener.registerListener(listener); +} + +bool unregisterListener(AndroidNfcListenerInterface *listener) +{ + return mainListener.unregisterListener(listener); +} + +jobject getTag(JNIEnv *env, jobject intent) +{ + jclass intentClass = env->GetObjectClass(intent); + Q_ASSERT_X(intentClass != 0, "getTag", "could not get Intent class"); + + jmethodID getParcelableExtraMID = env->GetMethodID(intentClass, + "getParcelableExtra", + "(Ljava/lang/String;)Landroid/os/Parcelable;"); + Q_ASSERT_X(getParcelableExtraMID != 0, "getTag", "could not get method ID for getParcelableExtra()"); + + jclass nfcAdapterClass = env->FindClass("android/nfc/NfcAdapter"); + Q_ASSERT_X(nfcAdapterClass != 0, "getTag", "could not find NfcAdapter class"); + + jfieldID extraTagFID = env->GetStaticFieldID(nfcAdapterClass, "EXTRA_TAG", "Ljava/lang/String;"); + Q_ASSERT_X(extraTagFID != 0, "getTag", "could not get field ID for EXTRA_TAG"); + + jobject extraTag = env->GetStaticObjectField(nfcAdapterClass, extraTagFID); + Q_ASSERT_X(extraTag != 0, "getTag", "could not get EXTRA_TAG"); + + jobject tag = env->CallObjectMethod(intent, getParcelableExtraMID, extraTag); + Q_ASSERT_X(tag != 0, "getTag", "could not get Tag object"); + + return tag; +} + +QT_END_ANDROIDNFC_NAMESPACE + +static const char logTag[] = "Qt"; +static const char classErrorMsg[] = "Can't find class \"%s\""; +static const char methodErrorMsg[] = "Can't find method \"%s%s\""; + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ + clazz = env->FindClass(CLASS_NAME); \ + if (!clazz) { \ + return JNI_FALSE; \ + } + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + return JNI_FALSE; \ + } + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) +{ + //__android_log_print(ANDROID_LOG_INFO, "Qt", "JNI_OnLoad for QtNfc"); + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + + jclass clazz; + FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/nfc/QtNfc"); + nfcClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + + GET_AND_CHECK_STATIC_METHOD(startDiscoveryId, nfcClass, "start", "()V"); + GET_AND_CHECK_STATIC_METHOD(stopDiscoveryId, nfcClass, "stop", "()V"); + GET_AND_CHECK_STATIC_METHOD(isAvailableId, nfcClass, "isAvailable", "()Z"); + javaVM = vm; + + return JNI_VERSION_1_6; +} diff --git a/src/nfc/android/androidjninfc_p.h b/src/nfc/android/androidjninfc_p.h new file mode 100644 index 00000000..dbd2867a --- /dev/null +++ b/src/nfc/android/androidjninfc_p.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDJNINFC_H +#define ANDROIDJNINFC_H + +#include "qglobal.h" + +#include <jni.h> + +#define QT_USE_ANDROIDNFC_NAMESPACE using namespace ::AndroidNfc; +#define QT_BEGIN_ANDROIDNFC_NAMESPACE namespace AndroidNfc { +#define QT_END_ANDROIDNFC_NAMESPACE } + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +class AndroidNfcListenerInterface +{ +public: + virtual ~AndroidNfcListenerInterface(){} + virtual void newIntent(jobject intent) = 0; +}; + +class AttachedJNIEnv +{ +public: + AttachedJNIEnv(); + virtual ~AttachedJNIEnv(); + bool attached; + JNIEnv *jniEnv; + +private: + Q_DISABLE_COPY(AttachedJNIEnv) +}; + +bool startDiscovery(); +bool stopDiscovery(); +bool isAvailable(); +bool registerListener(AndroidNfcListenerInterface *listener); +bool unregisterListener(AndroidNfcListenerInterface *listener); +jobject getTag(JNIEnv *env, jobject intent); + +QT_END_ANDROIDNFC_NAMESPACE + +#endif diff --git a/src/nfc/android/androidmainnewintentlistener.cpp b/src/nfc/android/androidmainnewintentlistener.cpp new file mode 100644 index 00000000..d27dac18 --- /dev/null +++ b/src/nfc/android/androidmainnewintentlistener.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2015 BasysKom GmbH +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidmainnewintentlistener_p.h" + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +MainNfcNewIntentListener::MainNfcNewIntentListener() + : listeners(), listenersLock() +{ + QtAndroidPrivate::registerNewIntentListener(this); +} + +MainNfcNewIntentListener::~MainNfcNewIntentListener() +{ + QtAndroidPrivate::unregisterNewIntentListener(this); +} + +bool MainNfcNewIntentListener::handleNewIntent(JNIEnv *env, jobject intent) +{ + AndroidNfc::AttachedJNIEnv aenv; + listenersLock.lockForRead(); + foreach (AndroidNfc::AndroidNfcListenerInterface *listener, listeners) { + // Making new global reference for each listener. + // Listeners must release reference when it is not used anymore. + jobject newIntentRef = env->NewGlobalRef(intent); + listener->newIntent(newIntentRef); + } + listenersLock.unlock(); + return true; +} + +bool MainNfcNewIntentListener::registerListener(AndroidNfcListenerInterface *listener) +{ + listenersLock.lockForWrite(); + if (!listeners.contains(listener)) + listeners.push_back(listener); + listenersLock.unlock(); + return true; +} + +bool MainNfcNewIntentListener::unregisterListener(AndroidNfcListenerInterface *listener) +{ + listenersLock.lockForWrite(); + listeners.removeOne(listener); + listenersLock.unlock(); + return true; +} + +QT_END_ANDROIDNFC_NAMESPACE diff --git a/src/nfc/android/androidmainnewintentlistener_p.h b/src/nfc/android/androidmainnewintentlistener_p.h new file mode 100644 index 00000000..34541214 --- /dev/null +++ b/src/nfc/android/androidmainnewintentlistener_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 BasysKom GmbH +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDMAINNEWINTENTLISTENER_P_H_ +#define ANDROIDMAINNEWINTENTLISTENER_P_H_ + +#include <QtCore/private/qjnihelpers_p.h> + +#include "qlist.h" +#include "qreadwritelock.h" +#include "androidjninfc_p.h" + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +class MainNfcNewIntentListener : public QtAndroidPrivate::NewIntentListener +{ +public: + MainNfcNewIntentListener(); + ~MainNfcNewIntentListener(); + bool handleNewIntent(JNIEnv *env, jobject intent); + bool registerListener(AndroidNfcListenerInterface *listener); + bool unregisterListener(AndroidNfcListenerInterface *listener); +protected: + QList<AndroidNfc::AndroidNfcListenerInterface*> listeners; + QReadWriteLock listenersLock; +}; + +QT_END_ANDROIDNFC_NAMESPACE + +#endif /* ANDROIDMAINNEWINTENTLISTENER_P_H_ */ diff --git a/src/nfc/nfc.pro b/src/nfc/nfc.pro index 7c6b6ff7..9ef57dad 100644 --- a/src/nfc/nfc.pro +++ b/src/nfc/nfc.pro @@ -98,9 +98,8 @@ CONFIG(blackberry) { qnearfieldsharemanagerimpl_p.h \ qnearfieldsharetargetimpl_p.h } -} -linux:qtHaveModule(dbus) { +} else:linux:!android:qtHaveModule(dbus) { NFC_BACKEND_AVAILABLE = yes QT += dbus @@ -123,9 +122,8 @@ linux:qtHaveModule(dbus) { qnearfieldmanager_neard.cpp include(neard/neard.pri) -} -simulator { +} else:simulator { NFC_BACKEND_AVAILABLE = yes QT *= gui @@ -146,6 +144,37 @@ simulator { qllcpserver_simulator_p.cpp \ qnearfieldsharemanagerimpl_p.cpp \ qnearfieldsharetargetimpl_p.cpp +} else:android:!android-no-sdk { + NFC_BACKEND_AVAILABLE = yes + ANDROID_PERMISSIONS = \ + android.permission.NFC + ANDROID_BUNDLED_JAR_DEPENDENCIES = \ + jar/QtNfc-bundled.jar:org.qtproject.qt5.android.nfc.QtNfc + ANDROID_JAR_DEPENDENCIES = \ + jar/QtNfc.jar:org.qtproject.qt5.android.nfc.QtNfc + DEFINES += ANDROID_NFC + QT += core-private + + PRIVATE_HEADERS += \ + qllcpserver_android_p.h \ + qllcpsocket_android_p.h \ + android/androidjninfc_p.h \ + qnearfieldmanager_android_p.h \ + qnearfieldtarget_android_p.h \ + qnearfieldsharemanagerimpl_p.h \ + qnearfieldsharetargetimpl_p.h \ + android/androidmainnewintentlistener_p.h + + + SOURCES += \ + qllcpserver_android_p.cpp \ + qllcpsocket_android_p.cpp \ + android/androidjninfc.cpp \ + qnearfieldmanager_android.cpp \ + qnearfieldtarget_android.cpp \ + qnearfieldsharemanagerimpl_p.cpp \ + qnearfieldsharetargetimpl_p.cpp \ + android/androidmainnewintentlistener.cpp } isEmpty(NFC_BACKEND_AVAILABLE) { diff --git a/src/nfc/qllcpserver_android_p.cpp b/src/nfc/qllcpserver_android_p.cpp new file mode 100644 index 00000000..9a8f7dd0 --- /dev/null +++ b/src/nfc/qllcpserver_android_p.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qllcpserver_android_p.h" +//#include "qnx/qnxnfcmanager_p.h" + +QT_BEGIN_NAMESPACE + +QLlcpServerPrivate::QLlcpServerPrivate(QLlcpServer *q) + : q_ptr(q), m_llcpSocket(0), m_connected(false) +{ +} + +bool QLlcpServerPrivate::listen(const QString &/*serviceUri*/) +{ + /*//The server is already listening + if (isListening()) + return false; + + nfc_result_t result = nfc_llcp_register_connection_listener(NFC_LLCP_SERVER, 0, serviceUri.toStdString().c_str(), &m_conListener); + m_connected = true; + if (result == NFC_RESULT_SUCCESS) { + m_serviceUri = serviceUri; + qQNXNFCDebug() << "LLCP server registered" << serviceUri; + } else { + qWarning() << Q_FUNC_INFO << "Could not register for llcp connection listener"; + return false; + } + QNXNFCManager::instance()->registerLLCPConnection(m_conListener, this);*/ + return true; +} + +bool QLlcpServerPrivate::isListening() const +{ + return m_connected; +} + +void QLlcpServerPrivate::close() +{ + /*nfc_llcp_unregister_connection_listener(m_conListener); + QNXNFCManager::instance()->unregisterLLCPConnection(m_conListener); + m_serviceUri = QString(); + m_connected = false;*/ +} + +QString QLlcpServerPrivate::serviceUri() const +{ + return m_serviceUri; +} + +quint8 QLlcpServerPrivate::serverPort() const +{ + /*unsigned int sap; + if (nfc_llcp_get_local_sap(m_target, &sap) == NFC_RESULT_SUCCESS) { + return sap; + }*/ + return -1; +} + +bool QLlcpServerPrivate::hasPendingConnections() const +{ + return m_llcpSocket != 0; +} + +QLlcpSocket *QLlcpServerPrivate::nextPendingConnection() +{ + /*QLlcpSocket *socket = m_llcpSocket; + m_llcpSocket = 0; + return socket;*/ + return 0; +} + +QLlcpSocket::SocketError QLlcpServerPrivate::serverError() const +{ + return QLlcpSocket::UnknownSocketError; +} + +/*void QLlcpServerPrivate::connected(nfc_target_t *target) +{ + m_target = target; + if (m_llcpSocket != 0) { + qWarning() << Q_FUNC_INFO << "LLCP socket not cloesed properly"; + return; + } + m_llcpSocket = new QLlcpSocket(); + m_llcpSocket->bind(serverPort()); +}*/ + +QT_END_NAMESPACE + + diff --git a/src/nfc/qllcpserver_android_p.h b/src/nfc/qllcpserver_android_p.h new file mode 100644 index 00000000..24e7854e --- /dev/null +++ b/src/nfc/qllcpserver_android_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLLCPSERVER_ANDROID_P_H +#define QLLCPSERVER_ANDROID_P_H + +#include "qllcpserver_p.h" +//#include "nfc/nfc.h" + +QT_BEGIN_NAMESPACE + +class QLlcpServerPrivate : public QObject +{ + Q_OBJECT +public: + QLlcpServerPrivate(QLlcpServer *q); + + bool listen(const QString &serviceUri); + bool isListening() const; + + void close(); + + QString serviceUri() const; + quint8 serverPort() const; + + bool hasPendingConnections() const; + QLlcpSocket *nextPendingConnection(); + + QLlcpSocket::SocketError serverError() const; + + //Q_INVOKABLE void connected(nfc_target_t *); + +private: + QLlcpServer *q_ptr; + QLlcpSocket *m_llcpSocket; + //We can not use m_conListener for the connection state + bool m_connected; + //nfc_llcp_connection_listener_t m_conListener; + QString m_serviceUri; + //nfc_target_t *m_target; +}; + +QT_END_NAMESPACE + +#endif // QLLCPSERVER_ANDROID_P_H diff --git a/src/nfc/qllcpsocket_android_p.cpp b/src/nfc/qllcpsocket_android_p.cpp new file mode 100644 index 00000000..db1bb972 --- /dev/null +++ b/src/nfc/qllcpsocket_android_p.cpp @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qllcpsocket_android_p.h" +#include <unistd.h> + +QT_BEGIN_NAMESPACE + +QLlcpSocketPrivate::QLlcpSocketPrivate(QLlcpSocket *q) + : q_ptr(q), m_state(QLlcpSocket::UnconnectedState), m_server(false) +{ +} + +QLlcpSocketPrivate::~QLlcpSocketPrivate() +{ + disconnectFromService(); +} + +void QLlcpSocketPrivate::connectToService(QNearFieldTarget *target, const QString &serviceUri) +{ + Q_UNUSED(target) + Q_UNUSED(serviceUri) + /*if (m_state != QLlcpSocket::UnconnectedState) { + qWarning() << Q_FUNC_INFO << "socket is already connected"; + return; + } + + m_state = QLlcpSocket::ConnectingState; + if (nfc_llcp_register_connection_listener(NFC_LLCP_CLIENT, 0, serviceUri.toLocal8Bit().constData(), + &m_conListener) != NFC_RESULT_SUCCESS) { + qWarning() << Q_FUNC_INFO << "could not register for connection listener"; + return; + } + + QNXNFCManager::instance()->registerLLCPConnection(m_conListener, this); + + qQNXNFCDebug() << "Connecting client socket" << serviceUri << m_conListener; + connect(QNXNFCManager::instance(), SIGNAL(llcpDisconnected()), this, SLOT(disconnectFromService()));*/ +} + +void QLlcpSocketPrivate::disconnectFromService() +{ + /*Q_Q(QLlcpSocket); + QNXNFCManager::instance()->unregisterTargetLost(this); + qQNXNFCDebug() << "Shutting down LLCP socket"; + if (!m_server && nfc_llcp_unregister_connection_listener(m_conListener) != NFC_RESULT_SUCCESS) { + qWarning() << Q_FUNC_INFO << "Error when trying to close LLCP socket"; + } + QNXNFCManager::instance()->unregisterLLCPConnection(m_conListener); + disconnect(QNXNFCManager::instance(), SIGNAL(llcpDisconnected()), this, SLOT(disconnectFromService())); + + q->disconnected(); + m_conListener = 0; + m_state = QLlcpSocket::UnconnectedState;*/ +} + +bool QLlcpSocketPrivate::bind(quint8 port) +{ + Q_UNUSED(port); + + /*m_state = QLlcpSocket::ConnectedState; + m_server = true; + connect(QNXNFCManager::instance(), SIGNAL(llcpDisconnected()), this, SLOT(disconnectFromService())); + connected(QNXNFCManager::instance()->getLastTarget());*/ + + return true; +} + +bool QLlcpSocketPrivate::hasPendingDatagrams() const +{ + return !m_receivedDatagrams.isEmpty(); +} + +qint64 QLlcpSocketPrivate::pendingDatagramSize() const +{ + if (m_receivedDatagrams.isEmpty()) + return -1; + + return m_receivedDatagrams.first().length(); +} + +qint64 QLlcpSocketPrivate::writeDatagram(const char *data, qint64 size) +{ + if (m_state == QLlcpSocket::ConnectedState) + return writeData(data, size); + + return -1; +} + +qint64 QLlcpSocketPrivate::writeDatagram(const QByteArray &datagram) +{ + return writeDatagram(datagram.constData(), datagram.size()); +} + +qint64 QLlcpSocketPrivate::readDatagram(char *data, qint64 maxSize, + QNearFieldTarget **target, quint8 *port) +{ + Q_UNUSED(target); + Q_UNUSED(port); + + if (m_state == QLlcpSocket::ConnectedState) + return readData(data, maxSize); + + return -1; +} + +qint64 QLlcpSocketPrivate::writeDatagram(const char *data, qint64 size, + QNearFieldTarget *target, quint8 port) +{ + Q_UNUSED(target); + Q_UNUSED(port); + + return writeDatagram(data, size); +} + +qint64 QLlcpSocketPrivate::writeDatagram(const QByteArray &datagram, + QNearFieldTarget *target, quint8 port) +{ + Q_UNUSED(datagram); + Q_UNUSED(target); + Q_UNUSED(port); + + return writeDatagram(datagram.constData(), datagram.size()-1); +} + +QLlcpSocket::SocketError QLlcpSocketPrivate::error() const +{ + return QLlcpSocket::UnknownSocketError; +} + +QLlcpSocket::SocketState QLlcpSocketPrivate::state() const +{ + return m_state; +} + +qint64 QLlcpSocketPrivate::readData(char *data, qint64 maxlen) +{ + Q_UNUSED(data); + Q_UNUSED(maxlen); + if (m_receivedDatagrams.isEmpty()) + return 0; + + /*const QByteArray datagram = m_receivedDatagrams.takeFirst(); + qint64 size = qMin(maxlen, qint64(datagram.length())); + memcpy(data, datagram.constData(), size); + return size;*/ + return 0; +} + +qint64 QLlcpSocketPrivate::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + /*if (socketState != Idle) { + m_writeQueue.append(QByteArray(data, len)); + return len; + } else { + socketState = Writing; + qQNXNFCDebug() << "LLCP write"; + nfc_result_t res = nfc_llcp_write(m_target, (uchar_t*)data, (size_t)len); + if (res == NFC_RESULT_SUCCESS) { + return len; + } else { + qWarning() << Q_FUNC_INFO << "Error writing to LLCP socket. Error" << res; + enteringIdle(); + return -1; + } + }*/ + return -1; +} + +qint64 QLlcpSocketPrivate::bytesAvailable() const +{ + /*qint64 available = 0; + foreach (const QByteArray &datagram, m_receivedDatagrams) + available += datagram.length(); + + return available;*/ + return 0; +} + +bool QLlcpSocketPrivate::canReadLine() const +{ + /*foreach (const QByteArray &datagram, m_receivedDatagrams) { + if (datagram.contains('\n')) + return true; + }*/ + + return false; +} + +bool QLlcpSocketPrivate::waitForReadyRead(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +bool QLlcpSocketPrivate::waitForBytesWritten(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +bool QLlcpSocketPrivate::waitForConnected(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +bool QLlcpSocketPrivate::waitForDisconnected(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +/*void QLlcpSocketPrivate::connected(nfc_target_t *target) +{ + Q_Q(QLlcpSocket); + m_target = target; + + m_state = QLlcpSocket::ConnectedState; + emit q->connected(); + qQNXNFCDebug() << "Socket connected"; + + unsigned int targetId; + nfc_get_target_connection_id(target, &targetId); + QNXNFCManager::instance()->requestTargetLost(this, targetId); + enteringIdle(); +}*/ + +void QLlcpSocketPrivate::targetLost() +{ + disconnectFromService(); + //qQNXNFCDebug() << "LLCP target lost...socket disconnected"; +} + +void QLlcpSocketPrivate::dataRead(QByteArray& data) +{ + Q_UNUSED(data); + /*Q_Q(QLlcpSocket); + if (!data.isEmpty()) { + m_receivedDatagrams.append(data); + emit q->readyRead(); + } + socketState = Idle; + enteringIdle();*/ +} + +void QLlcpSocketPrivate::dataWritten() +{ + //enteringIdle(); +} + +void QLlcpSocketPrivate::read() +{ + /*if (socketState != Idle) { + qQNXNFCDebug() << "Trying to read but socket state not in idle..abort"; + return; + } + socketState = Reading; + qQNXNFCDebug() << "LLCP read"; + if (nfc_llcp_read(m_target, 128) != NFC_RESULT_SUCCESS) { + qWarning() << Q_FUNC_INFO << "Could not register for reading"; + socketState = Idle; + }*/ +} + +void QLlcpSocketPrivate::enteringIdle() +{ + /*qQNXNFCDebug() << "entering idle; Socket state:" << socketState; + socketState = Idle; + if (m_state == QLlcpSocket::ConnectedState) { + if (m_writeQueue.isEmpty()) { + qQNXNFCDebug() << "Write queue empty, reading in 50ms"; + QTimer::singleShot(50, this, SLOT(read())); + } else { + qQNXNFCDebug() << "Write first package in queue"; + writeDatagram(m_writeQueue.takeFirst()); + } + }*/ +} + +QT_END_NAMESPACE + diff --git a/src/nfc/qllcpsocket_android_p.h b/src/nfc/qllcpsocket_android_p.h new file mode 100644 index 00000000..afbc573e --- /dev/null +++ b/src/nfc/qllcpsocket_android_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLLCPSOCKET_ANDROID_P_H +#define QLLCPSOCKET_ANDROID_P_H + +#include "qllcpsocket_p.h" + +//#include "qnearfieldtarget_ANDROID_p.h" + +QT_BEGIN_NAMESPACE + +class QLlcpSocketPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(QLlcpSocket) + +public: + QLlcpSocketPrivate(QLlcpSocket *q); + + ~QLlcpSocketPrivate(); + + void connectToService(QNearFieldTarget *target, const QString &serviceUri); + + bool bind(quint8 port); + + bool hasPendingDatagrams() const; + qint64 pendingDatagramSize() const; + + qint64 writeDatagram(const char *data, qint64 size); + qint64 writeDatagram(const QByteArray &datagram); + + qint64 readDatagram(char *data, qint64 maxSize, + QNearFieldTarget **target = 0, quint8 *port = 0); + qint64 writeDatagram(const char *data, qint64 size, + QNearFieldTarget *target, quint8 port); + qint64 writeDatagram(const QByteArray &datagram, QNearFieldTarget *target, quint8 port); + + QLlcpSocket::SocketError error() const; + QLlcpSocket::SocketState state() const; + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + + qint64 bytesAvailable() const; + bool canReadLine() const; + + bool waitForReadyRead(int msecs); + bool waitForBytesWritten(int msecs); + bool waitForConnected(int msecs); + bool waitForDisconnected(int msecs); + + //Q_INVOKABLE void connected(nfc_target_t *); + Q_INVOKABLE void targetLost(); + + void dataRead(QByteArray&); + void dataWritten(); + +public Q_SLOTS: + void disconnectFromService(); + +private: + QLlcpSocket *q_ptr; + unsigned int m_sap; + //nfc_llcp_connection_listener_t m_conListener; + //NearFieldTarget *m_target; + //nfc_target_t *m_target; + + QLlcpSocket::SocketState m_state; + + QList<QByteArray> m_receivedDatagrams; + QList<QByteArray> m_writeQueue; + + bool m_server; + + enum llcpState { + Idle, Reading, Writing + } socketState; + +private Q_SLOTS: + void read(); + void enteringIdle(); +}; + +QT_END_NAMESPACE + +#endif // QLLCPSOCKET_ANDROID_P_H diff --git a/src/nfc/qnearfieldmanager.cpp b/src/nfc/qnearfieldmanager.cpp index 7aaf0878..50bee6b1 100644 --- a/src/nfc/qnearfieldmanager.cpp +++ b/src/nfc/qnearfieldmanager.cpp @@ -40,6 +40,8 @@ #include "qnearfieldmanager_qnx_p.h" #elif defined(NEARD_NFC) #include "qnearfieldmanager_neard_p.h" +#elif defined(ANDROID_NFC) +#include "qnearfieldmanager_android_p.h" #else #include "qnearfieldmanagerimpl_p.h" #endif diff --git a/src/nfc/qnearfieldmanager_android.cpp b/src/nfc/qnearfieldmanager_android.cpp new file mode 100644 index 00000000..bcc90ecf --- /dev/null +++ b/src/nfc/qnearfieldmanager_android.cpp @@ -0,0 +1,299 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.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 <QtCore/QMetaType> +#include <QtCore/QMetaMethod> + +QT_BEGIN_NAMESPACE + +QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl() : + m_detecting(false), m_handlerID(0) +{ + qRegisterMetaType<jobject>("jobject"); + qRegisterMetaType<QNdefMessage>("QNdefMessage"); + connect(this, SIGNAL(targetDetected(QNearFieldTarget*)), this, SLOT(handlerTargetDetected(QNearFieldTarget*))); + connect(this, SIGNAL(targetLost(QNearFieldTarget*)), this, SLOT(handlerTargetLost(QNearFieldTarget*))); +} + +QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl() +{ +} + +void QNearFieldManagerPrivateImpl::handlerTargetDetected(QNearFieldTarget *target) +{ + if (ndefMessageHandlers.count() == 0 && ndefFilterHandlers.count() == 0) // if no handler is registered + return; + if (target->hasNdefMessage()) { + connect(target, SIGNAL(ndefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerNdefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &))); + connect(target, SIGNAL(requestCompleted(const QNearFieldTarget::RequestId &)), + this, SLOT(handlerRequestCompleted(const QNearFieldTarget::RequestId &))); + connect(target, SIGNAL(error(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerError(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &))); + + QNearFieldTarget::RequestId id = target->readNdefMessages(); + m_idToTarget.insert(id, target); + } +} + +void QNearFieldManagerPrivateImpl::handlerTargetLost(QNearFieldTarget *target) +{ + disconnect(target, SIGNAL(ndefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerNdefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &))); + disconnect(target, SIGNAL(requestCompleted(const QNearFieldTarget::RequestId &)), + this, SLOT(handlerRequestCompleted(const QNearFieldTarget::RequestId &))); + disconnect(target, SIGNAL(error(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerError(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &))); + m_idToTarget.remove(m_idToTarget.key(target)); +} + +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) { + QNdefFilter filter = ndefFilterHandlers.at(i).second.first; + if (filter.recordCount() > message.count()) + continue; + + int j; + for (j = 0; j < filter.recordCount();) { + if (message.at(j).typeNameFormat() != filter.recordAt(j).typeNameFormat + || message.at(j).type() != filter.recordAt(j).type ) { + break; + } + ++j; + } + if (j == filter.recordCount()) + 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::startTargetDetection() +{ + if (m_detecting) + return false; // Already detecting targets + + m_detecting = true; + updateReceiveState(); + return true; +} + +void QNearFieldManagerPrivateImpl::stopTargetDetection() +{ + m_detecting = false; + updateReceiveState(); +} + +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++; +} + +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(jobject 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(jobject, intent)); +} + +QByteArray QNearFieldManagerPrivateImpl::getUid(jobject intent) +{ + if (intent == 0) + return QByteArray(); + + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + jobject tag = AndroidNfc::getTag(env, intent); + + jclass tagClass = env->GetObjectClass(tag); + Q_ASSERT_X(tagClass != 0, "getUid", "could not get Tag class"); + + jmethodID getIdMID = env->GetMethodID(tagClass, "getId", "()[B"); + Q_ASSERT_X(getIdMID != 0, "getUid", "could not get method ID for getId()"); + + jbyteArray tagId = reinterpret_cast<jbyteArray>(env->CallObjectMethod(tag, getIdMID)); + Q_ASSERT_X(tagId != 0, "getUid", "getId() returned null object"); + + QByteArray uid; + jsize len = env->GetArrayLength(tagId); + uid.resize(len); + env->GetByteArrayRegion(tagId, 0, len, reinterpret_cast<jbyte*>(uid.data())); + + return uid; +} + +void QNearFieldManagerPrivateImpl::onTargetDiscovered(jobject intent) +{ + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + Q_ASSERT_X(env != 0, "onTargetDiscovered", "env pointer is null"); + + // Getting tag object and UID + jobject tag = AndroidNfc::getTag(env, intent); + QByteArray uid = getUid(env, tag); + + // Accepting all targest 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, SIGNAL(targetDestroyed(QByteArray)), this, SLOT(onTargetDestroyed(QByteArray))); + connect(target, SIGNAL(targetLost(QNearFieldTarget*)), this, SIGNAL(targetLost(QNearFieldTarget*))); + } + emit targetDetected(target); +} + +void QNearFieldManagerPrivateImpl::onTargetDestroyed(const QByteArray &uid) +{ + m_detectedTargets.remove(uid); +} + +QByteArray QNearFieldManagerPrivateImpl::getUid(JNIEnv *env, jobject tag) +{ + jclass tagClass = env->GetObjectClass(tag); + jmethodID getIdMID = env->GetMethodID(tagClass, "getId", "()[B"); + jbyteArray tagId = reinterpret_cast<jbyteArray>(env->CallObjectMethod(tag, getIdMID)); + QByteArray uid; + jsize len = env->GetArrayLength(tagId); + uid.resize(len); + env->GetByteArrayRegion(tagId, 0, len, reinterpret_cast<jbyte*>(uid.data())); + return uid; +} + +void QNearFieldManagerPrivateImpl::updateReceiveState() +{ + if (m_detecting) { + AndroidNfc::registerListener(this); + AndroidNfc::startDiscovery(); + } else { + if (ndefMessageHandlers.count() || ndefFilterHandlers.count()) { + AndroidNfc::registerListener(this); + AndroidNfc::startDiscovery(); + } else { + AndroidNfc::stopDiscovery(); + AndroidNfc::unregisterListener(this); + } + } +} + +QT_END_NAMESPACE diff --git a/src/nfc/qnearfieldmanager_android_p.h b/src/nfc/qnearfieldmanager_android_p.h new file mode 100644 index 00000000..1e1e218a --- /dev/null +++ b/src/nfc/qnearfieldmanager_android_p.h @@ -0,0 +1,103 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNEARFIELDMANAGER_ANDROID_P_H +#define QNEARFIELDMANAGER_ANDROID_P_H + +#include "qnearfieldmanager_p.h" +#include "qnearfieldmanager.h" +#include "qnearfieldtarget.h" +#include "android/androidjninfc_p.h" + +#include <QHash> +#include <QMap> + +QT_BEGIN_NAMESPACE + +typedef QList<QNdefMessage> QNdefMessageList; + +class NearFieldTarget; +class QByteArray; +class QNearFieldManagerPrivateImpl : public QNearFieldManagerPrivate, public AndroidNfc::AndroidNfcListenerInterface +{ + Q_OBJECT + +public: + QNearFieldManagerPrivateImpl(); + ~QNearFieldManagerPrivateImpl(); + + virtual bool isAvailable() const; + virtual bool startTargetDetection(); + virtual void stopTargetDetection(); + virtual int registerNdefMessageHandler(QObject *object, const QMetaMethod &method); + virtual int registerNdefMessageHandler(const QNdefFilter &filter, QObject *object, const QMetaMethod &method); + virtual bool unregisterNdefMessageHandler(int handlerId); + virtual void requestAccess(QNearFieldManager::TargetAccessModes accessModes); + virtual void releaseAccess(QNearFieldManager::TargetAccessModes accessModes); + virtual void newIntent(jobject intent); + QByteArray getUid(jobject intent); + +public slots: + void onTargetDiscovered(jobject intent); + void onTargetDestroyed(const QByteArray &uid); + void handlerTargetDetected(QNearFieldTarget *target); + void handlerTargetLost(QNearFieldTarget *target); + void handlerNdefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id); + void handlerRequestCompleted(const QNearFieldTarget::RequestId &id); + void handlerError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id); + +protected: + static QByteArray getUid(JNIEnv *env, jobject tag); + void updateReceiveState(); + +private: + bool m_detecting; + QHash<QByteArray, NearFieldTarget*> m_detectedTargets; + QMap<QNearFieldTarget::RequestId, QNearFieldTarget*> m_idToTarget; + + int m_handlerID; + QList< QPair<QPair<int, QObject *>, QMetaMethod> > ndefMessageHandlers; + QList< QPair<QPair<int, QObject *>, QPair<QNdefFilter, QMetaMethod> > > ndefFilterHandlers; +}; + +QT_END_NAMESPACE + +#endif // QNEARFIELDMANAGER_ANDROID_P_H diff --git a/src/nfc/qnearfieldtarget_android.cpp b/src/nfc/qnearfieldtarget_android.cpp new file mode 100644 index 00000000..0fd0e665 --- /dev/null +++ b/src/nfc/qnearfieldtarget_android.cpp @@ -0,0 +1,499 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnearfieldtarget_android_p.h" +#include "android/androidjninfc_p.h" +#include "qdebug.h" + +const QString NearFieldTarget::NdefTechology = QString::fromUtf8("android.nfc.tech.Ndef"); +const QString NearFieldTarget::NdefFormatableTechnology = QString::fromUtf8("android.nfc.tech.NdefFormatable"); +const QString NearFieldTarget::NfcATechnology = QString::fromUtf8("android.nfc.tech.NfcA"); +const QString NearFieldTarget::NfcBTechnology = QString::fromUtf8("android.nfc.tech.NfcB"); +const QString NearFieldTarget::NfcFTechnology = QString::fromUtf8("android.nfc.tech.NfcF"); +const QString NearFieldTarget::NfcVTechnology = QString::fromUtf8("android.nfc.tech.NfcV"); +const QString NearFieldTarget::MifareClassicTechnology = QString::fromUtf8("android.nfc.tech.MifareClassic"); +const QString NearFieldTarget::MifareUltralightTechnology = QString::fromUtf8("android.nfc.tech.MifareUltralight"); + + +NearFieldTarget::NearFieldTarget(jobject 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(NdefTechology); +} + +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 == 0) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Getting Ndef technology object + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + Q_ASSERT_X(env != 0, "readNdefMessages", "env pointer is null"); + jobject ndef = getTagTechnology(NdefTechology, env); + if (ndef == 0) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnsupportedError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Connect + jclass ndefClass = env->GetObjectClass(ndef); + jmethodID connectMID = env->GetMethodID(ndefClass, "connect", "()V"); + env->CallVoidMethod(ndef, connectMID); + if (catchJavaExceptions(env)) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Get NdefMessage object + jmethodID getNdefMessageMID = env->GetMethodID(ndefClass, "getNdefMessage", "()Landroid/nfc/NdefMessage;"); + jobject ndefMessage = env->CallObjectMethod(ndef, getNdefMessageMID); + if (catchJavaExceptions(env)) + ndefMessage = 0; + if (ndefMessage == 0) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefReadError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Convert to byte array + jclass ndefMessageClass = env->GetObjectClass(ndefMessage); + jmethodID toByteArrayMID = env->GetMethodID(ndefMessageClass, "toByteArray", "()[B"); + jbyteArray ndefMessageBA = reinterpret_cast<jbyteArray>(env->CallObjectMethod(ndefMessage, toByteArrayMID)); + QByteArray ndefMessageQBA = jbyteArrayToQByteArray(ndefMessageBA, env); + + // Closing connection + jmethodID closeMID = env->GetMethodID(ndefClass, "close", "()V"); + env->CallVoidMethod(ndef, closeMID); + catchJavaExceptions(env); // 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(NfcATechnology)) { + tagTech = getTagTechnology(NfcATechnology); + } else if (m_techList.contains(NfcBTechnology)) { + tagTech = getTagTechnology(NfcBTechnology); + } else if (m_techList.contains(NfcFTechnology)) { + tagTech = getTagTechnology(NfcFTechnology); + } else if (m_techList.contains(NfcVTechnology)) { + tagTech = getTagTechnology(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."); + + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + jmethodID writeMethod; + jobject tagTechnology; + jclass tagClass; + + // Getting write method + if (m_techList.contains(NdefFormatableTechnology)) { + tagTechnology = getTagTechnology(NdefFormatableTechnology, env); + tagClass = env->GetObjectClass(tagTechnology); + writeMethod = env->GetMethodID(tagClass, "format", "(Landroid/nfc/NdefMessage;)V"); + } else if (m_techList.contains(NdefTechology)) { + tagTechnology = getTagTechnology(NdefTechology, env); + tagClass = env->GetObjectClass(tagTechnology); + writeMethod = env->GetMethodID(tagClass, "writeNdefMessage", "(Landroid/nfc/NdefMessage;)V"); + } 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()); + jclass ndefMessageClass = env->FindClass("android/nfc/NdefMessage"); + jmethodID connectMethodID = env->GetMethodID(tagClass, "connect", "()V"); + env->CallVoidMethod(tagTechnology, connectMethodID); + if (catchJavaExceptions(env)) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Making NdefMessage object + jmethodID ndefMessageInitMID = env->GetMethodID(ndefMessageClass, "<init>", "([B)V"); + const QNdefMessage &message = messages.first(); + QByteArray ba = message.toByteArray(); + jbyteArray jba = env->NewByteArray(ba.size()); + env->SetByteArrayRegion(jba, 0, ba.size(), reinterpret_cast<jbyte*>(ba.data())); + jobject jmessage = env->NewObject(ndefMessageClass, ndefMessageInitMID, jba); + if (catchJavaExceptions(env)) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Writing + env->CallVoidMethod(tagTechnology, writeMethod, jmessage); + bool gotException = catchJavaExceptions(env); + env->DeleteLocalRef(jba); + env->DeleteLocalRef(jmessage); + if (gotException) { + 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 + jmethodID closeMID = env->GetMethodID(tagClass, "close", "()V"); + env->CallVoidMethod(tagTechnology, closeMID); + catchJavaExceptions(env); // IOException at this point does not matter anymore. + QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection); + return requestId; +} + +void NearFieldTarget::setIntent(jobject intent) +{ + if (m_intent == intent) + return; + + releaseIntent(); + m_intent = intent; + if (m_intent) { + // 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 == 0 || m_techList.isEmpty()) { + handleTargetLost(); + return; + } + + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + + // Using first available technology to check connection + QString techStr = m_techList.first(); + jobject tagTechObj = getTagTechnology(techStr, env); + jclass tagTechClass = env->GetObjectClass(tagTechObj); + jmethodID connectMID = env->GetMethodID(tagTechClass, "connect","()V"); + jmethodID closeMID = env->GetMethodID(tagTechClass, "close","()V"); + + env->CallObjectMethod(tagTechObj, connectMID); + if (catchJavaExceptions(env)) { + handleTargetLost(); + return; + } + env->CallObjectMethod(tagTechObj, closeMID); + if (catchJavaExceptions(env)) + handleTargetLost(); +} + +void NearFieldTarget::releaseIntent() +{ + m_targetCheckTimer->stop(); + + if (m_intent == 0) + return; + + AndroidNfc::AttachedJNIEnv aenv; + Q_ASSERT_X(aenv.jniEnv != 0, "releaseIntent", "aenv.jniEnv pointer is null"); + aenv.jniEnv->DeleteGlobalRef(m_intent); + m_intent = 0; +} + +void NearFieldTarget::updateTechList() +{ + if (m_intent == 0) + return; + + // Getting tech list + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + jobject tag = AndroidNfc::getTag(env, m_intent); + jclass tagClass = env->GetObjectClass(tag); + jmethodID techListMID = env->GetMethodID(tagClass, "getTechList", "()[Ljava/lang/String;"); + jobjectArray techListArray = reinterpret_cast<jobjectArray>(env->CallObjectMethod(tag, techListMID)); + if (techListArray == 0) { + handleTargetLost(); + return; + } + + // Converting tech list array to QStringList. + m_techList.clear(); + jsize techCount = env->GetArrayLength(techListArray); + for (jsize i = 0; i < techCount; ++i) { + jstring tech = reinterpret_cast<jstring>(env->GetObjectArrayElement(techListArray, i)); + const char *techStr = env->GetStringUTFChars(tech, JNI_FALSE); + m_techList.append(QString::fromUtf8(techStr)); + env->ReleaseStringUTFChars(tech, techStr); + } +} + +void NearFieldTarget::updateType() +{ + m_type = getTagType(); +} + +QNearFieldTarget::Type NearFieldTarget::getTagType() const +{ + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + Q_ASSERT_X(env != 0, "type", "env pointer is null"); + + if (m_techList.contains(NdefTechology)) { + jobject ndef = getTagTechnology(NdefTechology, env); + jclass ndefClass = env->GetObjectClass(ndef); + + jmethodID typeMethodID = env->GetMethodID(ndefClass, "getType", "()Ljava/lang/String;"); + jstring jtype = reinterpret_cast<jstring>(env->CallObjectMethod(ndef, typeMethodID)); + const char *type_data = env->GetStringUTFChars(jtype, JNI_FALSE); + QString qtype = QString::fromUtf8(type_data); + env->ReleaseStringUTFChars(jtype, type_data); + + QHash<QString, Type> types; + types.insert(QString::fromUtf8("com.nxp.ndef.mifareclassic"), MifareTag); + types.insert(QString::fromUtf8("org.nfcforum.ndef.type1"), NfcTagType1); + types.insert(QString::fromUtf8("org.nfcforum.ndef.type2"), NfcTagType2); + types.insert(QString::fromUtf8("org.nfcforum.ndef.type3"), NfcTagType3); + types.insert(QString::fromUtf8("org.nfcforum.ndef.type4"), NfcTagType4); + if (!types.contains(qtype)) + return ProprietaryTag; + return types[qtype]; + } else if (m_techList.contains(NfcATechnology)) { + if (m_techList.contains(MifareClassicTechnology)) + return MifareTag; + + // Checking ATQA/SENS_RES + // xxx0 0000 xxxx xxxx: Identifies tag Type 1 platform + jobject nfca = getTagTechnology(NfcATechnology, env); + jclass nfcaClass = env->GetObjectClass(nfca); + jmethodID atqaMethodID = env->GetMethodID(nfcaClass, "getAtqa", "()[B"); + jbyteArray atqaBA = reinterpret_cast<jbyteArray>(env->CallObjectMethod(nfca, atqaMethodID)); + QByteArray atqaQBA = jbyteArrayToQByteArray(atqaBA, env); + 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 + jmethodID sakMethodID = env->GetMethodID(nfcaClass, "getSak", "()S"); + jshort sakS = env->CallShortMethod(nfca, sakMethodID); + if ((sakS & 0x0064) == 0x0000) + return NfcTagType2; + else if ((sakS & 0x0064) == 0x0020) + return NfcTagType4; + return ProprietaryTag; + } else if (m_techList.contains(NfcBTechnology)) { + return NfcTagType4; + } else if (m_techList.contains(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); +} + +jobject NearFieldTarget::getTagTechnology(const QString &tech, JNIEnv *env) const +{ + QString techClass(tech); + techClass.replace(QLatin1Char('.'), QLatin1Char('/')); + + // Getting requested technology + jobject tag = AndroidNfc::getTag(env, m_intent); + jclass tagClass = env->FindClass(techClass.toUtf8().constData()); + const QString sig = QString::fromUtf8("(Landroid/nfc/Tag;)L%1;"); + jmethodID getTagMethodID = env->GetStaticMethodID(tagClass, "get", sig.arg(techClass).toUtf8().constData()); + jobject tagtech = env->CallStaticObjectMethod(tagClass, getTagMethodID, tag); + return tagtech; +} + +QByteArray NearFieldTarget::jbyteArrayToQByteArray(const jbyteArray &byteArray, JNIEnv *env) const +{ + 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(JNIEnv *env) const +{ + jthrowable exc = env->ExceptionOccurred(); + if (exc) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return true; + } + return false; +} diff --git a/src/nfc/qnearfieldtarget_android_p.h b/src/nfc/qnearfieldtarget_android_p.h new file mode 100644 index 00000000..ab52f58b --- /dev/null +++ b/src/nfc/qnearfieldtarget_android_p.h @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNEARFIELDTARGET_ANDROID_P_H +#define QNEARFIELDTARGET_ANDROID_P_H + +#include "android/androidjninfc_p.h" +#include "qnearfieldtarget.h" +#include "qnearfieldtarget_p.h" +#include "qndefmessage.h" +#include "qlist.h" +#include "qstringlist.h" +#include <QTimer> + + +QT_BEGIN_NAMESPACE + +class NearFieldTarget : public QNearFieldTarget +{ + Q_OBJECT +public: + NearFieldTarget(jobject intent, + const QByteArray uid, + QObject *parent = 0); + virtual ~NearFieldTarget(); + virtual QByteArray uid() const; + virtual Type type() const; + virtual AccessMethods accessMethods() const; + virtual bool hasNdefMessage(); + virtual RequestId readNdefMessages(); + virtual RequestId sendCommand(const QByteArray &command); + virtual RequestId sendCommands(const QList<QByteArray> &commands); + virtual RequestId writeNdefMessages(const QList<QNdefMessage> &messages); + void setIntent(jobject intent); + +signals: + void targetDestroyed(const QByteArray &tagId); + void targetLost(QNearFieldTarget *target); + void ndefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id); + +protected slots: + void checkIsTargetLost(); + +protected: + void releaseIntent(); + void updateTechList(); + void updateType(); + Type getTagType() const; + void setupTargetCheckTimer(); + void handleTargetLost(); + jobject getTagTechnology(const QString &tech, JNIEnv *env) const; + QByteArray jbyteArrayToQByteArray(const jbyteArray &byteArray, JNIEnv *env) const; + bool catchJavaExceptions(JNIEnv *env) const; + +protected: + jobject m_intent; + QByteArray m_uid; + QStringList m_techList; + Type m_type; + static const QString NdefTechology; + static const QString NdefFormatableTechnology; + static const QString NfcATechnology; + static const QString NfcBTechnology; + static const QString NfcFTechnology; + static const QString NfcVTechnology; + static const QString MifareClassicTechnology; + static const QString MifareUltralightTechnology; + QTimer *m_targetCheckTimer; +}; + +QT_END_NAMESPACE + +#endif // QNEARFIELDTARGET_ANDROID_P_H |