Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qbluetoothlocaldevice_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
9#include <QCoreApplication>
10#include <QtCore/QLoggingCategory>
11#include <QtCore/QJniEnvironment>
12#include <QtCore/QJniObject>
13#include <QtBluetooth/QBluetoothLocalDevice>
14#include <QtBluetooth/QBluetoothAddress>
15
17
18Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
19
22 q_ptr(q)
23{
25
27
28 receiver = new LocalDeviceBroadcastReceiver(q_ptr);
30 this, &QBluetoothLocalDevicePrivate::processHostModeChange);
32 this, &QBluetoothLocalDevicePrivate::processPairingStateChanged);
34 this, &QBluetoothLocalDevicePrivate::processConnectDeviceChanges);
35}
36
38{
39 receiver->unregisterReceiver();
40 delete receiver;
41 delete obj;
42}
43
44QJniObject *QBluetoothLocalDevicePrivate::adapter()
45{
46 return obj;
47}
48
49void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address)
50{
52
53 if (!adapter.isValid()) {
54 qCWarning(QT_BT_ANDROID) << "Device does not support Bluetooth";
55 return;
56 }
57
59 qCWarning(QT_BT_ANDROID) << "Local device initialize() failed due to missing permissions";
60 return;
61 }
62
63 obj = new QJniObject(adapter);
64 if (!address.isNull()) {
65 const QString localAddress = obj->callMethod<jstring>("getAddress").toString();
66 if (localAddress != address.toString()) {
67 // passed address not local one -> invalid
68 delete obj;
69 obj = nullptr;
70 }
71 }
72}
73
75{
76 return obj ? true : false;
77}
78
79void QBluetoothLocalDevicePrivate::processHostModeChange(QBluetoothLocalDevice::HostMode newMode)
80{
81 qCDebug(QT_BT_ANDROID) << "Processing host mode change:" << newMode
82 << ", pending transition:" << pendingConnectableHostModeTransition;
83 if (!pendingConnectableHostModeTransition) {
84 // If host mode is not in transition -> pass data on
85 emit q_ptr->hostModeStateChanged(newMode);
86 return;
87 }
88
89 // Host mode is in transition: check if the new mode is 'off' in which state
90 // we can enter the targeted 'Connectable' state
92 const bool success = (bool)QJniObject::callStaticMethod<jboolean>(
93 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(),
94 "setEnabled");
95 if (!success) {
96 qCWarning(QT_BT_ANDROID) << "Transitioning Bluetooth from OFF to ON failed";
98 }
99 }
100 pendingConnectableHostModeTransition = false;
101}
102
103// Return -1 if address is not part of a pending pairing request
104// Otherwise it returns the index of address in pendingPairings
105int QBluetoothLocalDevicePrivate::pendingPairing(const QBluetoothAddress &address)
106{
107 for (qsizetype i = 0; i < pendingPairings.size(); ++i) {
108 if (pendingPairings.at(i).first == address)
109 return i;
110 }
111
112 return -1;
113}
114
115void QBluetoothLocalDevicePrivate::processPairingStateChanged(
117{
118 int index = pendingPairing(address);
119
120 if (index < 0)
121 return; // ignore unrelated pairing signals
122
123 QPair<QBluetoothAddress, bool> entry = pendingPairings.takeAt(index);
124 if ((entry.second && pairing == QBluetoothLocalDevice::Paired)
125 || (!entry.second && pairing == QBluetoothLocalDevice::Unpaired)) {
126 emit q_ptr->pairingFinished(address, pairing);
127 } else {
129 }
130}
131
132void QBluetoothLocalDevicePrivate::processConnectDeviceChanges(const QBluetoothAddress &address,
133 bool isConnectEvent)
134{
135 if (isConnectEvent) { // connect event
136 if (connectedDevices.contains(address))
137 return;
138 connectedDevices.append(address);
140 } else { // disconnect event
141 connectedDevices.removeAll(address);
143 }
144}
145
151
157
159{
160 if (d_ptr->adapter())
161 return d_ptr->adapter()->callMethod<jstring>("getName").toString();
162
163 return QString();
164}
165
167{
169 if (d_ptr->adapter())
170 result = d_ptr->adapter()->callMethod<jstring>("getAddress").toString();
171
173 return address;
174}
175
177{
178 if (hostMode() != HostPoweredOff)
179 return;
180
181 if (d_ptr->adapter()) {
182 bool success(false);
183 if (QNativeInterface::QAndroidApplication::sdkVersion() >= 31) {
184 success = (bool)QJniObject::callStaticMethod<jboolean>(
185 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(),
186 "setEnabled");
187 } else {
188 success = (bool)d_ptr->adapter()->callMethod<jboolean>("enable");
189 }
190 if (!success) {
191 qCWarning(QT_BT_ANDROID) << "Enabling bluetooth failed";
193 }
194 }
195}
196
198{
199 QBluetoothLocalDevice::HostMode nextMode = requestedMode;
200 if (requestedMode == HostDiscoverableLimitedInquiry)
201 nextMode = HostDiscoverable;
202
203 if (nextMode == hostMode())
204 return;
205
206 switch (nextMode) {
207
209 bool success = false;
210 if (d_ptr->adapter()) {
211 if (QNativeInterface::QAndroidApplication::sdkVersion() >= 31) {
212 success = (bool)QJniObject::callStaticMethod<jboolean>(
213 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(),
214 "setDisabled");
215 } else {
216 success = (bool)d_ptr->adapter()->callMethod<jboolean>("disable");
217 }
218 }
219 if (!success) {
220 qCWarning(QT_BT_ANDROID) << "Unable to power off the adapter";
222 }
223 break;
224 }
225
228 // On Android 'Discoverable' is actually 'CONNECTABLE_DISCOVERABLE', and
229 // it seems we cannot go directly from "Discoverable" to "Connectable". Instead
230 // we need to go to disabled mode first and then to the 'Connectable' mode
232 d_ptr->pendingConnectableHostModeTransition = true;
233 } else {
234 const bool success = (bool)QJniObject::callStaticMethod<jboolean>(
235 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(),
236 "setEnabled");
237 if (!success) {
238 qCWarning(QT_BT_ANDROID) << "Unable to enable the Bluetooth";
240 }
241 }
242 break;
243 }
244
247 qCWarning(QT_BT_ANDROID) << "Local device setHostMode() failed due to "
248 "missing permissions";
250 return;
251 }
252 const bool success = (bool)QJniObject::callStaticMethod<jboolean>(
253 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(),
254 "setDiscoverable");
255 if (!success) {
256 qCWarning(QT_BT_ANDROID) << "Unable to set Bluetooth as discoverable";
258 }
259 break;
260 }
261 default:
262 qCWarning(QT_BT_ANDROID) << "setHostMode() unsupported host mode:" << nextMode;
263 break;
264 }
265}
266
268{
269 if (d_ptr->adapter()) {
270 jint scanMode = d_ptr->adapter()->callMethod<jint>("getScanMode");
271
272 switch (scanMode) {
273 case 20: // BluetoothAdapter.SCAN_MODE_NONE
274 return HostPoweredOff;
275 case 21: // BluetoothAdapter.SCAN_MODE_CONNECTABLE
276 return HostConnectable;
277 case 23: // BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
278 return HostDiscoverable;
279 default:
280 break;
281 }
282 }
283
284 return HostPoweredOff;
285}
286
287QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
288{
289 // As a static class function we need to ensure permissions here (in addition to initialize())
291 qCWarning(QT_BT_ANDROID) << "Local device allDevices() failed due to "
292 "missing permissions";
293 return {};
294 }
295 // Android only supports max of one device (so far)
296 QList<QBluetoothHostInfo> localDevices;
297
299 if (o.isValid()) {
301 info.setName(o.callMethod<jstring>("getName").toString());
302 info.setAddress(QBluetoothAddress(o.callMethod<jstring>("getAddress").toString()));
303 localDevices.append(info);
304 }
305 return localDevices;
306}
307
309{
310 if (address.isNull()) {
311 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
314 return;
315 }
316
317 const Pairing previousPairing = pairingStatus(address);
318 Pairing newPairing = pairing;
319 if (pairing == AuthorizedPaired) // AuthorizedPaired same as Paired on Android
320 newPairing = Paired;
321
322 if (previousPairing == newPairing) {
323 QMetaObject::invokeMethod(this, "pairingFinished", Qt::QueuedConnection,
326 return;
327 }
328
329 if (!d_ptr->adapter()) {
330 qCWarning(QT_BT_ANDROID) << "Unable to pair, invalid adapter";
331 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
334 return;
335 }
336
337 QJniObject inputString = QJniObject::fromString(address.toString());
338 jboolean success = QJniObject::callStaticMethod<jboolean>(
339 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(),
340 "setPairingMode",
341 inputString.object<jstring>(),
342 jboolean(newPairing == Paired ? JNI_TRUE : JNI_FALSE));
343
344 if (!success) {
345 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
348 } else {
349 d_ptr->pendingPairings.append(qMakePair(address, newPairing == Paired ? true : false));
350 }
351}
352
354 const QBluetoothAddress &address) const
355{
356 if (address.isNull() || !d_ptr->adapter())
357 return Unpaired;
358
359 QJniObject inputString = QJniObject::fromString(address.toString());
360 QJniObject remoteDevice
361 = d_ptr->adapter()->callMethod<QtJniTypes::BluetoothDevice>("getRemoteDevice",
362 inputString.object<jstring>());
363
364 if (!remoteDevice.isValid())
365 return Unpaired;
366
367 jint bondState = remoteDevice.callMethod<jint>("getBondState");
368 switch (bondState) {
369 case 12: // BluetoothDevice.BOND_BONDED
370 return Paired;
371 default:
372 break;
373 }
374
375 return Unpaired;
376}
377
378QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
379{
380 /*
381 * Android does not have an API to list all connected devices. We have to collect
382 * the information based on a few indicators.
383 *
384 * Primarily we detect connected devices by monitoring connect/disconnect signals.
385 * Unfortunately the list may only be complete after very long monitoring time.
386 * However there are some Android APIs which provide the list of connected devices
387 * for specific Bluetooth profiles. QtBluetoothBroadcastReceiver.getConnectedDevices()
388 * returns a few connections of common profiles. The returned list is not complete either
389 * but at least it can complement our already detected connections.
390 */
391 QJniObject connectedDevices = QJniObject::callStaticMethod<QtJniTypes::StringArray>(
392 QtJniTypes::Traits<QtJniTypes::QtBtBroadcastReceiver>::className(), "getConnectedDevices");
393
394 if (!connectedDevices.isValid())
395 return d_ptr->connectedDevices;
396
397 jobjectArray connectedDevicesArray = connectedDevices.object<jobjectArray>();
398 if (!connectedDevicesArray)
399 return d_ptr->connectedDevices;
400
401 QJniEnvironment env;
402 QList<QBluetoothAddress> knownAddresses = d_ptr->connectedDevices;
404
405 jint size = env->GetArrayLength(connectedDevicesArray);
406 for (int i = 0; i < size; i++) {
407 p = env->GetObjectArrayElement(connectedDevicesArray, i);
408 QBluetoothAddress address(p.toString());
409 if (!address.isNull() && !knownAddresses.contains(address))
410 knownAddresses.append(address);
411 }
412
413 return knownAddresses;
414}
415
QJniObject getDefaultBluetoothAdapter()
QT_BEGIN_NAMESPACE bool ensureAndroidPermission(QBluetoothPermission::CommunicationModes modes)
void pairingStateChanged(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
void connectDeviceChanges(const QBluetoothAddress &address, bool isConnectEvent)
void hostModeStateChanged(QBluetoothLocalDevice::HostMode state)
\inmodule QtBluetooth
\inmodule QtBluetooth
\inmodule QtBluetooth
void powerOn()
Powers on the device after returning it to the hostMode() state, if it was powered off.
Pairing
This enum describes the pairing state between the two Bluetooth devices.
void errorOccurred(QBluetoothLocalDevice::Error error)
Signal emitted if there's an exceptional error while pairing.
void requestPairing(const QBluetoothAddress &address, Pairing pairing)
Set the pairing status with address.
HostMode
This enum describes the most of the local Bluetooth device.
HostMode hostMode() const
Returns the current host mode of this local Bluetooth device.
void deviceConnected(const QBluetoothAddress &address)
Error
This enum describes errors that maybe returned.
QList< QBluetoothAddress > connectedDevices() const
QBluetoothLocalDevice(QObject *parent=nullptr)
Constructs a QBluetoothLocalDevice with parent.
QString name() const
Returns the name assgined by the user to this Bluetooth device.
static QList< QBluetoothHostInfo > allDevices()
Returns a list of all available local Bluetooth devices.
Pairing pairingStatus(const QBluetoothAddress &address) const
Returns the current bluetooth pairing status of address, if it's unpaired, paired,...
QBluetoothAddress address() const
Returns the MAC address of this Bluetooth device.
void deviceDisconnected(const QBluetoothAddress &address)
void pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
Pairing or unpairing has completed with address.
void setHostMode(QBluetoothLocalDevice::HostMode mode)
Sets the host mode of this local Bluetooth device to mode.
void hostModeStateChanged(QBluetoothLocalDevice::HostMode state)
The state of the host has transitioned to a different HostMode.
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qobject.h:103
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
#define this
Definition dialogs.cpp:9
Combined button and popup list for selecting options.
@ QueuedConnection
void registerQBluetoothLocalDeviceMetaType()
static bool initialize()
Definition qctf.cpp:94
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLhandleARB obj
[2]
GLuint entry
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QHostInfo info
[0]
char * toString(const MyType &t)
[31]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...