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>
32 const qsizetype numberOfMajorDeviceClasses = 11;
35 const QBluetoothDeviceInfo::MajorDeviceClass classes = QBluetoothDeviceInfo::ComputerDevice;
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:
50 qCWarning(QT_BT_ANDROID) <<
"Unknown category of major device class:" << classes;
53 return QBitArray(numberOfMajorDeviceClasses,
false);
60static const char javaBluetoothDeviceClassName[] =
"android/bluetooth/BluetoothDevice";
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 },
115 {
"AUDIO_VIDEO_CAMCORDER", QBluetoothDeviceInfo::Camcorder },
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 },
135 {
"COMPUTER_UNCATEGORIZED", QBluetoothDeviceInfo::UncategorizedComputer },
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 },
145 {
"HEALTH_BLOOD_PRESSURE", QBluetoothDeviceInfo::HealthBloodPressureMonitor },
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 },
156 {
"PHONE_CELLULAR", QBluetoothDeviceInfo::CellularPhone },
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 },
165 {
"TOY_CONTROLLER", QBluetoothDeviceInfo::ToyController },
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 },
174 {
"WEARABLE_GLASSES", QBluetoothDeviceInfo::WearableGlasses },
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 },
184 {
"PERIPHERAL_NON_KEYBOARD_NON_POINTING", QBluetoothDeviceInfo::UncategorizedPeripheral },
185 {
"PERIPHERAL_KEYBOARD", QBluetoothDeviceInfo::KeyboardPeripheral },
186 {
"PERIPHERAL_POINTING", QBluetoothDeviceInfo::PointingDevicePeripheral },
187 {
"PERIPHERAL_KEYBOARD_POINTING", QBluetoothDeviceInfo::KeyboardWithPointingDevicePeripheral },
206
207
208
227 const JCachedBtTypes::iterator it = cachedBtTypes()->find(javaType);
228 if (it == cachedBtTypes()->end()) {
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);
250 qCWarning(QT_BT_ANDROID) <<
"Unknown Bluetooth device type value";
253 return QBluetoothDeviceInfo::UnknownCoreConfiguration;
261 const JCachedMajorTypes::iterator it = cachedMajorTypes()->find(javaType);
262 if (it == cachedMajorTypes()->end()) {
266 QBluetoothDeviceInfo::MajorDeviceClass result = QBluetoothDeviceInfo::UncategorizedDevice;
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;
276 cachedMajorTypes()->insert(javaType, QBluetoothDeviceInfo::UncategorizedDevice);
278 cachedMajorTypes()->insert(fieldValue, majorMapping.qtMajor);
281 if (fieldValue == javaType)
282 result = majorMapping.qtMajor;
292
293
294
297 int mappingIndex = (
int) major;
298 if (major == QBluetoothDeviceInfo::UncategorizedDevice)
303 Q_ASSERT(mappingIndex >=0
304 && mappingIndex <= (QBluetoothDeviceInfo::HealthDevice + 1));
312 int mappingIndex = mappingIndexForMajor(major);
313 quint8 sizeIndex = minorIndexSizes[mappingIndex];
316 jint fieldValue = QJniObject::getStaticField<jint>(
317 javaBluetoothClassDeviceClassName, minorMappings[sizeIndex].javaFieldName);
319 Q_ASSERT(fieldValue >= 0);
320 cachedMinorTypes()->insert(fieldValue,
minorMappings[sizeIndex].qtMinor);
324 initializedCacheTracker()->setBit(mappingIndex);
334 int mappingIndex = mappingIndexForMajor(major);
338 if (!initializedCacheTracker()->at(mappingIndex))
339 triggerCachingOfMinorsForMajor(major);
341 const JCachedMinorTypes::iterator it = cachedMinorTypes()->find(javaMinor);
342 if (it == cachedMinorTypes()->end())
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>()));
365 QJniObject intentObject(intent);
366 const QString action = intentObject.callMethod<jstring>(
"getAction").toString();
368 qCDebug(QT_BT_ANDROID) <<
"DeviceDiscoveryBroadcastReceiver::onReceive() - event:" << action;
370 if (action == valueForStaticField<QtJniTypes::BluetoothAdapter,
371 JavaNames::ActionDiscoveryFinished>()) {
373 }
else if (action == valueForStaticField<QtJniTypes::BluetoothAdapter,
374 JavaNames::ActionDiscoveryStarted>()) {
375 emit discoveryStarted();
376 }
else if (action == valueForStaticField<QtJniTypes::BluetoothDevice,
377 JavaNames::ActionFound>()) {
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>());
386 if (!bluetoothDevice.isValid())
389 keyExtra = QJniObject::fromString(valueForStaticField<QtJniTypes::BluetoothDevice,
390 JavaNames::ExtraRssi>());
391 int rssi = intentObject.callMethod<jshort>(
"getShortExtra",
392 keyExtra.object<jstring>(), jshort(0));
394 const QBluetoothDeviceInfo info = retrieveDeviceInfo(bluetoothDevice, rssi);
396 emit deviceDiscovered(info,
false);
402 JNIEnv *, jobject jBluetoothDevice, jint rssi, jbyteArray scanRecord)
404 const QJniObject bluetoothDevice(jBluetoothDevice);
405 if (!bluetoothDevice.isValid())
408 const QBluetoothDeviceInfo info = retrieveDeviceInfo(bluetoothDevice, rssi, scanRecord);
410 emit deviceDiscovered(info,
true);
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");
421 if (!bluetoothClass.isValid())
422 return QBluetoothDeviceInfo();
424 QBluetoothDeviceInfo::MajorDeviceClass majorClass = resolveAndroidMajorClass(
425 bluetoothClass.callMethod<jint>(
"getMajorDeviceClass"));
427 quint32 classType = ((quint32(majorClass) & 0x1f) << 8);
429 jint javaMinor = bluetoothClass.callMethod<jint>(
"getDeviceClass");
430 quint8 minorDeviceType = resolveAndroidMinorClass(majorClass, javaMinor);
433 classType |= ((quint32(minorDeviceType) & 0x3f) << 2);
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,
447 quint32 serviceResult = 0;
448 for (quint32 current : services) {
449 int androidId = (current << 16);
450 if (bluetoothClass.callMethod<jboolean>(
"hasService", androidId))
451 serviceResult |= current;
455 classType |= (serviceResult << 13);
457 QBluetoothDeviceInfo info(deviceAddress, deviceName, classType);
460 if (scanRecord !=
nullptr) {
463 jbyte *elems = env->GetByteArrayElements(scanRecord, &isCopy);
464 const char *scanRecordBuffer =
reinterpret_cast<
const char *>(elems);
465 const jsize scanRecordLength = env->GetArrayLength(scanRecord);
467 QList<QBluetoothUuid> serviceUuids;
472 while (i < scanRecordLength) {
476 const int nBytes = scanRecordBuffer[i];
480 if (i >= scanRecordLength - nBytes)
483 const int adType = scanRecordBuffer[i+1];
484 const char *dataPtr = &scanRecordBuffer[i+2];
485 QBluetoothUuid foundService;
488 case ADType16BitUuidIncomplete:
489 case ADType16BitUuidComplete:
490 foundService = QBluetoothUuid(qFromLittleEndian<quint16>(dataPtr));
492 case ADType32BitUuidIncomplete:
493 case ADType32BitUuidComplete:
494 foundService = QBluetoothUuid(qFromLittleEndian<quint32>(dataPtr));
496 case ADType128BitUuidIncomplete:
497 case ADType128BitUuidComplete:
499 QBluetoothUuid(qToBigEndian<QUuid::Id128Bytes>(qFromLittleEndian<QUuid::Id128Bytes>(dataPtr)));
503 info.setServiceData(QBluetoothUuid(qFromLittleEndian<quint16>(dataPtr)),
504 QByteArray(dataPtr + 2, nBytes - 3));
509 info.setServiceData(QBluetoothUuid(qFromLittleEndian<quint32>(dataPtr)),
510 QByteArray(dataPtr + 4, nBytes - 5));
515 info.setServiceData(QBluetoothUuid(qToBigEndian<QUuid::Id128Bytes>(
516 qFromLittleEndian<QUuid::Id128Bytes>(dataPtr))),
517 QByteArray(dataPtr + 16, nBytes - 17));
522 info.setManufacturerData(qFromLittleEndian<quint16>(dataPtr),
523 QByteArray(dataPtr + 2, nBytes - 3));
528 case ADTypeShortenedLocalName:
529 if (localName.isEmpty())
530 localName = QString::fromUtf8(dataPtr, nBytes - 1);
532 case ADTypeCompleteLocalName:
533 localName = QString::fromUtf8(dataPtr, nBytes - 1);
544 if (!foundService.isNull() && !serviceUuids.contains(foundService))
545 serviceUuids.append(foundService);
548 if (info.name().isEmpty())
549 info.setName(localName);
551 info.setServiceUuids(serviceUuids);
553 env->ReleaseByteArrayElements(scanRecord, elems, JNI_ABORT);
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));
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[]
@ ADType16BitUuidComplete
@ ADType16BitUuidIncomplete
@ ADType32BitUuidComplete
@ ADType32BitUuidIncomplete
@ ADTypeShortenedLocalName
@ ADType128BitUuidIncomplete
@ ADTypeCompleteLocalName
@ ADType128BitUuidComplete
@ ADTypeManufacturerSpecificData
@ ADTypeServiceData128Bit
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 } }))
const char javaFieldName[14]
QBluetoothDeviceInfo::MajorDeviceClass qtMajor
char const * javaFieldName