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