summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@insta.fi>2022-04-25 08:25:18 +0300
committerJuha Vuolle <juha.vuolle@insta.fi>2022-05-10 13:18:49 +0300
commite043bb633f44cc85080973857e6485a8f3801d30 (patch)
treec6453aab1b2038a5fb2d64273261ad372d79b48b /src
parentb2804c49f3b84b7d89cf9831d02bd415cc88b7b0 (diff)
Replace deprecated bluetooth disable/enable methods on Android
Starting from Android 13 (API level 33) the BluetoothAdapter enable() and disable() methods have been deprecated. In addition both methods are strongly discouraged as they do not necessarily trigger a permission dialogue to turn the bluetooth ON/OFF. The methods are replaced with preferred 'action requests' which are available since API level 5 and have the benefit of triggering a user dialogue when powering Bluetooth ON/OFF. The calls to these replacing APIs are surrounded by sdkVersion checks with one exception: it appears that the old enable() call does not work well when performing a multi-state transition from Discoverable => PoweredOff => Connectable. The replacing API fairs better there and hence it is replaced unconditionally. Elsewhere the sdkVersion check is for >= 31 in order to be able to test with devices available at the moment (API level 31 corresponds with Android 12). As a drive-by few related code changes: - handle hostmode enum in a switch-case instead of if-elseif - rename the opaque tokens and setConnectable() method in the broadcast receiver to better reflect their role Fixes: QTBUG-102442 Change-Id: I5d9395ce9e5ecd28b1f8e2f37d13e8aea7cfcdd3 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@qt.io> (cherry picked from commit b865d41be6181b309808f432ee825dc84a670e62)
Diffstat (limited to 'src')
-rw-r--r--src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver.java49
-rw-r--r--src/bluetooth/qbluetoothlocaldevice.cpp10
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_android.cpp101
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h2
4 files changed, 125 insertions, 37 deletions
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver.java
index c91cda5c..cfc8b914 100644
--- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver.java
+++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver.java
@@ -61,8 +61,15 @@ public class QtBluetoothBroadcastReceiver extends BroadcastReceiver
@SuppressWarnings("WeakerAccess")
static Context qtContext = null;
- private static final int TURN_BT_ON = 3330;
+ // These are opaque tokens that could be used to match the completed action
+ private static final int TURN_BT_ENABLED = 3330;
private static final int TURN_BT_DISCOVERABLE = 3331;
+ private static final int TURN_BT_DISABLED = 3332;
+
+ // The 'Disable' action identifier is hidden in the public APIs so we define it here
+ public static final String ACTION_REQUEST_DISABLE =
+ "android.bluetooth.adapter.action.REQUEST_DISABLE";
+
private static final String TAG = "QtBluetoothBroadcastReceiver";
public void onReceive(Context context, Intent intent)
@@ -90,35 +97,61 @@ public class QtBluetoothBroadcastReceiver extends BroadcastReceiver
qtContext = context;
}
- static public void setDiscoverable()
+ static public boolean setDisabled()
+ {
+ if (!(qtContext instanceof android.app.Activity)) {
+ Log.w(TAG, "Bluetooth cannot be disabled from a service.");
+ return false;
+ }
+ // The 'disable' is hidden in the public API and as such
+ // there are no availability guarantees; may throw an "ActivityNotFoundException"
+ Intent intent = new Intent(ACTION_REQUEST_DISABLE);
+
+ try {
+ ((Activity)qtContext).startActivityForResult(intent, TURN_BT_DISABLED);
+ } catch (Exception ex) {
+ Log.w(TAG, "setDisabled() failed to initiate Bluetooth disablement");
+ ex.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ static public boolean setDiscoverable()
{
if (!(qtContext instanceof android.app.Activity)) {
Log.w(TAG, "Discovery mode cannot be enabled from a service.");
- return;
+ return false;
}
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
try {
- ((Activity)qtContext).startActivityForResult(intent, TURN_BT_ON);
+ ((Activity)qtContext).startActivityForResult(intent, TURN_BT_DISCOVERABLE);
} catch (Exception ex) {
+ Log.w(TAG, "setDisabled() failed to initiate Bluetooth discoverability change");
ex.printStackTrace();
+ return false;
}
+ return true;
}
- static public void setConnectable()
+ static public boolean setEnabled()
{
if (!(qtContext instanceof android.app.Activity)) {
- Log.w(TAG, "Connectable mode cannot be enabled from a service.");
- return;
+ Log.w(TAG, "Bluetooth cannot be enabled from a service.");
+ return false;
}
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
try {
- ((Activity)qtContext).startActivityForResult(intent, TURN_BT_DISCOVERABLE);
+ ((Activity)qtContext).startActivityForResult(intent, TURN_BT_ENABLED);
} catch (Exception ex) {
+ Log.w(TAG, "setEnabled() failed to initiate Bluetooth enablement");
ex.printStackTrace();
+ return false;
}
+ return true;
}
static public boolean setPairingMode(String address, boolean isPairing)
diff --git a/src/bluetooth/qbluetoothlocaldevice.cpp b/src/bluetooth/qbluetoothlocaldevice.cpp
index 7f4de876..7e17433e 100644
--- a/src/bluetooth/qbluetoothlocaldevice.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice.cpp
@@ -104,6 +104,16 @@ QT_BEGIN_NAMESPACE
be connectable and powered on, if required. This mode is is not supported on Android.
On \macos, it is not possible to set the \l hostMode() to HostConnectable or HostPoweredOff.
+ \note Starting from Android 13 (API level 33) the HostPoweredOff state relies on
+ non-public Android API as the public one has been deprecated, see
+ (\l {https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()}
+ {disable()}). This may change in a future version of Android.
+
+ \note At least on Android 12 the device's Bluetooth visibility setting may dictate the result
+ of setting either HostDiscoverable or HostConnectable. For example if the visibility is set
+ \e off, it may not be possible to enter the HostDiscoverable mode, but HostConnectable will be
+ used instead. This may change in future version of Android.
+
*/
void registerQBluetoothLocalDeviceMetaType()
diff --git a/src/bluetooth/qbluetoothlocaldevice_android.cpp b/src/bluetooth/qbluetoothlocaldevice_android.cpp
index fce88838..b7a3a874 100644
--- a/src/bluetooth/qbluetoothlocaldevice_android.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_android.cpp
@@ -135,19 +135,26 @@ bool QBluetoothLocalDevicePrivate::isValid() const
void QBluetoothLocalDevicePrivate::processHostModeChange(QBluetoothLocalDevice::HostMode newMode)
{
- if (!pendingHostModeTransition) {
- // if not in transition -> pass data on
+ qCDebug(QT_BT_ANDROID) << "Processing host mode change:" << newMode
+ << ", pending transition:" << pendingConnectableHostModeTransition;
+ if (!pendingConnectableHostModeTransition) {
+ // If host mode is not in transition -> pass data on
emit q_ptr->hostModeStateChanged(newMode);
return;
}
+ // Host mode is in transition: check if the new mode is 'off' in which state
+ // we can enter the targeted 'Connectable' state
if (isValid() && newMode == QBluetoothLocalDevice::HostPoweredOff) {
- bool success = (bool)obj->callMethod<jboolean>("enable", "()Z");
- if (!success)
+ const bool success = (bool)QAndroidJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver",
+ "setEnabled");
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Transitioning Bluetooth from OFF to ON failed";
emit q_ptr->error(QBluetoothLocalDevice::UnknownError);
+ }
}
-
- pendingHostModeTransition = false;
+ pendingConnectableHostModeTransition = false;
}
// Return -1 if address is not part of a pending pairing request
@@ -251,49 +258,87 @@ void QBluetoothLocalDevice::powerOn()
return;
if (d_ptr->adapter()) {
- bool ret = (bool)d_ptr->adapter()->callMethod<jboolean>("enable", "()Z");
- if (!ret)
+ bool success(false);
+ if (QtAndroidPrivate::androidSdkVersion() >= 31) {
+ success = (bool)QAndroidJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver",
+ "setEnabled");
+ } else {
+ success = (bool)d_ptr->adapter()->callMethod<jboolean>("enable", "()Z");
+ }
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Enabling bluetooth failed";
emit error(QBluetoothLocalDevice::UnknownError);
+ }
}
}
void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode requestedMode)
{
- QBluetoothLocalDevice::HostMode mode = requestedMode;
+ QBluetoothLocalDevice::HostMode nextMode = requestedMode;
if (requestedMode == HostDiscoverableLimitedInquiry)
- mode = HostDiscoverable;
+ nextMode = HostDiscoverable;
- if (mode == hostMode())
+ if (nextMode == hostMode())
return;
- if (mode == QBluetoothLocalDevice::HostPoweredOff) {
- bool success = false;
- if (d_ptr->adapter())
- success = (bool)d_ptr->adapter()->callMethod<jboolean>("disable", "()Z");
+ switch (nextMode) {
- if (!success)
+ case QBluetoothLocalDevice::HostPoweredOff: {
+ bool success = false;
+ if (d_ptr->adapter()) {
+ if (QtAndroidPrivate::androidSdkVersion() >= 31) {
+ success = (bool)QAndroidJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver",
+ "setDisabled");
+ } else {
+ success = (bool)d_ptr->adapter()->callMethod<jboolean>("disable", "()Z");
+ }
+ }
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Unable to power off the adapter";
emit error(QBluetoothLocalDevice::UnknownError);
- } else if (mode == QBluetoothLocalDevice::HostConnectable) {
- if (hostMode() == QBluetoothLocalDevice::HostDiscoverable) {
- // cannot directly go from Discoverable to Connectable
- // we need to go to disabled mode and enable once disabling came through
+ }
+ break;
+ }
+ case QBluetoothLocalDevice::HostConnectable: {
+ if (hostMode() == QBluetoothLocalDevice::HostDiscoverable) {
+ // On Android 'Discoverable' is actually 'CONNECTABLE_DISCOVERABLE', and
+ // it seems we cannot go directly from "Discoverable" to "Connectable". Instead
+ // we need to go to disabled mode first and then to the 'Connectable' mode
setHostMode(QBluetoothLocalDevice::HostPoweredOff);
- d_ptr->pendingHostModeTransition = true;
+ d_ptr->pendingConnectableHostModeTransition = true;
} else {
- QAndroidJniObject::callStaticMethod<void>(
- "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver",
- "setConnectable");
+ const bool success = (bool)QAndroidJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver",
+ "setEnabled");
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Unable to enable the Bluetooth";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ }
}
- } else if (mode == QBluetoothLocalDevice::HostDiscoverable
- || mode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry) {
+ break;
+ }
+
+ case QBluetoothLocalDevice::HostDiscoverable: {
if (!ensureAndroidPermission(BluetoothPermission::Advertise)) {
qCWarning(QT_BT_ANDROID) << "Local device setHostMode() failed due to"
"missing permissions";
return;
}
- QAndroidJniObject::callStaticMethod<void>(
- "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver", "setDiscoverable");
+ const bool success = (bool)QAndroidJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver",
+ "setDiscoverable");
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Unable to set Bluetooth as discoverable";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ }
+ break;
+ }
+ default:
+ qCWarning(QT_BT_ANDROID) << "setHostMode() unsupported host mode:" << nextMode;
+ break;
}
}
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index 198d1d1f..71e75a7d 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -138,7 +138,7 @@ private:
public:
LocalDeviceBroadcastReceiver *receiver;
- bool pendingHostModeTransition = false;
+ bool pendingConnectableHostModeTransition = false;
QList<QPair<QBluetoothAddress, bool> > pendingPairings;
QList<QBluetoothAddress> connectedDevices;