/**************************************************************************** ** ** Copyright (C) 2016 Lauri Laanmets (Proekspert AS) ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "android/devicediscoverybroadcastreceiver_p.h" #include #include #include #include #include #include "android/jni_android_p.h" #include #include #include #include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) typedef QHash JCachedBtTypes; Q_GLOBAL_STATIC(JCachedBtTypes, cachedBtTypes) typedef QHash JCachedMajorTypes; Q_GLOBAL_STATIC(JCachedMajorTypes, cachedMajorTypes) typedef QHash 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(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); if (it == cachedBtTypes()->end()) { QAndroidJniEnvironment env; if (javaType == QAndroidJniObject::getStaticField( javaBluetoothDeviceClassName, javaDeviceTypeClassic)) { cachedBtTypes()->insert(javaType, QBluetoothDeviceInfo::BaseRateCoreConfiguration); return QBluetoothDeviceInfo::BaseRateCoreConfiguration; } else if (javaType == QAndroidJniObject::getStaticField( javaBluetoothDeviceClassName, javaDeviceTypeLE)) { cachedBtTypes()->insert(javaType, QBluetoothDeviceInfo::LowEnergyCoreConfiguration); return QBluetoothDeviceInfo::LowEnergyCoreConfiguration; } else if (javaType == QAndroidJniObject::getStaticField( javaBluetoothDeviceClassName, javaDeviceTypeDual)) { cachedBtTypes()->insert(javaType, QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration); return QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration; } else if (javaType == QAndroidJniObject::getStaticField( javaBluetoothDeviceClassName, javaDeviceTypeUnknown)) { cachedBtTypes()->insert(javaType, QBluetoothDeviceInfo::UnknownCoreConfiguration); } else { if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } qCWarning(QT_BT_ANDROID) << "Unknown Bluetooth device type value"; } return QBluetoothDeviceInfo::UnknownCoreConfiguration; } else { return it.value(); } } 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( 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( 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)); addAction(valueForStaticField(JavaNames::BluetoothAdapter, JavaNames::ActionDiscoveryStarted)); addAction(valueForStaticField(JavaNames::BluetoothAdapter, JavaNames::ActionDiscoveryFinished)); } // Runs in Java thread void DeviceDiscoveryBroadcastReceiver::onReceive(JNIEnv *env, jobject context, jobject intent) { Q_UNUSED(context); Q_UNUSED(env); QAndroidJniObject intentObject(intent); const QString action = intentObject.callObjectMethod("getAction", "()Ljava/lang/String;").toString(); qCDebug(QT_BT_ANDROID) << "DeviceDiscoveryBroadcastReceiver::onReceive() - event:" << action; if (action == valueForStaticField(JavaNames::BluetoothAdapter, JavaNames::ActionDiscoveryFinished).toString()) { emit finished(); } else if (action == valueForStaticField(JavaNames::BluetoothAdapter, JavaNames::ActionDiscoveryStarted).toString()) { } else if (action == valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ActionFound).toString()) { //get BluetoothDevice QAndroidJniObject keyExtra = valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ExtraDevice); const QAndroidJniObject bluetoothDevice = intentObject.callObjectMethod("getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;", keyExtra.object()); if (!bluetoothDevice.isValid()) return; keyExtra = valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ExtraRssi); int rssi = intentObject.callMethod("getShortExtra", "(Ljava/lang/String;S)S", keyExtra.object(), 0); const QBluetoothDeviceInfo info = retrieveDeviceInfo(env, bluetoothDevice, rssi); if (info.isValid()) emit deviceDiscovered(info, false); } } // Runs in Java thread void DeviceDiscoveryBroadcastReceiver::onReceiveLeScan( JNIEnv *env, jobject jBluetoothDevice, jint rssi, jbyteArray scanRecord) { const QAndroidJniObject bluetoothDevice(jBluetoothDevice); if (!bluetoothDevice.isValid()) return; 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, jbyteArray scanRecord) { const QString deviceName = bluetoothDevice.callObjectMethod("getName").toString(); const QBluetoothAddress deviceAddress(bluetoothDevice.callObjectMethod("getAddress").toString()); const QAndroidJniObject bluetoothClass = bluetoothDevice.callObjectMethod("getBluetoothClass", "()Landroid/bluetooth/BluetoothClass;"); if (!bluetoothClass.isValid()) return QBluetoothDeviceInfo(); QBluetoothDeviceInfo::MajorDeviceClass majorClass = resolveAndroidMajorClass( bluetoothClass.callMethod("getMajorDeviceClass")); // major device class is 5 bits from index 8 - 12 quint32 classType = ((quint32(majorClass) & 0x1f) << 8); jint javaMinor = bluetoothClass.callMethod("getDeviceClass"); quint8 minorDeviceType = resolveAndroidMinorClass(majorClass, javaMinor); // minor device class is 6 bits from index 2 - 7 classType |= ((quint32(minorDeviceType) & 0x3f) << 2); static QList services; if (services.count() == 0) services << QBluetoothDeviceInfo::PositioningService << QBluetoothDeviceInfo::NetworkingService << QBluetoothDeviceInfo::RenderingService << QBluetoothDeviceInfo::CapturingService << QBluetoothDeviceInfo::ObjectTransferService << QBluetoothDeviceInfo::AudioService << QBluetoothDeviceInfo::TelephonyService << QBluetoothDeviceInfo::InformationService; // Matching BluetoothClass.Service values quint32 serviceResult = 0; quint32 current = 0; for (int i = 0; i < services.count(); i++) { current = services.at(i); int androidId = (current << 16); // Android values shift by 2 bytes compared to Qt enums if (bluetoothClass.callMethod("hasService", "(I)Z", androidId)) serviceResult |= current; } // 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(elems); const int scanRecordLength = env->GetArrayLength(scanRecord); QList 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(dataPtr)); break; case ADType32BitUuidIncomplete: case ADType32BitUuidComplete: foundService = QBluetoothUuid(qFromLittleEndian(dataPtr)); break; case ADType128BitUuidIncomplete: case ADType128BitUuidComplete: foundService = QBluetoothUuid(qToBigEndian(qFromLittleEndian(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("getType"); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } else { info.setCoreConfigurations(qtBtTypeForJavaBtType(javaBtType)); } } return info; } QT_END_NAMESPACE