summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth/android/devicediscoverybroadcastreceiver.cpp')
-rw-r--r--src/bluetooth/android/devicediscoverybroadcastreceiver.cpp397
1 files changed, 382 insertions, 15 deletions
diff --git a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
index a3d17b90..9c9c0409 100644
--- a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
+++ b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
@@ -39,12 +39,16 @@
****************************************************************************/
#include "android/devicediscoverybroadcastreceiver_p.h"
+#include <QtCore/QtEndian>
#include <QtCore/QLoggingCategory>
#include <QtBluetooth/QBluetoothAddress>
#include <QtBluetooth/QBluetoothDeviceInfo>
+#include <QtBluetooth/QBluetoothUuid>
#include "android/jni_android_p.h"
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/QHash>
+#include <QtCore/qbitarray.h>
+#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -52,14 +56,208 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
typedef QHash<jint, QBluetoothDeviceInfo::CoreConfigurations> JCachedBtTypes;
Q_GLOBAL_STATIC(JCachedBtTypes, cachedBtTypes)
+typedef QHash<jint, QBluetoothDeviceInfo::MajorDeviceClass> JCachedMajorTypes;
+Q_GLOBAL_STATIC(JCachedMajorTypes, cachedMajorTypes)
-// class name
+typedef QHash<jint, quint8> JCachedMinorTypes;
+Q_GLOBAL_STATIC(JCachedMinorTypes, cachedMinorTypes)
+
+static QBitArray initializeMinorCaches()
+{
+ const int numberOfMajorDeviceClasses = 11; // count QBluetoothDeviceInfo::MajorDeviceClass values
+
+ // switch below used to ensure that we notice additions to MajorDeviceClass enum
+ const QBluetoothDeviceInfo::MajorDeviceClass classes = QBluetoothDeviceInfo::ComputerDevice;
+ switch (classes) {
+ case QBluetoothDeviceInfo::MiscellaneousDevice:
+ case QBluetoothDeviceInfo::ComputerDevice:
+ case QBluetoothDeviceInfo::PhoneDevice:
+ case QBluetoothDeviceInfo::LANAccessDevice:
+ case QBluetoothDeviceInfo::AudioVideoDevice:
+ case QBluetoothDeviceInfo::PeripheralDevice:
+ case QBluetoothDeviceInfo::ImagingDevice:
+ case QBluetoothDeviceInfo::WearableDevice:
+ case QBluetoothDeviceInfo::ToyDevice:
+ case QBluetoothDeviceInfo::HealthDevice:
+ case QBluetoothDeviceInfo::UncategorizedDevice:
+ break;
+ default:
+ qCWarning(QT_BT_ANDROID) << "Unknown category of major device class:" << classes;
+ }
+
+ return QBitArray(numberOfMajorDeviceClasses, false);
+}
+
+Q_GLOBAL_STATIC_WITH_ARGS(QBitArray, initializedCacheTracker, (initializeMinorCaches()))
+
+
+// class names
static const char * const javaBluetoothDeviceClassName = "android/bluetooth/BluetoothDevice";
+static const char * const javaBluetoothClassDeviceMajorClassName = "android/bluetooth/BluetoothClass$Device$Major";
+static const char * const javaBluetoothClassDeviceClassName = "android/bluetooth/BluetoothClass$Device";
+
+// field names device type (LE vs classic)
static const char * const javaDeviceTypeClassic = "DEVICE_TYPE_CLASSIC";
static const char * const javaDeviceTypeDual = "DEVICE_TYPE_DUAL";
static const char * const javaDeviceTypeLE = "DEVICE_TYPE_LE";
static const char * const javaDeviceTypeUnknown = "DEVICE_TYPE_UNKNOWN";
+struct MajorClassJavaToQtMapping
+{
+ char const * javaFieldName;
+ QBluetoothDeviceInfo::MajorDeviceClass qtMajor;
+};
+
+static const MajorClassJavaToQtMapping majorMappings[] = {
+ { "AUDIO_VIDEO", QBluetoothDeviceInfo::AudioVideoDevice },
+ { "COMPUTER", QBluetoothDeviceInfo::ComputerDevice },
+ { "HEALTH", QBluetoothDeviceInfo::HealthDevice },
+ { "IMAGING", QBluetoothDeviceInfo::ImagingDevice },
+ { "MISC", QBluetoothDeviceInfo::MiscellaneousDevice },
+ { "NETWORKING", QBluetoothDeviceInfo::LANAccessDevice },
+ { "PERIPHERAL", QBluetoothDeviceInfo::PeripheralDevice },
+ { "PHONE", QBluetoothDeviceInfo::PhoneDevice },
+ { "TOY", QBluetoothDeviceInfo::ToyDevice },
+ { "UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedDevice },
+ { "WEARABLE", QBluetoothDeviceInfo::WearableDevice },
+ { Q_NULLPTR, QBluetoothDeviceInfo::UncategorizedDevice } //end of list
+};
+
+// QBluetoothDeviceInfo::MajorDeviceClass value plus 1 matches index
+// UncategorizedDevice shifts to index 0
+static const int minorIndexSizes[] = {
+ 64, // QBluetoothDevice::UncategorizedDevice
+ 61, // QBluetoothDevice::MiscellaneousDevice
+ 18, // QBluetoothDevice::ComputerDevice
+ 35, // QBluetoothDevice::PhoneDevice
+ 62, // QBluetoothDevice::LANAccessDevice
+ 0, // QBluetoothDevice::AudioVideoDevice
+ 56, // QBluetoothDevice::PeripheralDevice
+ 63, // QBluetoothDevice::ImagingDEvice
+ 49, // QBluetoothDevice::WearableDevice
+ 42, // QBluetoothDevice::ToyDevice
+ 26, // QBluetoothDevice::HealthDevice
+};
+
+struct MinorClassJavaToQtMapping
+{
+ char const * javaFieldName;
+ quint8 qtMinor;
+};
+
+static const MinorClassJavaToQtMapping minorMappings[] = {
+ // QBluetoothDevice::AudioVideoDevice -> 17 entries
+ { "AUDIO_VIDEO_CAMCORDER", QBluetoothDeviceInfo::Camcorder }, //index 0
+ { "AUDIO_VIDEO_CAR_AUDIO", QBluetoothDeviceInfo::CarAudio },
+ { "AUDIO_VIDEO_HANDSFREE", QBluetoothDeviceInfo::HandsFreeDevice },
+ { "AUDIO_VIDEO_HEADPHONES", QBluetoothDeviceInfo::Headphones },
+ { "AUDIO_VIDEO_HIFI_AUDIO", QBluetoothDeviceInfo::HiFiAudioDevice },
+ { "AUDIO_VIDEO_LOUDSPEAKER", QBluetoothDeviceInfo::Loudspeaker },
+ { "AUDIO_VIDEO_MICROPHONE", QBluetoothDeviceInfo::Microphone },
+ { "AUDIO_VIDEO_PORTABLE_AUDIO", QBluetoothDeviceInfo::PortableAudioDevice },
+ { "AUDIO_VIDEO_SET_TOP_BOX", QBluetoothDeviceInfo::SetTopBox },
+ { "AUDIO_VIDEO_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedAudioVideoDevice },
+ { "AUDIO_VIDEO_VCR", QBluetoothDeviceInfo::Vcr },
+ { "AUDIO_VIDEO_VIDEO_CAMERA", QBluetoothDeviceInfo::VideoCamera },
+ { "AUDIO_VIDEO_VIDEO_CONFERENCING", QBluetoothDeviceInfo::VideoConferencing },
+ { "AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER", QBluetoothDeviceInfo::VideoDisplayAndLoudspeaker },
+ { "AUDIO_VIDEO_VIDEO_GAMING_TOY", QBluetoothDeviceInfo::GamingDevice },
+ { "AUDIO_VIDEO_VIDEO_MONITOR", QBluetoothDeviceInfo::VideoMonitor },
+ { "AUDIO_VIDEO_WEARABLE_HEADSET", QBluetoothDeviceInfo::WearableHeadsetDevice },
+ { Q_NULLPTR, 0 }, // separator
+
+ // QBluetoothDevice::ComputerDevice -> 7 entries
+ { "COMPUTER_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedComputer }, // index 18
+ { "COMPUTER_DESKTOP", QBluetoothDeviceInfo::DesktopComputer },
+ { "COMPUTER_HANDHELD_PC_PDA", QBluetoothDeviceInfo::HandheldComputer },
+ { "COMPUTER_LAPTOP", QBluetoothDeviceInfo::LaptopComputer },
+ { "COMPUTER_PALM_SIZE_PC_PDA", QBluetoothDeviceInfo::HandheldClamShellComputer },
+ { "COMPUTER_SERVER", QBluetoothDeviceInfo::ServerComputer },
+ { "COMPUTER_WEARABLE", QBluetoothDeviceInfo::WearableComputer },
+ { Q_NULLPTR, 0 }, // separator
+
+ // QBluetoothDevice::HealthDevice -> 8 entries
+ { "HEALTH_BLOOD_PRESSURE", QBluetoothDeviceInfo::HealthBloodPressureMonitor }, // index 26
+ { "HEALTH_DATA_DISPLAY", QBluetoothDeviceInfo::HealthDataDisplay },
+ { "HEALTH_GLUCOSE", QBluetoothDeviceInfo::HealthGlucoseMeter },
+ { "HEALTH_PULSE_OXIMETER", QBluetoothDeviceInfo::HealthPulseOximeter },
+ { "HEALTH_PULSE_RATE", QBluetoothDeviceInfo::HealthStepCounter },
+ { "HEALTH_THERMOMETER", QBluetoothDeviceInfo::HealthThermometer },
+ { "HEALTH_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedHealthDevice },
+ { "HEALTH_WEIGHING", QBluetoothDeviceInfo::HealthWeightScale },
+ { Q_NULLPTR, 0 }, // separator
+
+ // QBluetoothDevice::PhoneDevice -> 6 entries
+ { "PHONE_CELLULAR", QBluetoothDeviceInfo::CellularPhone }, // index 35
+ { "PHONE_CORDLESS", QBluetoothDeviceInfo::CordlessPhone },
+ { "PHONE_ISDN", QBluetoothDeviceInfo::CommonIsdnAccessPhone },
+ { "PHONE_MODEM_OR_GATEWAY", QBluetoothDeviceInfo::WiredModemOrVoiceGatewayPhone },
+ { "PHONE_SMART", QBluetoothDeviceInfo::SmartPhone },
+ { "PHONE_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedPhone },
+ { Q_NULLPTR, 0 }, // separator
+
+ // QBluetoothDevice::ToyDevice -> 6 entries
+ { "TOY_CONTROLLER", QBluetoothDeviceInfo::ToyController }, // index 42
+ { "TOY_DOLL_ACTION_FIGURE", QBluetoothDeviceInfo::ToyDoll },
+ { "TOY_GAME", QBluetoothDeviceInfo::ToyGame },
+ { "TOY_ROBOT", QBluetoothDeviceInfo::ToyRobot },
+ { "TOY_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedToy },
+ { "TOY_VEHICLE", QBluetoothDeviceInfo::ToyVehicle },
+ { Q_NULLPTR, 0 }, // separator
+
+ // QBluetoothDevice::WearableDevice -> 6 entries
+ { "WEARABLE_GLASSES", QBluetoothDeviceInfo::WearableGlasses }, // index 49
+ { "WEARABLE_HELMET", QBluetoothDeviceInfo::WearableHelmet },
+ { "WEARABLE_JACKET", QBluetoothDeviceInfo::WearableJacket },
+ { "WEARABLE_PAGER", QBluetoothDeviceInfo::WearablePager },
+ { "WEARABLE_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedWearableDevice },
+ { "WEARABLE_WRIST_WATCH", QBluetoothDeviceInfo::WearableWristWatch },
+ { Q_NULLPTR, 0 }, // separator
+
+ // QBluetoothDevice::PeripheralDevice -> 3 entries
+ // For some reason these are not mentioned in Android docs but still exist
+ { "PERIPHERAL_NON_KEYBOARD_NON_POINTING", QBluetoothDeviceInfo::UncategorizedPeripheral }, // index 56
+ { "PERIPHERAL_KEYBOARD", QBluetoothDeviceInfo::KeyboardPeripheral },
+ { "PERIPHERAL_POINTING", QBluetoothDeviceInfo::PointingDevicePeripheral },
+ { "PERIPHERAL_KEYBOARD_POINTING", QBluetoothDeviceInfo::KeyboardWithPointingDevicePeripheral },
+ { Q_NULLPTR, 0 }, // separator
+
+ // the following entries do not exist on Android
+ // we map them to the unknown minor version case
+ // QBluetoothDevice::Miscellaneous
+ { Q_NULLPTR, 0 }, // index 61 & separator
+
+ // QBluetoothDevice::LANAccessDevice
+ { Q_NULLPTR, 0 }, // index 62 & separator
+
+ // QBluetoothDevice::ImagingDevice
+ { Q_NULLPTR, 0 }, // index 63 & separator
+
+ // QBluetoothDevice::UncategorizedDevice
+ { Q_NULLPTR, 0 }, // index 64 & separator
+};
+
+/*! Advertising Data Type (AD type) for LE scan records, as defined in Bluetooth CSS v6. */
+enum ADType {
+ ADType16BitUuidIncomplete = 0x02,
+ ADType16BitUuidComplete = 0x03,
+ ADType32BitUuidIncomplete = 0x04,
+ ADType32BitUuidComplete = 0x05,
+ ADType128BitUuidIncomplete = 0x06,
+ ADType128BitUuidComplete = 0x07,
+ // .. more will be added when required
+};
+
+// Endianness conversion for quint128 doesn't (yet) exist in qtendian.h
+template <>
+inline quint128 qbswap<quint128>(const quint128 src)
+{
+ quint128 dst;
+ for (int i = 0; i < 16; i++)
+ dst.data[i] = src.data[15 - i];
+ return dst;
+}
+
QBluetoothDeviceInfo::CoreConfigurations qtBtTypeForJavaBtType(jint javaType)
{
const JCachedBtTypes::iterator it = cachedBtTypes()->find(javaType);
@@ -86,6 +284,10 @@ QBluetoothDeviceInfo::CoreConfigurations qtBtTypeForJavaBtType(jint javaType)
cachedBtTypes()->insert(javaType,
QBluetoothDeviceInfo::UnknownCoreConfiguration);
} else {
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
qCWarning(QT_BT_ANDROID) << "Unknown Bluetooth device type value";
}
@@ -95,6 +297,106 @@ QBluetoothDeviceInfo::CoreConfigurations qtBtTypeForJavaBtType(jint javaType)
}
}
+QBluetoothDeviceInfo::MajorDeviceClass resolveAndroidMajorClass(jint javaType)
+{
+ QAndroidJniEnvironment env;
+
+ const JCachedMajorTypes::iterator it = cachedMajorTypes()->find(javaType);
+ if (it == cachedMajorTypes()->end()) {
+ QAndroidJniEnvironment env;
+ // precache all major device class fields
+ int i = 0;
+ jint fieldValue;
+ QBluetoothDeviceInfo::MajorDeviceClass result = QBluetoothDeviceInfo::UncategorizedDevice;
+ while (majorMappings[i].javaFieldName != Q_NULLPTR) {
+ fieldValue = QAndroidJniObject::getStaticField<jint>(
+ javaBluetoothClassDeviceMajorClassName, majorMappings[i].javaFieldName);
+ if (env->ExceptionCheck()) {
+ qCWarning(QT_BT_ANDROID) << "Unknown BluetoothClass.Device.Major field" << javaType;
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ // add fallback value because field not readable
+ cachedMajorTypes()->insert(javaType, QBluetoothDeviceInfo::UncategorizedDevice);
+ } else {
+ cachedMajorTypes()->insert(fieldValue, majorMappings[i].qtMajor);
+ }
+
+ if (fieldValue == javaType)
+ result = majorMappings[i].qtMajor;
+
+ i++;
+ }
+
+ return result;
+ } else {
+ return it.value();
+ }
+}
+
+/*
+ The index for major into the MinorClassJavaToQtMapping and initializedCacheTracker
+ is major+1 except for UncategorizedDevice which is at index 0.
+*/
+int mappingIndexForMajor(QBluetoothDeviceInfo::MajorDeviceClass major)
+{
+ int mappingIndex = (int) major;
+ if (major == QBluetoothDeviceInfo::UncategorizedDevice)
+ mappingIndex = 0;
+ else
+ mappingIndex++;
+
+ Q_ASSERT(mappingIndex >=0
+ && mappingIndex <= (QBluetoothDeviceInfo::HealthDevice + 1));
+
+ return mappingIndex;
+}
+
+void triggerCachingOfMinorsForMajor(QBluetoothDeviceInfo::MajorDeviceClass major)
+{
+ //qCDebug(QT_BT_ANDROID) << "Caching minor values for major" << major;
+ int mappingIndex = mappingIndexForMajor(major);
+ int sizeIndex = minorIndexSizes[mappingIndex];
+ QAndroidJniEnvironment env;
+
+ while (minorMappings[sizeIndex].javaFieldName != Q_NULLPTR) {
+ jint fieldValue = QAndroidJniObject::getStaticField<jint>(
+ javaBluetoothClassDeviceClassName, minorMappings[sizeIndex].javaFieldName);
+ if (env->ExceptionCheck()) { // field lookup failed? skip it
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+
+ Q_ASSERT(fieldValue >= 0);
+ cachedMinorTypes()->insert(fieldValue, minorMappings[sizeIndex].qtMinor);
+ sizeIndex++;
+ }
+
+ initializedCacheTracker()->setBit(mappingIndex);
+}
+
+quint8 resolveAndroidMinorClass(QBluetoothDeviceInfo::MajorDeviceClass major, jint javaMinor)
+{
+ // there are no minor device classes in java with value 0
+ //qCDebug(QT_BT_ANDROID) << "received minor class device:" << javaMinor;
+ if (javaMinor == 0)
+ return 0;
+
+ int mappingIndex = mappingIndexForMajor(major);
+
+ // whenever we encounter a not yet seen major device class
+ // we populate the cache with all its related minor values
+ if (!initializedCacheTracker()->at(mappingIndex))
+ triggerCachingOfMinorsForMajor(major);
+
+ const JCachedMinorTypes::iterator it = cachedMinorTypes()->find(javaMinor);
+ if (it == cachedMinorTypes()->end())
+ return 0;
+ else
+ return it.value();
+}
+
+
DeviceDiscoveryBroadcastReceiver::DeviceDiscoveryBroadcastReceiver(QObject* parent): AndroidBroadcastReceiver(parent)
{
addAction(valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ActionFound));
@@ -147,19 +449,18 @@ void DeviceDiscoveryBroadcastReceiver::onReceive(JNIEnv *env, jobject context, j
// Runs in Java thread
void DeviceDiscoveryBroadcastReceiver::onReceiveLeScan(
- JNIEnv *env, jobject jBluetoothDevice, jint rssi)
+ JNIEnv *env, jobject jBluetoothDevice, jint rssi, jbyteArray scanRecord)
{
- qCDebug(QT_BT_ANDROID) << "DeviceDiscoveryBroadcastReceiver::onReceiveLeScan()";
const QAndroidJniObject bluetoothDevice(jBluetoothDevice);
if (!bluetoothDevice.isValid())
return;
- const QBluetoothDeviceInfo info = retrieveDeviceInfo(env, bluetoothDevice, rssi);
+ const QBluetoothDeviceInfo info = retrieveDeviceInfo(env, bluetoothDevice, rssi, scanRecord);
if (info.isValid())
emit deviceDiscovered(info, true);
}
-QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv *env, const QAndroidJniObject &bluetoothDevice, int rssi)
+QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv *env, const QAndroidJniObject &bluetoothDevice, int rssi, jbyteArray scanRecord)
{
const QString deviceName = bluetoothDevice.callObjectMethod<jstring>("getName").toString();
const QBluetoothAddress deviceAddress(bluetoothDevice.callObjectMethod<jstring>("getAddress").toString());
@@ -168,10 +469,19 @@ QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv
"()Landroid/bluetooth/BluetoothClass;");
if (!bluetoothClass.isValid())
return QBluetoothDeviceInfo();
- int classType = bluetoothClass.callMethod<jint>("getDeviceClass");
+ QBluetoothDeviceInfo::MajorDeviceClass majorClass = resolveAndroidMajorClass(
+ bluetoothClass.callMethod<jint>("getMajorDeviceClass"));
+ // major device class is 5 bits from index 8 - 12
+ quint32 classType = ((quint32(majorClass) & 0x1f) << 8);
- static QList<qint32> services;
+ jint javaMinor = bluetoothClass.callMethod<jint>("getDeviceClass");
+ quint8 minorDeviceType = resolveAndroidMinorClass(majorClass, javaMinor);
+
+ // minor device class is 6 bits from index 2 - 7
+ classType |= ((quint32(minorDeviceType) & 0x3f) << 2);
+
+ static QList<quint32> services;
if (services.count() == 0)
services << QBluetoothDeviceInfo::PositioningService
<< QBluetoothDeviceInfo::NetworkingService
@@ -182,22 +492,79 @@ QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv
<< QBluetoothDeviceInfo::TelephonyService
<< QBluetoothDeviceInfo::InformationService;
- //Matching BluetoothClass.Service values
- qint32 result = 0;
- qint32 current = 0;
+ // Matching BluetoothClass.Service values
+ quint32 serviceResult = 0;
+ quint32 current = 0;
for (int i = 0; i < services.count(); i++) {
current = services.at(i);
- int id = (current << 16);
- if (bluetoothClass.callMethod<jboolean>("hasService", "(I)Z", id))
- result |= current;
+ int androidId = (current << 16); // Android values shift by 2 bytes compared to Qt enums
+ if (bluetoothClass.callMethod<jboolean>("hasService", "(I)Z", androidId))
+ serviceResult |= current;
}
- result = result << 13;
- classType |= result;
+ // service class info is 11 bits from index 13 - 23
+ classType |= (serviceResult << 13);
QBluetoothDeviceInfo info(deviceAddress, deviceName, classType);
info.setRssi(rssi);
+ if (scanRecord != nullptr) {
+ // Parse scan record
+ jboolean isCopy;
+ jbyte *elems = env->GetByteArrayElements(scanRecord, &isCopy);
+ const char *scanRecordBuffer = reinterpret_cast<const char *>(elems);
+ const int scanRecordLength = env->GetArrayLength(scanRecord);
+
+ QList<QBluetoothUuid> serviceUuids;
+ int i = 0;
+
+ // Spec 4.2, Vol 3, Part C, Chapter 11
+ while (i < scanRecordLength) {
+ // sizeof(EIR Data) = sizeof(Length) + sizeof(EIR data Type) + sizeof(EIR Data)
+ // Length = sizeof(EIR data Type) + sizeof(EIR Data)
+
+ const int nBytes = scanRecordBuffer[i];
+ if (nBytes == 0)
+ break;
+
+ if ((i + nBytes) >= scanRecordLength)
+ break;
+
+ const int adType = scanRecordBuffer[i+1];
+ const char *dataPtr = &scanRecordBuffer[i+2];
+ QBluetoothUuid foundService;
+
+ switch (adType) {
+ case ADType16BitUuidIncomplete:
+ case ADType16BitUuidComplete:
+ foundService = QBluetoothUuid(qFromLittleEndian<quint16>(dataPtr));
+ break;
+ case ADType32BitUuidIncomplete:
+ case ADType32BitUuidComplete:
+ foundService = QBluetoothUuid(qFromLittleEndian<quint32>(dataPtr));
+ break;
+ case ADType128BitUuidIncomplete:
+ case ADType128BitUuidComplete:
+ foundService =
+ QBluetoothUuid(qToBigEndian<quint128>(qFromLittleEndian<quint128>(dataPtr)));
+ break;
+ default:
+ // no other types supported yet and therefore skipped
+ // https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
+ break;
+ }
+
+ i += nBytes + 1;
+
+ if (!foundService.isNull() && !serviceUuids.contains(foundService))
+ serviceUuids.append(foundService);
+ }
+
+ info.setServiceUuids(serviceUuids, QBluetoothDeviceInfo::DataIncomplete);
+
+ env->ReleaseByteArrayElements(scanRecord, elems, JNI_ABORT);
+ }
+
if (QtAndroidPrivate::androidSdkVersion() >= 18) {
jint javaBtType = bluetoothDevice.callMethod<jint>("getType");