4#include "ios/qiosnfcndefsessiondelegate_p.h"
5#include "ios/qiosndefnotifier_p.h"
9#import <CoreNFC/NFCNDEFReaderSession.h>
10#import <CoreNFC/NFCReaderSession.h>
11#import <CoreNFC/NFCTagReaderSession.h>
12#import <CoreNFC/NFCISO7816Tag.h>
13#import <CoreNFC/NFCTag.h>
15#include <QtCore/qapplicationstatic.h>
16#include <QtCore/qloggingcategory.h>
20Q_APPLICATION_STATIC(ResponseProvider, responseProvider)
22void ResponseProvider::provideResponse(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer) {
23 Q_EMIT responseReceived(requestId, error, recvBuffer);
28 id some =
static_cast<id>(obj);
30 if ([some conformsToProtocol:@protocol(NFCNDEFTag)])
31 [
static_cast<id<NFCNDEFTag>>(some) release];
32 else if ([some conformsToProtocol:@protocol(NFCTag)])
33 [
static_cast<id<NFCTag>>(some) release];
39 QNearFieldTargetPrivate(parent),
44 QObject::connect(
this, &QNearFieldTargetPrivate::error,
this, &QNearFieldTargetPrivateImpl::onTargetError);
45 QObject::connect(responseProvider, &ResponseProvider::responseReceived,
this, &QNearFieldTargetPrivateImpl::onResponseReceived);
46 QObject::connect(&targetCheckTimer, &QTimer::timeout,
this, &QNearFieldTargetPrivateImpl::onTargetCheck);
47 targetCheckTimer.start(500);
54 Q_ASSERT(delegate && tag);
55 Q_ASSERT([id(tag) conformsToProtocol:@protocol(NFCNDEFTag)]);
57 auto qtDelegate =
static_cast<QIosNfcNdefSessionDelegate *>(sessionDelegate = delegate);
58 notifier = [qtDelegate ndefNotifier];
63 QObject::connect(notifier, &QNfcNdefNotifier::tagError,
this,
64 &QNearFieldTargetPrivate::error, Qt::QueuedConnection);
66 QObject::connect(
this, &QNearFieldTargetPrivate::error,
this, &QNearFieldTargetPrivateImpl::onTargetError);
67 QObject::connect(&targetCheckTimer, &QTimer::timeout,
this, &QNearFieldTargetPrivateImpl::onTargetCheck);
69 targetCheckTimer.start(500);
79 ndefOperations.clear();
84 QObject::disconnect(notifier,
nullptr,
this,
nullptr);
89 sessionDelegate = nil;
91 targetCheckTimer.stop();
93 QMetaObject::invokeMethod(
this, [
this]() {
94 Q_EMIT targetLost(
this);
95 }, Qt::QueuedConnection);
100 if (!nfcTag || isNdefTag())
103 if (@available(iOS 13, *)) {
104 id<NFCTag> tag =
static_cast<id<NFCTag>>(nfcTag.get());
105 id<NFCISO7816Tag> iso7816Tag = tag.asNFCISO7816Tag;
107 return QByteArray::fromNSData(iso7816Tag.identifier);
115 if (!nfcTag || isNdefTag())
116 return QNearFieldTarget::ProprietaryTag;
118 if (@available(iOS 13, *)) {
119 id<NFCTag> tag =
static_cast<id<NFCTag>>(nfcTag.get());
120 id<NFCISO7816Tag> iso7816Tag = tag.asNFCISO7816Tag;
122 if (tag.type != NFCTagTypeISO7816Compatible || iso7816Tag == nil)
123 return QNearFieldTarget::ProprietaryTag;
125 if (iso7816Tag.historicalBytes != nil && iso7816Tag.applicationData == nil)
126 return QNearFieldTarget::NfcTagType4A;
128 if (iso7816Tag.historicalBytes == nil && iso7816Tag.applicationData != nil)
129 return QNearFieldTarget::NfcTagType4B;
131 return QNearFieldTarget::NfcTagType4;
134 return QNearFieldTarget::ProprietaryTag;
140 return QNearFieldTarget::NdefAccess;
142 if (@available(iOS 13, *)) {
143 id<NFCTag> tag =
static_cast<id<NFCTag>>(nfcTag.get());
144 if (tag && [tag conformsToProtocol:@protocol(NFCISO7816Tag)])
145 return QNearFieldTarget::TagTypeSpecificAccess;
148 return QNearFieldTarget::UnknownAccess;
153 if (accessMethods() & QNearFieldTarget::TagTypeSpecificAccess)
162 QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(
new QNearFieldTarget::RequestIdPrivate());
164 if (!(accessMethods() & QNearFieldTarget::TagTypeSpecificAccess)) {
165 reportError(QNearFieldTarget::UnsupportedError, requestId);
169 queue.enqueue(std::pair(requestId, command));
172 reportError(QNearFieldTarget::TargetOutOfRangeError, requestId);
182 return hasNDEFMessage;
187 hasNDEFMessage =
false;
189 QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(
new QNearFieldTarget::RequestIdPrivate);
191 if (!(accessMethods() & QNearFieldTarget::NdefAccess) || !isNdefTag()) {
192 qCWarning(QT_IOS_NFC,
"Target does not allow to read NDEF messages, "
193 "was not detected as NDEF tag by the reader session?");
194 reportError(QNearFieldTarget::UnsupportedError, requestId);
200 op.requestId = requestId;
202 ndefOperations.push_back(op);
210 auto requestId = QNearFieldTarget::RequestId(
new QNearFieldTarget::RequestIdPrivate);
212 if (!(accessMethods() & QNearFieldTarget::NdefAccess) || !isNdefTag()) {
213 qCWarning(QT_IOS_NFC,
"Target does not allow to write NDEF messages, "
214 "was not detected as NDEF tag by the reader session?");
215 reportError(QNearFieldTarget::UnsupportedError, requestId);
219 if (messages.size() != 1) {
224 qCWarning(QT_IOS_NFC,
"Only one NDEF message per request ID can be written");
225 reportError(QNearFieldTarget::UnsupportedError, requestId);
231 op.requestId = requestId;
232 op.message = messages.first();
234 ndefOperations.push_back(op);
242 if (requestInProgress.isValid())
245 const auto tagIsAvailable = [
this](
auto tag) {
246 return tag && (!connected || tag.available);
250 return tagIsAvailable(
static_cast<id<NFCNDEFTag>>(nfcTag.get()));
252 if (@available(iOS 13, *))
253 return tagIsAvailable(
static_cast<id<NFCTag>>(nfcTag.get()));
260 if (connected || requestInProgress.isValid())
264 return connected =
true;
266 if (!isAvailable() || queue.isEmpty())
269 if (@available(iOS 13, *)) {
270 requestInProgress = queue.head().first;
271 id<NFCTag> tag =
static_cast<id<NFCTag>>(nfcTag.get());
272 NFCTagReaderSession* session = tag.session;
273 [session connectToTag: tag completionHandler: ^(NSError* error){
274 const int errorCode = error == nil ? -1 : error.code;
275 QMetaObject::invokeMethod(
this, [
this, errorCode] {
276 requestInProgress = QNearFieldTarget::RequestId();
277 if (errorCode == -1) {
279 justConnected =
true;
282 const auto requestId = queue.dequeue().first;
284 errorCode == NFCReaderError::NFCReaderErrorSecurityViolation
285 ? QNearFieldTarget::UnsupportedTargetError
286 : QNearFieldTarget::ConnectionError,
300 const id tag =
static_cast<id>(nfcTag.get());
301 if ([tag conformsToProtocol:@protocol(NFCMiFareTag)])
303 if ([tag conformsToProtocol:@protocol(NFCFeliCaTag)])
305 if ([tag conformsToProtocol:@protocol(NFCISO15693Tag)])
307 if ([tag conformsToProtocol:@protocol(NFCISO7816Tag)])
309 return [tag conformsToProtocol:@protocol(NFCNDEFTag)];
322 if (error == QNearFieldTarget::TimeoutError)
328QNdefMessage ndefToQtNdefMessage(NFCNDEFMessage *nativeMessage)
333 QList<QNdefRecord> ndefRecords;
334 for (NFCNDEFPayload *ndefRecord in nativeMessage.records) {
335 QNdefRecord qtNdefRecord;
336 if (ndefRecord.typeNameFormat != NFCTypeNameFormatUnchanged)
337 qtNdefRecord.setTypeNameFormat(QNdefRecord::TypeNameFormat(ndefRecord.typeNameFormat));
338 if (ndefRecord.identifier)
339 qtNdefRecord.setId(QByteArray::fromNSData(ndefRecord.identifier));
341 qtNdefRecord.setType(QByteArray::fromNSData(ndefRecord.type));
342 if (ndefRecord.payload)
343 qtNdefRecord.setPayload(QByteArray::fromNSData(ndefRecord.payload));
344 ndefRecords.push_back(qtNdefRecord);
354 if (!nfcTag || requestInProgress.isValid())
358 if (ndefOperations.empty())
361 auto *ndefDelegate =
static_cast<QIosNfcNdefSessionDelegate *>(sessionDelegate);
362 Q_ASSERT(ndefDelegate);
364 Q_ASSERT(qt_Nfc_Queue());
366 const auto op = ndefOperations.front();
367 ndefOperations.pop_front();
368 requestInProgress = op.requestId;
369 auto requestId = requestInProgress;
371 id<NFCNDEFTag> ndefTag =
static_cast<id<NFCNDEFTag>>(nfcTag.get());
374 auto *cbNotifier = guard.get();
376 QObject::connect(cbNotifier, &QNfcNdefNotifier::tagError,
this,
377 &QNearFieldTargetPrivate::error, Qt::QueuedConnection);
380 QObject::connect(cbNotifier, &QNfcNdefNotifier::ndefMessageRead,
381 this, &QNearFieldTargetPrivateImpl::messageRead,
382 Qt::QueuedConnection);
385 [ndefTag readNDEFWithCompletionHandler:^(NFCNDEFMessage *
_Nullable msg, NSError *
_Nullable err) {
386 const std::unique_ptr<QNfcNdefNotifier> notifierGuard(cbNotifier);
388 NSLog(@
"Reading NDEF messaged ended with error: %@", err);
389 emit cbNotifier->tagError(QNearFieldTarget::NdefReadError, requestId);
393 const QNdefMessage ndefMessage(ndefToQtNdefMessage(msg));
394 emit cbNotifier->ndefMessageRead(ndefMessage, requestId);
397 QObject::connect(cbNotifier, &QNfcNdefNotifier::ndefMessageWritten,
398 this, &QNearFieldTargetPrivateImpl::messageWritten,
399 Qt::QueuedConnection);
401 NSData *ndefData = op.message.toByteArray().toNSData();
404 NFCNDEFMessage *ndefMessage = [NFCNDEFMessage ndefMessageWithData:ndefData];
405 Q_ASSERT(ndefMessage);
407 [ndefTag writeNDEF:ndefMessage completionHandler:^(NSError *err) {
408 const std::unique_ptr<QNfcNdefNotifier> notifierGuard(cbNotifier);
410 NSLog(@
"Writing NDEF messaged ended with error: %@", err);
411 emit cbNotifier->tagError(QNearFieldTarget::NdefWriteError, requestId);
415 emit cbNotifier->ndefMessageWritten(requestId);
422 if (@available(iOS 13, *)) {
425 const auto request = queue.dequeue();
426 requestInProgress = request.first;
427 const auto tag =
static_cast<id<NFCISO7816Tag>>(nfcTag.get());
428 auto *apdu = [[[NFCISO7816APDU alloc] initWithData: request.second.toNSData()] autorelease];
429 [tag sendCommandAPDU: apdu completionHandler: ^(NSData* responseData, uint8_t sw1, uint8_t sw2, NSError* error){
430 QByteArray recvBuffer = QByteArray::fromNSData(responseData);
431 recvBuffer +=
static_cast<
char>(sw1);
432 recvBuffer +=
static_cast<
char>(sw2);
433 auto errorToReport = QNearFieldTarget::NoError;
436 switch (error.code) {
437 case NFCReaderError::NFCReaderTransceiveErrorSessionInvalidated:
438 case NFCReaderError::NFCReaderTransceiveErrorTagNotConnected:
440 errorToReport = QNearFieldTarget::UnsupportedTargetError;
441 justConnected =
false;
446 errorToReport = QNearFieldTarget::CommandError;
449 responseProvider->provideResponse(request.first, errorToReport, recvBuffer);
454void QNearFieldTargetPrivateImpl::onResponseReceived(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer)
456 if (requestInProgress != requestId)
459 requestInProgress = QNearFieldTarget::RequestId();
460 if (error == QNearFieldTarget::NoError) {
461 setResponseForRequest(requestId, recvBuffer,
true);
464 reportError(error, requestId);
471 hasNDEFMessage = message.size() != 0;
473 setResponseForRequest(requestId, message.toByteArray(),
true);
474 requestInProgress = {};
477 Q_EMIT ndefMessageRead(message);
482 requestInProgress = {};
485 Q_EMIT requestCompleted(requestId);
The QNdefMessage class provides an NFC NDEF message.
QNearFieldTarget::RequestId writeNdefMessages(const QList< QNdefMessage > &messages) override
QNearFieldTargetPrivateImpl(void *sessionDelegate, void *tag, QObject *parent=nullptr)
int maxCommandLength() const override
QNearFieldTarget::RequestId readNdefMessages() override
~QNearFieldTargetPrivateImpl() override
QNearFieldTarget::Type type() const override
QNearFieldTarget::RequestId sendCommand(const QByteArray &command) override
QNearFieldTarget::AccessMethods accessMethods() const override
bool hasNdefMessage() override
QByteArray uid() const override
Combined button and popup list for selecting options.
void operator()(void *tag)