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
devicediscoverybroadcastreceiver.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// Qt-Security score:critical reason:data-parser
5
6#include "android/devicediscoverybroadcastreceiver_p.h"
7#include <QCoreApplication>
8#include <QtCore/QtEndian>
9#include <QtCore/QLoggingCategory>
10#include <QtBluetooth/QBluetoothAddress>
11#include <QtBluetooth/QBluetoothDeviceInfo>
12#include <QtBluetooth/QBluetoothUuid>
13#include "android/jni_android_p.h"
14#include <QtCore/QHash>
15#include <QtCore/qbitarray.h>
16#include <algorithm>
17
19
21
26
29
31{
32 const qsizetype numberOfMajorDeviceClasses = 11; // count QBluetoothDeviceInfo::MajorDeviceClass values
33
34 // switch below used to ensure that we notice additions to MajorDeviceClass enum
35 const QBluetoothDeviceInfo::MajorDeviceClass classes = QBluetoothDeviceInfo::ComputerDevice;
36 switch (classes) {
37 case QBluetoothDeviceInfo::MiscellaneousDevice:
38 case QBluetoothDeviceInfo::ComputerDevice:
39 case QBluetoothDeviceInfo::PhoneDevice:
40 case QBluetoothDeviceInfo::NetworkDevice:
41 case QBluetoothDeviceInfo::AudioVideoDevice:
42 case QBluetoothDeviceInfo::PeripheralDevice:
43 case QBluetoothDeviceInfo::ImagingDevice:
44 case QBluetoothDeviceInfo::WearableDevice:
45 case QBluetoothDeviceInfo::ToyDevice:
46 case QBluetoothDeviceInfo::HealthDevice:
47 case QBluetoothDeviceInfo::UncategorizedDevice:
48 break;
49 default:
50 qCWarning(QT_BT_ANDROID) << "Unknown category of major device class:" << classes;
51 }
52
53 return QBitArray(numberOfMajorDeviceClasses, false);
54}
55
56Q_GLOBAL_STATIC_WITH_ARGS(QBitArray, initializedCacheTracker, (initializeMinorCaches()))
57
58
59// class names
60static const char javaBluetoothDeviceClassName[] = "android/bluetooth/BluetoothDevice";
61static const char javaBluetoothClassDeviceMajorClassName[] = "android/bluetooth/BluetoothClass$Device$Major";
62static const char javaBluetoothClassDeviceClassName[] = "android/bluetooth/BluetoothClass$Device";
63
64// field names device type (LE vs classic)
65static const char javaDeviceTypeClassic[] = "DEVICE_TYPE_CLASSIC";
66static const char javaDeviceTypeDual[] = "DEVICE_TYPE_DUAL";
67static const char javaDeviceTypeLE[] = "DEVICE_TYPE_LE";
68static const char javaDeviceTypeUnknown[] = "DEVICE_TYPE_UNKNOWN";
69
75static_assert(sizeof(MajorClassJavaToQtMapping) == 16);
76
78 { "AUDIO_VIDEO", QBluetoothDeviceInfo::AudioVideoDevice },
79 { "COMPUTER", QBluetoothDeviceInfo::ComputerDevice },
80 { "HEALTH", QBluetoothDeviceInfo::HealthDevice },
81 { "IMAGING", QBluetoothDeviceInfo::ImagingDevice },
82 { "MISC", QBluetoothDeviceInfo::MiscellaneousDevice },
83 { "NETWORKING", QBluetoothDeviceInfo::NetworkDevice },
84 { "PERIPHERAL", QBluetoothDeviceInfo::PeripheralDevice },
85 { "PHONE", QBluetoothDeviceInfo::PhoneDevice },
86 { "TOY", QBluetoothDeviceInfo::ToyDevice },
87 { "UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedDevice },
88 { "WEARABLE", QBluetoothDeviceInfo::WearableDevice },
89};
90
91// QBluetoothDeviceInfo::MajorDeviceClass value plus 1 matches index
92// UncategorizedDevice shifts to index 0
93static constexpr quint8 minorIndexSizes[] = {
94 64, // QBluetoothDevice::UncategorizedDevice
95 61, // QBluetoothDevice::MiscellaneousDevice
96 18, // QBluetoothDevice::ComputerDevice
97 35, // QBluetoothDevice::PhoneDevice
98 62, // QBluetoothDevice::NetworkDevice
99 0, // QBluetoothDevice::AudioVideoDevice
100 56, // QBluetoothDevice::PeripheralDevice
101 63, // QBluetoothDevice::ImagingDEvice
102 49, // QBluetoothDevice::WearableDevice
103 42, // QBluetoothDevice::ToyDevice
104 26, // QBluetoothDevice::HealthDevice
105};
106
112
114 // QBluetoothDevice::AudioVideoDevice -> 17 entries
115 { "AUDIO_VIDEO_CAMCORDER", QBluetoothDeviceInfo::Camcorder }, //index 0
116 { "AUDIO_VIDEO_CAR_AUDIO", QBluetoothDeviceInfo::CarAudio },
117 { "AUDIO_VIDEO_HANDSFREE", QBluetoothDeviceInfo::HandsFreeDevice },
118 { "AUDIO_VIDEO_HEADPHONES", QBluetoothDeviceInfo::Headphones },
119 { "AUDIO_VIDEO_HIFI_AUDIO", QBluetoothDeviceInfo::HiFiAudioDevice },
120 { "AUDIO_VIDEO_LOUDSPEAKER", QBluetoothDeviceInfo::Loudspeaker },
121 { "AUDIO_VIDEO_MICROPHONE", QBluetoothDeviceInfo::Microphone },
122 { "AUDIO_VIDEO_PORTABLE_AUDIO", QBluetoothDeviceInfo::PortableAudioDevice },
123 { "AUDIO_VIDEO_SET_TOP_BOX", QBluetoothDeviceInfo::SetTopBox },
124 { "AUDIO_VIDEO_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedAudioVideoDevice },
125 { "AUDIO_VIDEO_VCR", QBluetoothDeviceInfo::Vcr },
126 { "AUDIO_VIDEO_VIDEO_CAMERA", QBluetoothDeviceInfo::VideoCamera },
127 { "AUDIO_VIDEO_VIDEO_CONFERENCING", QBluetoothDeviceInfo::VideoConferencing },
128 { "AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER", QBluetoothDeviceInfo::VideoDisplayAndLoudspeaker },
129 { "AUDIO_VIDEO_VIDEO_GAMING_TOY", QBluetoothDeviceInfo::GamingDevice },
130 { "AUDIO_VIDEO_VIDEO_MONITOR", QBluetoothDeviceInfo::VideoMonitor },
131 { "AUDIO_VIDEO_WEARABLE_HEADSET", QBluetoothDeviceInfo::WearableHeadsetDevice },
132 { nullptr, 0 }, // separator
133
134 // QBluetoothDevice::ComputerDevice -> 7 entries
135 { "COMPUTER_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedComputer }, // index 18
136 { "COMPUTER_DESKTOP", QBluetoothDeviceInfo::DesktopComputer },
137 { "COMPUTER_HANDHELD_PC_PDA", QBluetoothDeviceInfo::HandheldComputer },
138 { "COMPUTER_LAPTOP", QBluetoothDeviceInfo::LaptopComputer },
139 { "COMPUTER_PALM_SIZE_PC_PDA", QBluetoothDeviceInfo::HandheldClamShellComputer },
140 { "COMPUTER_SERVER", QBluetoothDeviceInfo::ServerComputer },
141 { "COMPUTER_WEARABLE", QBluetoothDeviceInfo::WearableComputer },
142 { nullptr, 0 }, // separator
143
144 // QBluetoothDevice::HealthDevice -> 8 entries
145 { "HEALTH_BLOOD_PRESSURE", QBluetoothDeviceInfo::HealthBloodPressureMonitor }, // index 26
146 { "HEALTH_DATA_DISPLAY", QBluetoothDeviceInfo::HealthDataDisplay },
147 { "HEALTH_GLUCOSE", QBluetoothDeviceInfo::HealthGlucoseMeter },
148 { "HEALTH_PULSE_OXIMETER", QBluetoothDeviceInfo::HealthPulseOximeter },
149 { "HEALTH_PULSE_RATE", QBluetoothDeviceInfo::HealthStepCounter },
150 { "HEALTH_THERMOMETER", QBluetoothDeviceInfo::HealthThermometer },
151 { "HEALTH_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedHealthDevice },
152 { "HEALTH_WEIGHING", QBluetoothDeviceInfo::HealthWeightScale },
153 { nullptr, 0 }, // separator
154
155 // QBluetoothDevice::PhoneDevice -> 6 entries
156 { "PHONE_CELLULAR", QBluetoothDeviceInfo::CellularPhone }, // index 35
157 { "PHONE_CORDLESS", QBluetoothDeviceInfo::CordlessPhone },
158 { "PHONE_ISDN", QBluetoothDeviceInfo::CommonIsdnAccessPhone },
159 { "PHONE_MODEM_OR_GATEWAY", QBluetoothDeviceInfo::WiredModemOrVoiceGatewayPhone },
160 { "PHONE_SMART", QBluetoothDeviceInfo::SmartPhone },
161 { "PHONE_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedPhone },
162 { nullptr, 0 }, // separator
163
164 // QBluetoothDevice::ToyDevice -> 6 entries
165 { "TOY_CONTROLLER", QBluetoothDeviceInfo::ToyController }, // index 42
166 { "TOY_DOLL_ACTION_FIGURE", QBluetoothDeviceInfo::ToyDoll },
167 { "TOY_GAME", QBluetoothDeviceInfo::ToyGame },
168 { "TOY_ROBOT", QBluetoothDeviceInfo::ToyRobot },
169 { "TOY_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedToy },
170 { "TOY_VEHICLE", QBluetoothDeviceInfo::ToyVehicle },
171 { nullptr, 0 }, // separator
172
173 // QBluetoothDevice::WearableDevice -> 6 entries
174 { "WEARABLE_GLASSES", QBluetoothDeviceInfo::WearableGlasses }, // index 49
175 { "WEARABLE_HELMET", QBluetoothDeviceInfo::WearableHelmet },
176 { "WEARABLE_JACKET", QBluetoothDeviceInfo::WearableJacket },
177 { "WEARABLE_PAGER", QBluetoothDeviceInfo::WearablePager },
178 { "WEARABLE_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedWearableDevice },
179 { "WEARABLE_WRIST_WATCH", QBluetoothDeviceInfo::WearableWristWatch },
180 { nullptr, 0 }, // separator
181
182 // QBluetoothDevice::PeripheralDevice -> 3 entries
183 // For some reason these are not mentioned in Android docs but still exist
184 { "PERIPHERAL_NON_KEYBOARD_NON_POINTING", QBluetoothDeviceInfo::UncategorizedPeripheral }, // index 56
185 { "PERIPHERAL_KEYBOARD", QBluetoothDeviceInfo::KeyboardPeripheral },
186 { "PERIPHERAL_POINTING", QBluetoothDeviceInfo::PointingDevicePeripheral },
187 { "PERIPHERAL_KEYBOARD_POINTING", QBluetoothDeviceInfo::KeyboardWithPointingDevicePeripheral },
188 { nullptr, 0 }, // separator
189
190 // the following entries do not exist on Android
191 // we map them to the unknown minor version case
192 // QBluetoothDevice::Miscellaneous
193 { nullptr, 0 }, // index 61 & separator
194
195 // QBluetoothDevice::NetworkDevice
196 { nullptr, 0 }, // index 62 & separator
197
198 // QBluetoothDevice::ImagingDevice
199 { nullptr, 0 }, // index 63 & separator
200
201 // QBluetoothDevice::UncategorizedDevice
202 { nullptr, 0 }, // index 64 & separator
203};
204
205/*
206 Advertising Data Type (AD type) for LE scan records, as defined in
207 https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
208*/
224
226{
227 const JCachedBtTypes::iterator it = cachedBtTypes()->find(javaType);
228 if (it == cachedBtTypes()->end()) {
229
230 if (javaType == QJniObject::getStaticField<jint>(
231 javaBluetoothDeviceClassName, javaDeviceTypeClassic)) {
232 cachedBtTypes()->insert(javaType,
233 QBluetoothDeviceInfo::BaseRateCoreConfiguration);
234 return QBluetoothDeviceInfo::BaseRateCoreConfiguration;
235 } else if (javaType == QJniObject::getStaticField<jint>(
236 javaBluetoothDeviceClassName, javaDeviceTypeLE)) {
237 cachedBtTypes()->insert(javaType,
238 QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
239 return QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
240 } else if (javaType == QJniObject::getStaticField<jint>(
241 javaBluetoothDeviceClassName, javaDeviceTypeDual)) {
242 cachedBtTypes()->insert(javaType,
243 QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
244 return QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
245 } else if (javaType == QJniObject::getStaticField<jint>(
246 javaBluetoothDeviceClassName, javaDeviceTypeUnknown)) {
247 cachedBtTypes()->insert(javaType,
248 QBluetoothDeviceInfo::UnknownCoreConfiguration);
249 } else {
250 qCWarning(QT_BT_ANDROID) << "Unknown Bluetooth device type value";
251 }
252
253 return QBluetoothDeviceInfo::UnknownCoreConfiguration;
254 } else {
255 return it.value();
256 }
257}
258
260{
261 const JCachedMajorTypes::iterator it = cachedMajorTypes()->find(javaType);
262 if (it == cachedMajorTypes()->end()) {
263 QJniEnvironment env;
264 // precache all major device class fields
265 jint fieldValue;
266 QBluetoothDeviceInfo::MajorDeviceClass result = QBluetoothDeviceInfo::UncategorizedDevice;
267 auto clazz = env->FindClass(javaBluetoothClassDeviceMajorClassName);
268 for (const auto &majorMapping : majorMappings) {
269 auto fieldId = env->GetStaticFieldID(clazz, majorMapping.javaFieldName, "I");
270 if (!env->ExceptionCheck())
271 fieldValue = env->GetStaticIntField(clazz, fieldId);
272 if (env.checkAndClearExceptions()) {
273 qCWarning(QT_BT_ANDROID) << "Unknown BluetoothClass.Device.Major field" << javaType;
274
275 // add fallback value because field not readable
276 cachedMajorTypes()->insert(javaType, QBluetoothDeviceInfo::UncategorizedDevice);
277 } else {
278 cachedMajorTypes()->insert(fieldValue, majorMapping.qtMajor);
279 }
280
281 if (fieldValue == javaType)
282 result = majorMapping.qtMajor;
283 }
284
285 return result;
286 } else {
287 return it.value();
288 }
289}
290
291/*
292 The index for major into the MinorClassJavaToQtMapping and initializedCacheTracker
293 is major+1 except for UncategorizedDevice which is at index 0.
294*/
295int mappingIndexForMajor(QBluetoothDeviceInfo::MajorDeviceClass major)
296{
297 int mappingIndex = (int) major;
298 if (major == QBluetoothDeviceInfo::UncategorizedDevice)
299 mappingIndex = 0;
300 else
301 mappingIndex++;
302
303 Q_ASSERT(mappingIndex >=0
304 && mappingIndex <= (QBluetoothDeviceInfo::HealthDevice + 1));
305
306 return mappingIndex;
307}
308
309void triggerCachingOfMinorsForMajor(QBluetoothDeviceInfo::MajorDeviceClass major)
310{
311 //qCDebug(QT_BT_ANDROID) << "Caching minor values for major" << major;
312 int mappingIndex = mappingIndexForMajor(major);
313 quint8 sizeIndex = minorIndexSizes[mappingIndex];
314
315 while (minorMappings[sizeIndex].javaFieldName != nullptr) {
316 jint fieldValue = QJniObject::getStaticField<jint>(
317 javaBluetoothClassDeviceClassName, minorMappings[sizeIndex].javaFieldName);
318
319 Q_ASSERT(fieldValue >= 0);
320 cachedMinorTypes()->insert(fieldValue, minorMappings[sizeIndex].qtMinor);
321 sizeIndex++;
322 }
323
324 initializedCacheTracker()->setBit(mappingIndex);
325}
326
327quint8 resolveAndroidMinorClass(QBluetoothDeviceInfo::MajorDeviceClass major, jint javaMinor)
328{
329 // there are no minor device classes in java with value 0
330 //qCDebug(QT_BT_ANDROID) << "received minor class device:" << javaMinor;
331 if (javaMinor == 0)
332 return 0;
333
334 int mappingIndex = mappingIndexForMajor(major);
335
336 // whenever we encounter a not yet seen major device class
337 // we populate the cache with all its related minor values
338 if (!initializedCacheTracker()->at(mappingIndex))
339 triggerCachingOfMinorsForMajor(major);
340
341 const JCachedMinorTypes::iterator it = cachedMinorTypes()->find(javaMinor);
342 if (it == cachedMinorTypes()->end())
343 return 0;
344 else
345 return it.value();
346}
347
348
349DeviceDiscoveryBroadcastReceiver::DeviceDiscoveryBroadcastReceiver(QObject* parent): AndroidBroadcastReceiver(parent)
350{
351 addAction(QJniObject::fromString(
352 valueForStaticField<QtJniTypes::BluetoothDevice, JavaNames::ActionFound>()));
353 addAction(QJniObject::fromString(
354 valueForStaticField<QtJniTypes::BluetoothAdapter, JavaNames::ActionDiscoveryStarted>()));
355 addAction(QJniObject::fromString(
356 valueForStaticField<QtJniTypes::BluetoothAdapter, JavaNames::ActionDiscoveryFinished>()));
357}
358
359// Runs in Java thread
360void DeviceDiscoveryBroadcastReceiver::onReceive(JNIEnv *env, jobject context, jobject intent)
361{
362 Q_UNUSED(context)
363 Q_UNUSED(env)
364
365 QJniObject intentObject(intent);
366 const QString action = intentObject.callMethod<jstring>("getAction").toString();
367
368 qCDebug(QT_BT_ANDROID) << "DeviceDiscoveryBroadcastReceiver::onReceive() - event:" << action;
369
370 if (action == valueForStaticField<QtJniTypes::BluetoothAdapter,
371 JavaNames::ActionDiscoveryFinished>()) {
372 emit finished();
373 } else if (action == valueForStaticField<QtJniTypes::BluetoothAdapter,
374 JavaNames::ActionDiscoveryStarted>()) {
375 emit discoveryStarted();
376 } else if (action == valueForStaticField<QtJniTypes::BluetoothDevice,
377 JavaNames::ActionFound>()) {
378 //get BluetoothDevice
379 QJniObject keyExtra =
380 QJniObject::fromString(valueForStaticField<QtJniTypes::BluetoothDevice,
381 JavaNames::ExtraDevice>());
382 const QJniObject bluetoothDevice =
383 intentObject.callMethod<QtJniTypes::Parcelable>("getParcelableExtra",
384 keyExtra.object<jstring>());
385
386 if (!bluetoothDevice.isValid())
387 return;
388
389 keyExtra = QJniObject::fromString(valueForStaticField<QtJniTypes::BluetoothDevice,
390 JavaNames::ExtraRssi>());
391 int rssi = intentObject.callMethod<jshort>("getShortExtra",
392 keyExtra.object<jstring>(), jshort(0));
393
394 const QBluetoothDeviceInfo info = retrieveDeviceInfo(bluetoothDevice, rssi);
395 if (info.isValid())
396 emit deviceDiscovered(info, false);
397 }
398}
399
400// Runs in Java thread
402 JNIEnv */*env*/, jobject jBluetoothDevice, jint rssi, jbyteArray scanRecord)
403{
404 const QJniObject bluetoothDevice(jBluetoothDevice);
405 if (!bluetoothDevice.isValid())
406 return;
407
408 const QBluetoothDeviceInfo info = retrieveDeviceInfo(bluetoothDevice, rssi, scanRecord);
409 if (info.isValid())
410 emit deviceDiscovered(info, true);
411}
412
413QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(const QJniObject &bluetoothDevice, int rssi, jbyteArray scanRecord)
414{
415 const QString deviceName = bluetoothDevice.callMethod<jstring>("getName").toString();
416 const QBluetoothAddress deviceAddress(
417 bluetoothDevice.callMethod<jstring>("getAddress").toString());
418 const QJniObject bluetoothClass =
419 bluetoothDevice.callMethod<QtJniTypes::BluetoothClass>("getBluetoothClass");
420
421 if (!bluetoothClass.isValid())
422 return QBluetoothDeviceInfo();
423
424 QBluetoothDeviceInfo::MajorDeviceClass majorClass = resolveAndroidMajorClass(
425 bluetoothClass.callMethod<jint>("getMajorDeviceClass"));
426 // major device class is 5 bits from index 8 - 12
427 quint32 classType = ((quint32(majorClass) & 0x1f) << 8);
428
429 jint javaMinor = bluetoothClass.callMethod<jint>("getDeviceClass");
430 quint8 minorDeviceType = resolveAndroidMinorClass(majorClass, javaMinor);
431
432 // minor device class is 6 bits from index 2 - 7
433 classType |= ((quint32(minorDeviceType) & 0x3f) << 2);
434
435 static constexpr quint32 services[] = {
436 QBluetoothDeviceInfo::PositioningService,
437 QBluetoothDeviceInfo::NetworkingService,
438 QBluetoothDeviceInfo::RenderingService,
439 QBluetoothDeviceInfo::CapturingService,
440 QBluetoothDeviceInfo::ObjectTransferService,
441 QBluetoothDeviceInfo::AudioService,
442 QBluetoothDeviceInfo::TelephonyService,
443 QBluetoothDeviceInfo::InformationService,
444 };
445
446 // Matching BluetoothClass.Service values
447 quint32 serviceResult = 0;
448 for (quint32 current : services) {
449 int androidId = (current << 16); // Android values shift by 2 bytes compared to Qt enums
450 if (bluetoothClass.callMethod<jboolean>("hasService", androidId))
451 serviceResult |= current;
452 }
453
454 // service class info is 11 bits from index 13 - 23
455 classType |= (serviceResult << 13);
456
457 QBluetoothDeviceInfo info(deviceAddress, deviceName, classType);
458 info.setRssi(rssi);
459 QJniEnvironment env;
460 if (scanRecord != nullptr) {
461 // Parse scan record
462 jboolean isCopy;
463 jbyte *elems = env->GetByteArrayElements(scanRecord, &isCopy);
464 const char *scanRecordBuffer = reinterpret_cast<const char *>(elems);
465 const jsize scanRecordLength = env->GetArrayLength(scanRecord);
466
467 QList<QBluetoothUuid> serviceUuids;
468 jsize i = 0;
469
470 // Spec 4.2, Vol 3, Part C, Chapter 11
471 QString localName;
472 while (i < scanRecordLength) {
473 // sizeof(EIR Data) = sizeof(Length) + sizeof(EIR data Type) + sizeof(EIR Data)
474 // Length = sizeof(EIR data Type) + sizeof(EIR Data)
475
476 const int nBytes = scanRecordBuffer[i];
477 if (nBytes == 0)
478 break;
479
480 if (i >= scanRecordLength - nBytes)
481 break;
482
483 const int adType = scanRecordBuffer[i+1];
484 const char *dataPtr = &scanRecordBuffer[i+2];
485 QBluetoothUuid foundService;
486
487 switch (adType) {
488 case ADType16BitUuidIncomplete:
489 case ADType16BitUuidComplete:
490 foundService = QBluetoothUuid(qFromLittleEndian<quint16>(dataPtr));
491 break;
492 case ADType32BitUuidIncomplete:
493 case ADType32BitUuidComplete:
494 foundService = QBluetoothUuid(qFromLittleEndian<quint32>(dataPtr));
495 break;
496 case ADType128BitUuidIncomplete:
497 case ADType128BitUuidComplete:
498 foundService =
499 QBluetoothUuid(qToBigEndian<QUuid::Id128Bytes>(qFromLittleEndian<QUuid::Id128Bytes>(dataPtr)));
500 break;
502 if (nBytes >= 3) {
503 info.setServiceData(QBluetoothUuid(qFromLittleEndian<quint16>(dataPtr)),
504 QByteArray(dataPtr + 2, nBytes - 3));
505 }
506 break;
508 if (nBytes >= 5) {
509 info.setServiceData(QBluetoothUuid(qFromLittleEndian<quint32>(dataPtr)),
510 QByteArray(dataPtr + 4, nBytes - 5));
511 }
512 break;
514 if (nBytes >= 17) {
515 info.setServiceData(QBluetoothUuid(qToBigEndian<QUuid::Id128Bytes>(
516 qFromLittleEndian<QUuid::Id128Bytes>(dataPtr))),
517 QByteArray(dataPtr + 16, nBytes - 17));
518 }
519 break;
521 if (nBytes >= 3) {
522 info.setManufacturerData(qFromLittleEndian<quint16>(dataPtr),
523 QByteArray(dataPtr + 2, nBytes - 3));
524 }
525 break;
526 // According to Spec 5.0, Vol 3, Part C, Chapter 12.1
527 // the device's local name is utf8 encoded
528 case ADTypeShortenedLocalName:
529 if (localName.isEmpty())
530 localName = QString::fromUtf8(dataPtr, nBytes - 1);
531 break;
532 case ADTypeCompleteLocalName:
533 localName = QString::fromUtf8(dataPtr, nBytes - 1);
534 break;
535 default:
536 // qWarning() << "Unhandled AD Type" << Qt::hex << adType;
537 // no other types supported yet and therefore skipped
538 // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
539 break;
540 }
541
542 i += nBytes + 1;
543
544 if (!foundService.isNull() && !serviceUuids.contains(foundService))
545 serviceUuids.append(foundService);
546 }
547
548 if (info.name().isEmpty())
549 info.setName(localName);
550
551 info.setServiceUuids(serviceUuids);
552
553 env->ReleaseByteArrayElements(scanRecord, elems, JNI_ABORT);
554 }
555
556 auto methodId = env.findMethod(bluetoothDevice.objectClass(), "getType", "()I");
557 jint javaBtType = env->CallIntMethod(bluetoothDevice.object(), methodId);
558 if (!env.checkAndClearExceptions()) {
559 info.setCoreConfigurations(qtBtTypeForJavaBtType(javaBtType));
560 }
561
562 return info;
563}
564
565QT_END_NAMESPACE
virtual void onReceiveLeScan(JNIEnv *env, jobject jBluetoothDevice, jint rssi, jbyteArray scanRecord)
virtual void onReceive(JNIEnv *env, jobject context, jobject intent)
static QBitArray initializeMinorCaches()
static constexpr quint8 minorIndexSizes[]
QHash< jint, QBluetoothDeviceInfo::MajorDeviceClass > JCachedMajorTypes
static const char javaBluetoothClassDeviceClassName[]
static const char javaBluetoothClassDeviceMajorClassName[]
int mappingIndexForMajor(QBluetoothDeviceInfo::MajorDeviceClass major)
QT_BEGIN_NAMESPACE typedef QHash< jint, QBluetoothDeviceInfo::CoreConfigurations > JCachedBtTypes
static const char javaDeviceTypeLE[]
static const char javaDeviceTypeClassic[]
static const char javaDeviceTypeDual[]
QBluetoothDeviceInfo::MajorDeviceClass resolveAndroidMajorClass(jint javaType)
quint8 resolveAndroidMinorClass(QBluetoothDeviceInfo::MajorDeviceClass major, jint javaMinor)
static constexpr MajorClassJavaToQtMapping majorMappings[]
QHash< jint, quint8 > JCachedMinorTypes
static const char javaDeviceTypeUnknown[]
static const MinorClassJavaToQtMapping minorMappings[]
void triggerCachingOfMinorsForMajor(QBluetoothDeviceInfo::MajorDeviceClass major)
QBluetoothDeviceInfo::CoreConfigurations qtBtTypeForJavaBtType(jint javaType)
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher)
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
Q_GLOBAL_STATIC_WITH_ARGS(PermissionStatusHash, g_permissionStatusHash,({ { qMetaTypeId< QCameraPermission >(), Qt::PermissionStatus::Undetermined }, { qMetaTypeId< QMicrophonePermission >(), Qt::PermissionStatus::Undetermined }, { qMetaTypeId< QBluetoothPermission >(), Qt::PermissionStatus::Undetermined }, { qMetaTypeId< QContactsPermission >(), Qt::PermissionStatus::Undetermined }, { qMetaTypeId< QCalendarPermission >(), Qt::PermissionStatus::Undetermined }, { qMetaTypeId< QLocationPermission >(), Qt::PermissionStatus::Undetermined } }))
QBluetoothDeviceInfo::MajorDeviceClass qtMajor