1// Copyright (C) 2016 BlackBerry Limited, Copyright (C) 2016 BasysKom GmbH
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qnearfieldtarget_neard_p.h"
5
6#include <qndefnfctextrecord.h>
7#include <qndefnfcsmartposterrecord.h>
8#include <qndefnfcurirecord.h>
9
10QT_BEGIN_NAMESPACE
11
12Q_DECLARE_LOGGING_CATEGORY(QT_NFC_NEARD)
13
14QNearFieldTargetPrivateImpl::QNearFieldTargetPrivateImpl(QObject *parent, QDBusObjectPath interfacePath)
15 : QNearFieldTargetPrivate(parent),
16 m_tagPath(interfacePath),
17 m_readRequested(false)
18{
19 m_readErrorTimer.setSingleShot(true);
20 m_recordPathsCollectedTimer.setSingleShot(true);
21 m_delayedWriteTimer.setSingleShot(true);
22
23 qCDebug(QT_NFC_NEARD) << "tag found at path" << interfacePath.path();
24 m_dbusProperties = new OrgFreedesktopDBusPropertiesInterface(QStringLiteral("org.neard"),
25 interfacePath.path(), QDBusConnection::systemBus(), this);
26 if (!m_dbusProperties->isValid()) {
27 qCWarning(QT_NFC_NEARD) << "Could not connect to dbus property interface at path"
28 << interfacePath.path();
29 return;
30 }
31
32 QDBusPendingReply<QVariantMap> reply = m_dbusProperties->GetAll(QStringLiteral("org.neard.Tag"));
33 reply.waitForFinished();
34 if (reply.isError()) {
35 qCWarning(QT_NFC_NEARD) << "Could not get properties of org.neard.Tag dbus interface";
36 return;
37 }
38
39 const QString &type = reply.value().value(QStringLiteral("Type")).toString();
40 m_type = QNearFieldTarget::ProprietaryTag;
41
42 if (type == QStringLiteral("Type 1"))
43 m_type = QNearFieldTarget::NfcTagType1;
44 else if (type == QStringLiteral("Type 2"))
45 m_type = QNearFieldTarget::NfcTagType2;
46 else if (type == QStringLiteral("Type 3"))
47 m_type = QNearFieldTarget::NfcTagType3;
48 else if (type == QStringLiteral("Type 4"))
49 m_type = QNearFieldTarget::NfcTagType4;
50
51 qCDebug(QT_NFC_NEARD) << "tag type" << type;
52
53 connect(&m_recordPathsCollectedTimer, &QTimer::timeout,
54 this, &QNearFieldTargetPrivateImpl::createNdefMessage);
55 connect(&m_readErrorTimer, &QTimer::timeout,
56 this, &QNearFieldTargetPrivateImpl::handleReadError);
57 connect(&m_delayedWriteTimer, &QTimer::timeout,
58 this, &QNearFieldTargetPrivateImpl::handleWriteRequest);
59 connect(NeardHelper::instance(), &NeardHelper::recordFound,
60 this, &QNearFieldTargetPrivateImpl::handleRecordFound);
61}
62
63QNearFieldTargetPrivateImpl::~QNearFieldTargetPrivateImpl()
64{
65}
66
67bool QNearFieldTargetPrivateImpl::isValid()
68{
69 return m_dbusProperties->isValid() && NeardHelper::instance()->dbusObjectManager()->isValid();
70}
71
72QByteArray QNearFieldTargetPrivateImpl::uid() const
73{
74 return QByteArray(); // TODO figure out a workaround because neard does not offer
75 // this property
76}
77
78QNearFieldTarget::Type QNearFieldTargetPrivateImpl::type() const
79{
80 return m_type;
81}
82
83QNearFieldTarget::AccessMethods QNearFieldTargetPrivateImpl::accessMethods() const
84{
85 return QNearFieldTarget::NdefAccess;
86}
87
88bool QNearFieldTargetPrivateImpl::hasNdefMessage()
89{
90 return !m_recordPaths.isEmpty();
91}
92
93QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::readNdefMessages()
94{
95 if (isValid()) {
96 // if the user calls readNdefMessages before the previous request has been completed
97 // return the current request id.
98 if (m_currentReadRequestId.isValid())
99 return m_currentReadRequestId;
100
101 QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
102 // save the id so it can be passed along with requestCompleted
103 m_currentReadRequestId = requestId;
104 // since the triggering of interfaceAdded will ultimately lead to createNdefMessage being called
105 // we need to make sure that ndefMessagesRead will only be triggered when readNdefMessages has
106 // been called before. In case readNdefMessages is called again after that we can directly call
107 // call createNdefMessage.
108 m_readRequested = true;
109 if (hasNdefMessage())
110 createNdefMessage();
111 else
112 m_readErrorTimer.start(1000);
113
114 return requestId;
115 } else {
116 return QNearFieldTarget::RequestId();
117 }
118}
119
120QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::sendCommand(const QByteArray &command)
121{
122 Q_UNUSED(command);
123 return QNearFieldTarget::RequestId();
124}
125
126QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::writeNdefMessages(const QList<QNdefMessage> &messages)
127{
128 // disabling write due to neard crash (see QTBUG-43802)
129 qWarning(msg: "QNearFieldTarget::WriteNdefMessages() disabled. See QTBUG-43802\n");
130 return QNearFieldTarget::RequestId();
131
132
133 // return old request id when previous write request hasn't completed
134 if (m_currentWriteRequestId.isValid())
135 return m_currentWriteRequestId;
136
137 qCDebug(QT_NFC_NEARD) << "writing messages";
138 if (messages.isEmpty() || messages.first().isEmpty()) {
139 qCWarning(QT_NFC_NEARD) << "No record specified";
140 return QNearFieldTarget::RequestId();
141 }
142 if (messages.count() > 1 || messages.first().count() > 1) {
143 // neard only supports one ndef record per tag
144 qCWarning(QT_NFC_NEARD) << "Writing of only one NDEF record and message is supported";
145 return QNearFieldTarget::RequestId();
146 }
147 QNdefRecord record = messages.first().first();
148
149 if (record.typeNameFormat() == QNdefRecord::NfcRtd) {
150 m_currentWriteRequestData.clear();
151 if (record.isRecordType<QNdefNfcUriRecord>()) {
152 m_currentWriteRequestData.insert(QStringLiteral("Type"), QStringLiteral("URI"));
153 QNdefNfcUriRecord uriRecord = static_cast<QNdefNfcUriRecord>(record);
154 m_currentWriteRequestData.insert(QStringLiteral("URI"), uriRecord.uri().toString());
155 } else if (record.isRecordType<QNdefNfcSmartPosterRecord>()) {
156 m_currentWriteRequestData.insert(QStringLiteral("Type"), QStringLiteral("SmartPoster"));
157 QNdefNfcSmartPosterRecord spRecord = static_cast<QNdefNfcSmartPosterRecord>(record);
158 m_currentWriteRequestData.insert(QStringLiteral("URI"), spRecord.uri().toString());
159 // Currently neard only supports the uri property for writing
160 } else if (record.isRecordType<QNdefNfcTextRecord>()) {
161 m_currentWriteRequestData.insert(QStringLiteral("Type"), QStringLiteral("Text"));
162 QNdefNfcTextRecord textRecord = static_cast<QNdefNfcTextRecord>(record);
163 m_currentWriteRequestData.insert(QStringLiteral("Representation"), value: textRecord.text());
164 m_currentWriteRequestData.insert(QStringLiteral("Encoding"),
165 value: textRecord.encoding() == QNdefNfcTextRecord::Utf8 ?
166 QStringLiteral("UTF-8") : QStringLiteral("UTF-16") );
167 m_currentWriteRequestData.insert(QStringLiteral("Language"), value: textRecord.locale());
168 } else {
169 qCWarning(QT_NFC_NEARD) << "Record type not supported for writing";
170 return QNearFieldTarget::RequestId();
171 }
172
173 m_currentWriteRequestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
174 // trigger delayed write
175 m_delayedWriteTimer.start(100);
176
177 return m_currentWriteRequestId;
178 }
179
180 return QNearFieldTarget::RequestId();
181}
182
183QNdefRecord QNearFieldTargetPrivateImpl::readRecord(const QDBusObjectPath &path)
184{
185 qCDebug(QT_NFC_NEARD) << "reading record for path" << path.path();
186 OrgFreedesktopDBusPropertiesInterface recordInterface(QStringLiteral("org.neard"), path.path(),
187 QDBusConnection::systemBus());
188 if (!recordInterface.isValid())
189 return QNdefRecord();
190
191 QDBusPendingReply<QVariantMap> reply = recordInterface.GetAll(QStringLiteral("org.neard.Record"));
192 reply.waitForFinished();
193 if (reply.isError())
194 return QNdefRecord();
195
196 const QString &value = reply.value().value(QStringLiteral("Representation")).toString();
197 const QString &locale = reply.value().value(QStringLiteral("Language")).toString();
198 const QString &encoding = reply.value().value(QStringLiteral("Encoding")).toString();
199 const QString &uri = reply.value().value(QStringLiteral("URI")).toString();
200
201 //const QString &mime = reply.value().value(QStringLiteral("MIME")).toString();
202 //const QString &arr = reply.value().value(QStringLiteral("ARR")).toString();
203
204 const QString type = reply.value().value(QStringLiteral("Type")).toString();
205 if (type == QStringLiteral("Text")) {
206 QNdefNfcTextRecord textRecord;
207 textRecord.setText(value);
208 textRecord.setLocale(locale);
209 textRecord.setEncoding((encoding == QStringLiteral("UTF-8")) ? QNdefNfcTextRecord::Utf8
210 : QNdefNfcTextRecord::Utf16);
211 return textRecord;
212 } else if (type == QStringLiteral("SmartPoster")) {
213 QNdefNfcSmartPosterRecord spRecord;
214 if (!value.isEmpty()) {
215 spRecord.addTitle(text: value, locale, encoding: (encoding == QStringLiteral("UTF-8"))
216 ? QNdefNfcTextRecord::Utf8
217 : QNdefNfcTextRecord::Utf16);
218 }
219
220 if (!uri.isEmpty())
221 spRecord.setUri(QUrl(uri));
222
223 const QString &action = reply.value().value(QStringLiteral("Action")).toString();
224 if (!action.isEmpty()) {
225 if (action == QStringLiteral("Do"))
226 spRecord.setAction(QNdefNfcSmartPosterRecord::DoAction);
227 else if (action == QStringLiteral("Save"))
228 spRecord.setAction(QNdefNfcSmartPosterRecord::SaveAction);
229 else if (action == QStringLiteral("Edit"))
230 spRecord.setAction(QNdefNfcSmartPosterRecord::EditAction);
231 }
232
233 if (reply.value().contains(QStringLiteral("Size"))) {
234 uint size = reply.value().value(QStringLiteral("Size")).toUInt();
235 spRecord.setSize(size);
236 }
237
238 const QString &mimeType = reply.value().value(QStringLiteral("MIMEType")).toString();
239 if (!mimeType.isEmpty()) {
240 spRecord.setTypeInfo(mimeType);
241 }
242
243
244 return spRecord;
245 } else if (type == QStringLiteral("URI")) {
246 QNdefNfcUriRecord uriRecord;
247 uriRecord.setUri(QUrl(uri));
248 return uriRecord;
249 } else if (type == QStringLiteral("MIME")) {
250
251 } else if (type == QStringLiteral("AAR")) {
252
253 }
254
255 return QNdefRecord();
256}
257
258void QNearFieldTargetPrivateImpl::handleRecordFound(const QDBusObjectPath &path)
259{
260 m_recordPaths.append(t: path);
261 // FIXME: this timer only exists because neard doesn't currently supply enough
262 // information to let us know when all record interfaces have been added or
263 // how many records are actually contained on a tag. We assume that when no
264 // signal has been received for 100ms all record interfaces have been added.
265 m_recordPathsCollectedTimer.start(100);
266 // as soon as record paths have been added we can handle errors without the timer.
267 m_readErrorTimer.stop();
268}
269
270void QNearFieldTargetPrivateImpl::createNdefMessage()
271{
272 if (m_readRequested) {
273 qCDebug(QT_NFC_NEARD) << "creating Ndef message, reading" << m_recordPaths.length() << "record paths";
274 QNdefMessage newNdefMessage;
275 for (const QDBusObjectPath &recordPath : std::as_const(t&: m_recordPaths))
276 newNdefMessage.append(t: readRecord(path: recordPath));
277
278 if (!newNdefMessage.isEmpty()) {
279 QMetaObject::invokeMethod(this, "ndefMessageRead", Qt::QueuedConnection,
280 Q_ARG(QNdefMessage, newNdefMessage));
281 // the request id in requestCompleted has to match the one created in readNdefMessages
282 QMetaObject::invokeMethod(this, [this]() {
283 Q_EMIT requestCompleted(m_currentReadRequestId);
284 }, Qt::QueuedConnection);
285 } else {
286 reportError(QNearFieldTarget::UnknownError, m_currentReadRequestId);
287 }
288
289 m_readRequested = false;
290 // invalidate the current request id
291 m_currentReadRequestId = QNearFieldTarget::RequestId(0);
292 }
293}
294
295void QNearFieldTargetPrivateImpl::handleReadError()
296{
297 reportError(QNearFieldTarget::UnknownError, m_currentReadRequestId);
298 m_currentReadRequestId = QNearFieldTarget::RequestId(0);
299}
300
301void QNearFieldTargetPrivateImpl::handleWriteRequest()
302{
303 OrgNeardTagInterface tagInterface(QStringLiteral("org.neard"), m_tagPath.path(),
304 QDBusConnection::systemBus());
305 if (!tagInterface.isValid()) {
306 qCWarning(QT_NFC_NEARD) << "tag interface invalid";
307 } else {
308 QDBusPendingReply<> reply;
309 reply = tagInterface.Write(m_currentWriteRequestData);
310 reply.waitForFinished();
311 if (reply.isError()) {
312 qCWarning(QT_NFC_NEARD) << "Error writing to NFC tag" << reply.error();
313 reportError(QNearFieldTarget::UnknownError, m_currentWriteRequestId);
314 }
315
316 QMetaObject::invokeMethod(this, [this]() {
317 Q_EMIT requestCompleted(m_currentWriteRequestId);
318 }, Qt::QueuedConnection);
319 }
320
321 // invalidate current write request
322 m_currentWriteRequestId = QNearFieldTarget::RequestId(0);
323}
324
325QT_END_NAMESPACE
326

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtconnectivity/src/nfc/qnearfieldtarget_neard_p.cpp