Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnearfieldtarget_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Centria research and development
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "android/androidjninfc_p.h"
6#include "qdebug.h"
7
8#define NDEFTECHNOLOGY QStringLiteral("android.nfc.tech.Ndef")
9#define NDEFFORMATABLETECHNOLOGY QStringLiteral("android.nfc.tech.NdefFormatable")
10#define ISODEPTECHNOLOGY QStringLiteral("android.nfc.tech.IsoDep")
11#define NFCATECHNOLOGY QStringLiteral("android.nfc.tech.NfcA")
12#define NFCBTECHNOLOGY QStringLiteral("android.nfc.tech.NfcB")
13#define NFCFTECHNOLOGY QStringLiteral("android.nfc.tech.NfcF")
14#define NFCVTECHNOLOGY QStringLiteral("android.nfc.tech.NfcV")
15#define MIFARECLASSICTECHNOLOGY QStringLiteral("android.nfc.tech.MifareClassic")
16#define MIFARECULTRALIGHTTECHNOLOGY QStringLiteral("android.nfc.tech.MifareUltralight")
17
18#define MIFARETAG QStringLiteral("com.nxp.ndef.mifareclassic")
19#define NFCTAGTYPE1 QStringLiteral("org.nfcforum.ndef.type1")
20#define NFCTAGTYPE2 QStringLiteral("org.nfcforum.ndef.type2")
21#define NFCTAGTYPE3 QStringLiteral("org.nfcforum.ndef.type3")
22#define NFCTAGTYPE4 QStringLiteral("org.nfcforum.ndef.type4")
23
24QNearFieldTargetPrivateImpl::QNearFieldTargetPrivateImpl(QJniObject intent,
25 const QByteArray uid,
26 QObject *parent)
27: QNearFieldTargetPrivate(parent),
28 targetIntent(intent),
29 targetUid(uid)
30{
31 updateTechList();
32 updateType();
33 setupTargetCheckTimer();
34}
35
37{
39 Q_EMIT targetDestroyed(targetUid);
40}
41
43{
44 return targetUid;
45}
46
48{
49 return tagType;
50}
51
53{
54 QNearFieldTarget::AccessMethods result = QNearFieldTarget::UnknownAccess;
55
56 if (techList.contains(NDEFTECHNOLOGY)
57 || techList.contains(NDEFFORMATABLETECHNOLOGY))
58 result |= QNearFieldTarget::NdefAccess;
59
60 if (techList.contains(ISODEPTECHNOLOGY)
61 || techList.contains(NFCATECHNOLOGY)
62 || techList.contains(NFCBTECHNOLOGY)
63 || techList.contains(NFCFTECHNOLOGY)
64 || techList.contains(NFCVTECHNOLOGY))
65 result |= QNearFieldTarget::TagTypeSpecificAccess;
66
67 return result;
68}
69
71{
72 if (!tagTech.isValid())
73 return false;
74 QJniEnvironment env;
75 bool connected = tagTech.callMethod<jboolean>("isConnected");
76 if (!connected)
77 return false;
78 auto methodId = env.findMethod<void>(tagTech.objectClass(), "close");
79 if (!methodId)
80 return false;
81 env->CallVoidMethod(tagTech.object(), methodId);
82 return !env.checkAndClearExceptions();
83}
84
86{
87 return techList.contains(NDEFTECHNOLOGY);
88}
89
91{
92 // Making sure that target has NDEF messages
93 if (!hasNdefMessage())
94 return QNearFieldTarget::RequestId();
95
96 // Making sure that target is still in range
97 QNearFieldTarget::RequestId requestId(new QNearFieldTarget::RequestIdPrivate);
98 if (!targetIntent.isValid()) {
99 reportError(QNearFieldTarget::TargetOutOfRangeError, requestId);
100 return requestId;
101 }
102
103 // Getting Ndef technology object
104 if (!setTagTechnology({NDEFTECHNOLOGY})) {
105 reportError(QNearFieldTarget::UnsupportedError, requestId);
106 return requestId;
107 }
108
109 // Connect
110 if (!connect()) {
111 reportError(QNearFieldTarget::ConnectionError, requestId);
112 return requestId;
113 }
114
115 // Get NdefMessage object
116 QJniObject ndefMessage = tagTech.callMethod<QtJniTypes::NdefMessage>("getNdefMessage");
117 if (!ndefMessage.isValid()) {
118 reportError(QNearFieldTarget::NdefReadError, requestId);
119 return requestId;
120 }
121
122 QByteArray ndefMessageQBA = ndefMessage.callMethod<jbyte[]>("toByteArray").toContainer();
123
124 // Sending QNdefMessage, requestCompleted and exit.
125 QNdefMessage qNdefMessage = QNdefMessage::fromByteArray(ndefMessageQBA);
126 QMetaObject::invokeMethod(this, [this, qNdefMessage]() {
127 Q_EMIT this->q_ptr->ndefMessageRead(qNdefMessage);
128 }, Qt::QueuedConnection);
129 QMetaObject::invokeMethod(this, [this, requestId]() {
130 Q_EMIT this->requestCompleted(requestId);
131 }, Qt::QueuedConnection);
132 QMetaObject::invokeMethod(this, [this, qNdefMessage, requestId]() {
133 //TODO This is an Android specific signal in NearFieldTarget.
134 // We need to check if it is still necessary.
135 Q_EMIT this->ndefMessageRead(qNdefMessage, requestId);
136 }, Qt::QueuedConnection);
137 return requestId;
138}
139
141{
142 QJniObject tagTech;
143 if (techList.contains(ISODEPTECHNOLOGY))
144 tagTech = getTagTechnology(ISODEPTECHNOLOGY);
145 else if (techList.contains(NFCATECHNOLOGY))
146 tagTech = getTagTechnology(NFCATECHNOLOGY);
147 else if (techList.contains(NFCBTECHNOLOGY))
148 tagTech = getTagTechnology(NFCBTECHNOLOGY);
149 else if (techList.contains(NFCFTECHNOLOGY))
150 tagTech = getTagTechnology(NFCFTECHNOLOGY);
151 else if (techList.contains(NFCVTECHNOLOGY))
152 tagTech = getTagTechnology(NFCVTECHNOLOGY);
153 else
154 return 0;
155
156 return tagTech.callMethod<jint>("getMaxTransceiveLength");
157}
158
160{
161 const int maxLength = maxCommandLength();
162 if (maxLength == 0) {
163 // Similar to the handling of "transceive" below, some devices (especially Samsung)
164 // fail to report a lost card right away but consider it still present.
165 // Thus, checkIsTargetLost() does not emit the targetLost signal. When actually
166 // trying to communicate with the alleged card, the JNI call will fail with
167 // an SecurityException, indicating that the card was, in fact, lost before.
169 Q_EMIT error(QNearFieldTarget::ConnectionError, QNearFieldTarget::RequestId());
170 return QNearFieldTarget::RequestId();
171 }
172
173 if (command.size() == 0 || command.size() > maxLength) {
174 Q_EMIT error(QNearFieldTarget::InvalidParametersError, QNearFieldTarget::RequestId());
175 return QNearFieldTarget::RequestId();
176 }
177
178 // Making sure that target has commands
179 if (!(accessMethods() & QNearFieldTarget::TagTypeSpecificAccess))
180 return QNearFieldTarget::RequestId();
181
182 QJniEnvironment env;
183
185 Q_EMIT error(QNearFieldTarget::UnsupportedError, QNearFieldTarget::RequestId());
186 return QNearFieldTarget::RequestId();
187 }
188
189 // Connecting
190 QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
191 if (!connect()) {
192 reportError(QNearFieldTarget::ConnectionError, requestId);
193 return requestId;
194 }
195
196 // Writing
197 const QJniArray myNewVal = tagTech.callMethod<jbyte[]>("transceive", command);
198 if (!myNewVal.isValid()) {
199 // Some devices (Samsung, Huawei) throw an exception when the card is lost:
200 // "android.nfc.TagLostException: Tag was lost". But there seems to be a bug that
201 // isConnected still reports true. So we need to invalidate the target as soon as
202 // possible and treat the card as lost.
204
205 reportError(QNearFieldTarget::CommandError, requestId);
206 return requestId;
207 }
208 QByteArray result = myNewVal.toContainer();
209
210 setResponseForRequest(requestId, result, false);
211
212 QMetaObject::invokeMethod(this, [this, requestId]() {
213 Q_EMIT this->requestCompleted(requestId);
214 }, Qt::QueuedConnection);
215
216 return requestId;
217}
218
219QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::writeNdefMessages(const QList<QNdefMessage> &messages)
220{
221 if (messages.size() == 0)
222 return QNearFieldTarget::RequestId();
223
224 if (messages.size() > 1)
225 qWarning("QNearFieldTarget::writeNdefMessages: Android supports writing only one NDEF message per tag.");
226
227 QJniEnvironment env;
228 const char *writeMethod;
229
230 if (!setTagTechnology({NDEFFORMATABLETECHNOLOGY, NDEFTECHNOLOGY}))
231 return QNearFieldTarget::RequestId();
232
233 // Getting write method
234 if (selectedTech == NDEFFORMATABLETECHNOLOGY)
235 writeMethod = "format";
236 else
237 writeMethod = "writeNdefMessage";
238
239 // Connecting
240 QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
241 if (!connect()) {
242 reportError(QNearFieldTarget::ConnectionError, requestId);
243 return requestId;
244 }
245
246 // Making NdefMessage object
247 const QNdefMessage &message = messages.first();
248 QtJniTypes::NdefMessage jmessage(message.toByteArray());
249 if (!jmessage.isValid()) {
250 reportError(QNearFieldTarget::UnknownError, requestId);
251 return requestId;
252 }
253
254 // Writing
255 auto methodId =
256 env.findMethod<void, QtJniTypes::NdefMessage>(tagTech.objectClass(), writeMethod);
257 if (methodId)
258 env->CallVoidMethod(tagTech.object(), methodId, jmessage.object<jobject>());
259 if (!methodId || env.checkAndClearExceptions()) {
260 reportError(QNearFieldTarget::NdefWriteError, requestId);
261 return requestId;
262 }
263
264 QMetaObject::invokeMethod(this, [this, requestId]() {
265 Q_EMIT this->requestCompleted(requestId);
266 }, Qt::QueuedConnection);
267 return requestId;
268}
269
271{
272 if (targetIntent == intent)
273 return;
274
276 targetIntent = intent;
277 if (targetIntent.isValid()) {
278 // Updating tech list and type in case of there is another tag with same UID as one before.
281 targetCheckTimer->start();
282 }
283}
284
285void QNearFieldTargetPrivateImpl::checkIsTargetLost()
286{
287 if (!targetIntent.isValid() || !setTagTechnology({selectedTech})) {
289 return;
290 }
291
292 QJniEnvironment env;
293 bool connected = false;
294 auto methodId = env.findMethod<jboolean>(tagTech.objectClass(), "isConnected");
295 if (methodId)
296 connected = env->CallBooleanMethod(tagTech.object(), methodId);
297 if (!methodId || env.checkAndClearExceptions()) {
299 return;
300 }
301
302 if (connected)
303 return;
304
305 methodId = env.findMethod<void>(tagTech.objectClass(), "connect");
306 if (methodId)
307 env->CallVoidMethod(tagTech.object(), methodId);
308 if (!methodId || env.checkAndClearExceptions(QJniEnvironment::OutputMode::Silent)) {
310 return;
311 }
312 methodId = env.findMethod<void>(tagTech.objectClass(), "close");
313 if (methodId)
314 env->CallVoidMethod(tagTech.object(), methodId);
315 if (!methodId || env.checkAndClearExceptions(QJniEnvironment::OutputMode::Silent))
317}
318
320{
321 targetCheckTimer->stop();
322
323 targetIntent = QJniObject();
324}
325
327{
328 if (!targetIntent.isValid())
329 return;
330
331 // Getting tech list
332 QJniEnvironment env;
333 QJniObject tag = QtNfc::getTag(targetIntent);
334 Q_ASSERT_X(tag.isValid(), "updateTechList", "could not get Tag object");
335
336 const QJniArray techListArray = tag.callMethod<QtJniTypes::String[]>("getTechList");
337 if (!techListArray.isValid()) {
339 return;
340 }
341
342 // Converting tech list array to QStringList.
343 techList.clear();
344 for (const auto &tech : techListArray)
345 techList.append(tech.toString());
346}
347
349{
350 tagType = getTagType();
351}
352
353QNearFieldTarget::Type QNearFieldTargetPrivateImpl::getTagType() const
354{
355 if (techList.contains(NDEFTECHNOLOGY)) {
356 QJniObject ndef = getTagTechnology(NDEFTECHNOLOGY);
357 QString qtype = ndef.callMethod<jstring>("getType").toString();
358
359 if (qtype.compare(MIFARETAG) == 0)
360 return QNearFieldTarget::MifareTag;
361 if (qtype.compare(NFCTAGTYPE1) == 0)
362 return QNearFieldTarget::NfcTagType1;
363 if (qtype.compare(NFCTAGTYPE2) == 0)
364 return QNearFieldTarget::NfcTagType2;
365 if (qtype.compare(NFCTAGTYPE3) == 0)
366 return QNearFieldTarget::NfcTagType3;
367 if (qtype.compare(NFCTAGTYPE4) == 0)
368 return QNearFieldTarget::NfcTagType4;
369 return QNearFieldTarget::ProprietaryTag;
370 } else if (techList.contains(NFCATECHNOLOGY)) {
371 if (techList.contains(MIFARECLASSICTECHNOLOGY))
372 return QNearFieldTarget::MifareTag;
373
374 // Checking ATQA/SENS_RES
375 // xxx0 0000 xxxx xxxx: Identifies tag Type 1 platform
376 QJniObject nfca = getTagTechnology(NFCATECHNOLOGY);
377 const QByteArray atqaQBA = nfca.callMethod<QByteArray>("getAtqa");
378 if (atqaQBA.isEmpty())
379 return QNearFieldTarget::ProprietaryTag;
380 if ((atqaQBA[0] & 0x1F) == 0x00)
381 return QNearFieldTarget::NfcTagType1;
382
383 // Checking SAK/SEL_RES
384 // xxxx xxxx x00x x0xx: Identifies tag Type 2 platform
385 // xxxx xxxx x01x x0xx: Identifies tag Type 4 platform
386 jshort sakS = nfca.callMethod<jshort>("getSak");
387 if ((sakS & 0x0064) == 0x0000)
388 return QNearFieldTarget::NfcTagType2;
389 else if ((sakS & 0x0064) == 0x0020)
390 return QNearFieldTarget::NfcTagType4A;
391 return QNearFieldTarget::ProprietaryTag;
392 } else if (techList.contains(NFCBTECHNOLOGY)) {
393 return QNearFieldTarget::NfcTagType4B;
394 } else if (techList.contains(NFCFTECHNOLOGY)) {
395 return QNearFieldTarget::NfcTagType3;
396 }
397
398 return QNearFieldTarget::ProprietaryTag;
399}
400
402{
403 targetCheckTimer = new QTimer(this);
404 targetCheckTimer->setInterval(1000);
405 QObject::connect(targetCheckTimer, &QTimer::timeout, this, &QNearFieldTargetPrivateImpl::checkIsTargetLost);
406 targetCheckTimer->start();
407}
408
410{
412 Q_EMIT targetLost(this);
413}
414
416{
417 QString techClass(tech);
418 techClass.replace(QLatin1Char('.'), QLatin1Char('/'));
419
420 // Getting requested technology
421 QJniObject tag = QtNfc::getTag(targetIntent);
422 Q_ASSERT_X(tag.isValid(), "getTagTechnology", "could not get Tag object");
423
424 const QString sig = QString::fromUtf8("(Landroid/nfc/Tag;)L%1;");
425 QJniObject tagTech = QJniObject::callStaticObjectMethod(techClass.toUtf8().constData(), "get",
426 sig.arg(techClass).toUtf8().constData(), tag.object<jobject>());
427
428 return tagTech;
429}
430
431bool QNearFieldTargetPrivateImpl::setTagTechnology(const QStringList &technologies)
432{
433 for (const QString &tech : technologies) {
434 if (techList.contains(tech)) {
435 if (selectedTech == tech) {
436 return true;
437 }
438 selectedTech = tech;
439 tagTech = getTagTechnology(tech);
440 return tagTech.isValid();
441 }
442 }
443
444 return false;
445}
446
447bool QNearFieldTargetPrivateImpl::connect()
448{
449 if (!tagTech.isValid())
450 return false;
451
452 QJniEnvironment env;
453 auto methodId = env.findMethod<jboolean>(tagTech.objectClass(), "isConnected");
454 bool connected = false;
455 if (methodId)
456 connected = env->CallBooleanMethod(tagTech.object(), methodId);
457 if (!methodId || env.checkAndClearExceptions()) {
459 return false;
460 }
461
462 if (connected)
463 return true;
464
466 methodId = env.findMethod<void>(tagTech.objectClass(), "connect");
467 if (!methodId)
468 return false;
469 env->CallVoidMethod(tagTech.object(), methodId);
470 return !env.checkAndClearExceptions();
471}
472
474{
475 if (!tagTech.isValid())
476 return false;
477
478 QJniEnvironment env;
479 auto methodId = env.findMethod<void, jint>(tagTech.objectClass(), "setTimeout");
480 if (methodId)
481 env->CallVoidMethod(tagTech.object(), methodId, timeout);
482 return methodId && !env.checkAndClearExceptions();
483}
Definition qlist.h:80
The QNdefMessage class provides an NFC NDEF message.
QJniObject getTagTechnology(const QString &tech) const
bool setTagTechnology(const QStringList &technologies)
QNearFieldTarget::RequestId readNdefMessages() override
QNearFieldTarget::Type type() const override
QNearFieldTarget::RequestId sendCommand(const QByteArray &command) override
QNearFieldTarget::AccessMethods accessMethods() const override
QByteArray uid() const override
#define NFCBTECHNOLOGY
#define NFCTAGTYPE4
#define MIFARETAG
#define MIFARECLASSICTECHNOLOGY
#define NFCVTECHNOLOGY
#define NFCTAGTYPE1
#define NFCATECHNOLOGY
#define NFCFTECHNOLOGY
#define NDEFTECHNOLOGY
#define NFCTAGTYPE2
#define ISODEPTECHNOLOGY
#define NFCTAGTYPE3
#define NDEFFORMATABLETECHNOLOGY