@@ -9,6 +9,7 @@
*/
package eu.siacs.conversations.services;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -20,25 +21,25 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.AudioManager;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.os.Process;
import android.util.Log;
import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
+
+import com.google.common.collect.ImmutableList;
import org.webrtc.ThreadUtils;
+import java.util.Collections;
import java.util.List;
-import java.util.Set;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.AppRTCUtils;
-/**
- * AppRTCProximitySensor manages functions related to Bluetoth devices in the
- * AppRTC demo.
- */
+/** AppRTCProximitySensor manages functions related to Bluetoth devices in the AppRTC demo. */
public class AppRTCBluetoothManager {
// Timeout interval for starting or stopping audio to a Bluetooth SCO device.
private static final int BLUETOOTH_SCO_TIMEOUT_MS = 4000;
@@ -46,28 +47,26 @@ public class AppRTCBluetoothManager {
private static final int MAX_SCO_CONNECTION_ATTEMPTS = 2;
private final Context apprtcContext;
private final AppRTCAudioManager apprtcAudioManager;
- @Nullable
- private final AudioManager audioManager;
+ @Nullable private final AudioManager audioManager;
private final Handler handler;
private final BluetoothProfile.ServiceListener bluetoothServiceListener;
private final BroadcastReceiver bluetoothHeadsetReceiver;
int scoConnectionAttempts;
private State bluetoothState;
- @Nullable
- private BluetoothAdapter bluetoothAdapter;
- @Nullable
- private BluetoothHeadset bluetoothHeadset;
- @Nullable
- private BluetoothDevice bluetoothDevice;
+ @Nullable private BluetoothAdapter bluetoothAdapter;
+ @Nullable private BluetoothHeadset bluetoothHeadset;
+ @Nullable private BluetoothDevice bluetoothDevice;
// Runs when the Bluetooth timeout expires. We use that timeout after calling
// startScoAudio() or stopScoAudio() because we're not guaranteed to get a
// callback after those calls.
- private final Runnable bluetoothTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- bluetoothTimeout();
- }
- };
+ private final Runnable bluetoothTimeoutRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ bluetoothTimeout();
+ }
+ };
+
protected AppRTCBluetoothManager(Context context, AppRTCAudioManager audioManager) {
Log.d(Config.LOGTAG, "ctor");
ThreadUtils.checkIsOnMainThread();
@@ -80,42 +79,29 @@ public class AppRTCBluetoothManager {
handler = new Handler(Looper.getMainLooper());
}
- /**
- * Construction.
- */
+ /** Construction. */
static AppRTCBluetoothManager create(Context context, AppRTCAudioManager audioManager) {
Log.d(Config.LOGTAG, "create" + AppRTCUtils.getThreadInfo());
return new AppRTCBluetoothManager(context, audioManager);
}
- /**
- * Returns the internal state.
- */
+ /** Returns the internal state. */
public State getState() {
ThreadUtils.checkIsOnMainThread();
return bluetoothState;
}
/**
- * Activates components required to detect Bluetooth devices and to enable
- * BT SCO (audio is routed via BT SCO) for the headset profile. The end
- * state will be HEADSET_UNAVAILABLE but a state machine has started which
- * will start a state change sequence where the final outcome depends on
- * if/when the BT headset is enabled.
- * Example of state change sequence when start() is called while BT device
- * is connected and enabled:
- * UNINITIALIZED --> HEADSET_UNAVAILABLE --> HEADSET_AVAILABLE -->
- * SCO_CONNECTING --> SCO_CONNECTED <==> audio is now routed via BT SCO.
- * Note that the AppRTCAudioManager is also involved in driving this state
- * change.
+ * Activates components required to detect Bluetooth devices and to enable BT SCO (audio is
+ * routed via BT SCO) for the headset profile. The end state will be HEADSET_UNAVAILABLE but a
+ * state machine has started which will start a state change sequence where the final outcome
+ * depends on if/when the BT headset is enabled. Example of state change sequence when start()
+ * is called while BT device is connected and enabled: UNINITIALIZED --> HEADSET_UNAVAILABLE -->
+ * HEADSET_AVAILABLE --> SCO_CONNECTING --> SCO_CONNECTED <==> audio is now routed via BT SCO.
+ * Note that the AppRTCAudioManager is also involved in driving this state change.
*/
public void start() {
ThreadUtils.checkIsOnMainThread();
- Log.d(Config.LOGTAG, "start");
- if (!hasPermission(apprtcContext, android.Manifest.permission.BLUETOOTH)) {
- Log.w(Config.LOGTAG, "Process (pid=" + Process.myPid() + ") lacks BLUETOOTH permission");
- return;
- }
if (bluetoothState != State.UNINITIALIZED) {
Log.w(Config.LOGTAG, "Invalid BT state");
return;
@@ -130,11 +116,10 @@ public class AppRTCBluetoothManager {
return;
}
// Ensure that the device supports use of BT SCO audio for off call use cases.
- if (!audioManager.isBluetoothScoAvailableOffCall()) {
+ if (this.audioManager == null || !audioManager.isBluetoothScoAvailableOffCall()) {
Log.e(Config.LOGTAG, "Bluetooth SCO audio is not available off call");
return;
}
- logBluetoothAdapterInfo(bluetoothAdapter);
// Establish a connection to the HEADSET profile (includes both Bluetooth Headset and
// Hands-Free) proxy object and install a listener.
if (!getBluetoothProfileProxy(
@@ -149,16 +134,20 @@ public class AppRTCBluetoothManager {
// Register receiver for change in audio connection state of the Headset profile.
bluetoothHeadsetFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
registerReceiver(bluetoothHeadsetReceiver, bluetoothHeadsetFilter);
- Log.d(Config.LOGTAG, "HEADSET profile state: "
- + stateToString(bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET)));
+ if (hasBluetoothConnectPermission()) {
+ Log.d(
+ Config.LOGTAG,
+ "HEADSET profile state: "
+ + stateToString(
+ bluetoothAdapter.getProfileConnectionState(
+ BluetoothProfile.HEADSET)));
+ }
Log.d(Config.LOGTAG, "Bluetooth proxy for headset profile has started");
bluetoothState = State.HEADSET_UNAVAILABLE;
Log.d(Config.LOGTAG, "start done: BT state=" + bluetoothState);
}
- /**
- * Stops and closes all components related to Bluetooth audio.
- */
+ /** Stops and closes all components related to Bluetooth audio. */
public void stop() {
ThreadUtils.checkIsOnMainThread();
Log.d(Config.LOGTAG, "stop: BT state=" + bluetoothState);
@@ -184,23 +173,29 @@ public class AppRTCBluetoothManager {
}
/**
- * Starts Bluetooth SCO connection with remote device.
- * Note that the phone application always has the priority on the usage of the SCO connection
- * for telephony. If this method is called while the phone is in call it will be ignored.
- * Similarly, if a call is received or sent while an application is using the SCO connection,
- * the connection will be lost for the application and NOT returned automatically when the call
- * ends. Also note that: up to and including API version JELLY_BEAN_MR1, this method initiates a
- * virtual voice call to the Bluetooth headset. After API version JELLY_BEAN_MR2 only a raw SCO
- * audio connection is established.
+ * Starts Bluetooth SCO connection with remote device. Note that the phone application always
+ * has the priority on the usage of the SCO connection for telephony. If this method is called
+ * while the phone is in call it will be ignored. Similarly, if a call is received or sent while
+ * an application is using the SCO connection, the connection will be lost for the application
+ * and NOT returned automatically when the call ends. Also note that: up to and including API
+ * version JELLY_BEAN_MR1, this method initiates a virtual voice call to the Bluetooth headset.
+ * After API version JELLY_BEAN_MR2 only a raw SCO audio connection is established.
* TODO(henrika): should we add support for virtual voice call to BT headset also for JBMR2 and
* higher. It might be required to initiates a virtual voice call since many devices do not
* accept SCO audio without a "call".
*/
public boolean startScoAudio() {
ThreadUtils.checkIsOnMainThread();
- Log.d(Config.LOGTAG, "startSco: BT state=" + bluetoothState + ", "
- + "attempts: " + scoConnectionAttempts + ", "
- + "SCO is on: " + isScoOn());
+ Log.d(
+ Config.LOGTAG,
+ "startSco: BT state="
+ + bluetoothState
+ + ", "
+ + "attempts: "
+ + scoConnectionAttempts
+ + ", "
+ + "SCO is on: "
+ + isScoOn());
if (scoConnectionAttempts >= MAX_SCO_CONNECTION_ATTEMPTS) {
Log.e(Config.LOGTAG, "BT SCO connection fails - no more attempts");
return false;
@@ -213,24 +208,29 @@ public class AppRTCBluetoothManager {
Log.d(Config.LOGTAG, "Starting Bluetooth SCO and waits for ACTION_AUDIO_STATE_CHANGED...");
// The SCO connection establishment can take several seconds, hence we cannot rely on the
// connection to be available when the method returns but instead register to receive the
- // intent ACTION_SCO_AUDIO_STATE_UPDATED and wait for the state to be SCO_AUDIO_STATE_CONNECTED.
+ // intent ACTION_SCO_AUDIO_STATE_UPDATED and wait for the state to be
+ // SCO_AUDIO_STATE_CONNECTED.
bluetoothState = State.SCO_CONNECTING;
audioManager.startBluetoothSco();
audioManager.setBluetoothScoOn(true);
scoConnectionAttempts++;
startTimer();
- Log.d(Config.LOGTAG, "startScoAudio done: BT state=" + bluetoothState + ", "
- + "SCO is on: " + isScoOn());
+ Log.d(
+ Config.LOGTAG,
+ "startScoAudio done: BT state="
+ + bluetoothState
+ + ", "
+ + "SCO is on: "
+ + isScoOn());
return true;
}
- /**
- * Stops Bluetooth SCO connection with remote device.
- */
+ /** Stops Bluetooth SCO connection with remote device. */
public void stopScoAudio() {
ThreadUtils.checkIsOnMainThread();
- Log.d(Config.LOGTAG, "stopScoAudio: BT state=" + bluetoothState + ", "
- + "SCO is on: " + isScoOn());
+ Log.d(
+ Config.LOGTAG,
+ "stopScoAudio: BT state=" + bluetoothState + ", " + "SCO is on: " + isScoOn());
if (bluetoothState != State.SCO_CONNECTING && bluetoothState != State.SCO_CONNECTED) {
return;
}
@@ -238,17 +238,18 @@ public class AppRTCBluetoothManager {
audioManager.stopBluetoothSco();
audioManager.setBluetoothScoOn(false);
bluetoothState = State.SCO_DISCONNECTING;
- Log.d(Config.LOGTAG, "stopScoAudio done: BT state=" + bluetoothState + ", "
- + "SCO is on: " + isScoOn());
+ Log.d(
+ Config.LOGTAG,
+ "stopScoAudio done: BT state=" + bluetoothState + ", " + "SCO is on: " + isScoOn());
}
/**
- * Use the BluetoothHeadset proxy object (controls the Bluetooth Headset
- * Service via IPC) to update the list of connected devices for the HEADSET
- * profile. The internal state will change to HEADSET_UNAVAILABLE or to
- * HEADSET_AVAILABLE and |bluetoothDevice| will be mapped to the connected
- * device if available.
+ * Use the BluetoothHeadset proxy object (controls the Bluetooth Headset Service via IPC) to
+ * update the list of connected devices for the HEADSET profile. The internal state will change
+ * to HEADSET_UNAVAILABLE or to HEADSET_AVAILABLE and |bluetoothDevice| will be mapped to the
+ * connected device if available.
*/
+ @SuppressLint("MissingPermission")
public void updateDevice() {
if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) {
return;
@@ -257,7 +258,12 @@ public class AppRTCBluetoothManager {
// Get connected devices for the headset profile. Returns the set of
// devices which are in state STATE_CONNECTED. The BluetoothDevice class
// is just a thin wrapper for a Bluetooth hardware address.
- List<BluetoothDevice> devices = bluetoothHeadset.getConnectedDevices();
+ final List<BluetoothDevice> devices;
+ if (hasBluetoothConnectPermission()) {
+ devices = bluetoothHeadset.getConnectedDevices();
+ } else {
+ devices = ImmutableList.of();
+ }
if (devices.isEmpty()) {
bluetoothDevice = null;
bluetoothState = State.HEADSET_UNAVAILABLE;
@@ -266,17 +272,21 @@ public class AppRTCBluetoothManager {
// Always use first device in list. Android only supports one device.
bluetoothDevice = devices.get(0);
bluetoothState = State.HEADSET_AVAILABLE;
- Log.d(Config.LOGTAG, "Connected bluetooth headset: "
- + "name=" + bluetoothDevice.getName() + ", "
- + "state=" + stateToString(bluetoothHeadset.getConnectionState(bluetoothDevice))
- + ", SCO audio=" + bluetoothHeadset.isAudioConnected(bluetoothDevice));
+ Log.d(
+ Config.LOGTAG,
+ "Connected bluetooth headset: "
+ + "name="
+ + bluetoothDevice.getName()
+ + ", "
+ + "state="
+ + stateToString(bluetoothHeadset.getConnectionState(bluetoothDevice))
+ + ", SCO audio="
+ + bluetoothHeadset.isAudioConnected(bluetoothDevice));
}
Log.d(Config.LOGTAG, "updateDevice done: BT state=" + bluetoothState);
}
- /**
- * Stubs for test mocks.
- */
+ /** Stubs for test mocks. */
@Nullable
protected AudioManager getAudioManager(Context context) {
return (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -295,52 +305,31 @@ public class AppRTCBluetoothManager {
return bluetoothAdapter.getProfileProxy(context, listener, profile);
}
- protected boolean hasPermission(Context context, String permission) {
- return apprtcContext.checkPermission(permission, Process.myPid(), Process.myUid())
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Logs the state of the local Bluetooth adapter.
- */
- @SuppressLint("HardwareIds")
- protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) {
- Log.d(Config.LOGTAG, "BluetoothAdapter: "
- + "enabled=" + localAdapter.isEnabled() + ", "
- + "state=" + stateToString(localAdapter.getState()) + ", "
- + "name=" + localAdapter.getName() + ", "
- + "address=" + localAdapter.getAddress());
- // Log the set of BluetoothDevice objects that are bonded (paired) to the local adapter.
- Set<BluetoothDevice> pairedDevices = localAdapter.getBondedDevices();
- if (!pairedDevices.isEmpty()) {
- Log.d(Config.LOGTAG, "paired devices:");
- for (BluetoothDevice device : pairedDevices) {
- Log.d(Config.LOGTAG, " name=" + device.getName() + ", address=" + device.getAddress());
- }
+ protected boolean hasBluetoothConnectPermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ return ActivityCompat.checkSelfPermission(
+ apprtcContext, Manifest.permission.BLUETOOTH_CONNECT)
+ == PackageManager.PERMISSION_GRANTED;
+ } else {
+ return true;
}
}
- /**
- * Ensures that the audio manager updates its list of available audio devices.
- */
+ /** Ensures that the audio manager updates its list of available audio devices. */
private void updateAudioDeviceState() {
ThreadUtils.checkIsOnMainThread();
Log.d(Config.LOGTAG, "updateAudioDeviceState");
apprtcAudioManager.updateAudioDeviceState();
}
- /**
- * Starts timer which times out after BLUETOOTH_SCO_TIMEOUT_MS milliseconds.
- */
+ /** Starts timer which times out after BLUETOOTH_SCO_TIMEOUT_MS milliseconds. */
private void startTimer() {
ThreadUtils.checkIsOnMainThread();
Log.d(Config.LOGTAG, "startTimer");
handler.postDelayed(bluetoothTimeoutRunnable, BLUETOOTH_SCO_TIMEOUT_MS);
}
- /**
- * Cancels any outstanding timer tasks.
- */
+ /** Cancels any outstanding timer tasks. */
private void cancelTimer() {
ThreadUtils.checkIsOnMainThread();
Log.d(Config.LOGTAG, "cancelTimer");
@@ -348,23 +337,36 @@ public class AppRTCBluetoothManager {
}
/**
- * Called when start of the BT SCO channel takes too long time. Usually
- * happens when the BT device has been turned on during an ongoing call.
+ * Called when start of the BT SCO channel takes too long time. Usually happens when the BT
+ * device has been turned on during an ongoing call.
*/
+ @SuppressLint("MissingPermission")
private void bluetoothTimeout() {
ThreadUtils.checkIsOnMainThread();
if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) {
return;
}
- Log.d(Config.LOGTAG, "bluetoothTimeout: BT state=" + bluetoothState + ", "
- + "attempts: " + scoConnectionAttempts + ", "
- + "SCO is on: " + isScoOn());
+ Log.d(
+ Config.LOGTAG,
+ "bluetoothTimeout: BT state="
+ + bluetoothState
+ + ", "
+ + "attempts: "
+ + scoConnectionAttempts
+ + ", "
+ + "SCO is on: "
+ + isScoOn());
if (bluetoothState != State.SCO_CONNECTING) {
return;
}
// Bluetooth SCO should be connecting; check the latest result.
boolean scoConnected = false;
- List<BluetoothDevice> devices = bluetoothHeadset.getConnectedDevices();
+ final List<BluetoothDevice> devices;
+ if (hasBluetoothConnectPermission()) {
+ devices = bluetoothHeadset.getConnectedDevices();
+ } else {
+ devices = Collections.emptyList();
+ }
if (devices.size() > 0) {
bluetoothDevice = devices.get(0);
if (bluetoothHeadset.isAudioConnected(bluetoothDevice)) {
@@ -387,16 +389,12 @@ public class AppRTCBluetoothManager {
Log.d(Config.LOGTAG, "bluetoothTimeout done: BT state=" + bluetoothState);
}
- /**
- * Checks whether audio uses Bluetooth SCO.
- */
+ /** Checks whether audio uses Bluetooth SCO. */
private boolean isScoOn() {
return audioManager.isBluetoothScoOn();
}
- /**
- * Converts BluetoothAdapter states into local string representations.
- */
+ /** Converts BluetoothAdapter states into local string representations. */
private String stateToString(int state) {
switch (state) {
case BluetoothAdapter.STATE_DISCONNECTED:
@@ -412,11 +410,13 @@ public class AppRTCBluetoothManager {
case BluetoothAdapter.STATE_ON:
return "ON";
case BluetoothAdapter.STATE_TURNING_OFF:
- // Indicates the local Bluetooth adapter is turning off. Local clients should immediately
+ // Indicates the local Bluetooth adapter is turning off. Local clients should
+ // immediately
// attempt graceful disconnection of any remote links.
return "TURNING_OFF";
case BluetoothAdapter.STATE_TURNING_ON:
- // Indicates the local Bluetooth adapter is turning on. However local clients should wait
+ // Indicates the local Bluetooth adapter is turning on. However local clients should
+ // wait
// for STATE_ON before attempting to use the adapter.
return "TURNING_ON";
default:
@@ -457,7 +457,9 @@ public class AppRTCBluetoothManager {
if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) {
return;
}
- Log.d(Config.LOGTAG, "BluetoothServiceListener.onServiceConnected: BT state=" + bluetoothState);
+ Log.d(
+ Config.LOGTAG,
+ "BluetoothServiceListener.onServiceConnected: BT state=" + bluetoothState);
// Android only supports one connected Bluetooth Headset at a time.
bluetoothHeadset = (BluetoothHeadset) proxy;
updateAudioDeviceState();
@@ -470,7 +472,9 @@ public class AppRTCBluetoothManager {
if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) {
return;
}
- Log.d(Config.LOGTAG, "BluetoothServiceListener.onServiceDisconnected: BT state=" + bluetoothState);
+ Log.d(
+ Config.LOGTAG,
+ "BluetoothServiceListener.onServiceDisconnected: BT state=" + bluetoothState);
stopScoAudio();
bluetoothHeadset = null;
bluetoothDevice = null;
@@ -495,12 +499,20 @@ public class AppRTCBluetoothManager {
// headset while audio is active using another audio device.
if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
final int state =
- intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
- Log.d(Config.LOGTAG, "BluetoothHeadsetBroadcastReceiver.onReceive: "
- + "a=ACTION_CONNECTION_STATE_CHANGED, "
- + "s=" + stateToString(state) + ", "
- + "sb=" + isInitialStickyBroadcast() + ", "
- + "BT state: " + bluetoothState);
+ intent.getIntExtra(
+ BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
+ Log.d(
+ Config.LOGTAG,
+ "BluetoothHeadsetBroadcastReceiver.onReceive: "
+ + "a=ACTION_CONNECTION_STATE_CHANGED, "
+ + "s="
+ + stateToString(state)
+ + ", "
+ + "sb="
+ + isInitialStickyBroadcast()
+ + ", "
+ + "BT state: "
+ + bluetoothState);
if (state == BluetoothHeadset.STATE_CONNECTED) {
scoConnectionAttempts = 0;
updateAudioDeviceState();
@@ -516,13 +528,22 @@ public class AppRTCBluetoothManager {
// Change in the audio (SCO) connection state of the Headset profile.
// Typically received after call to startScoAudio() has finalized.
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
- final int state = intent.getIntExtra(
- BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
- Log.d(Config.LOGTAG, "BluetoothHeadsetBroadcastReceiver.onReceive: "
- + "a=ACTION_AUDIO_STATE_CHANGED, "
- + "s=" + stateToString(state) + ", "
- + "sb=" + isInitialStickyBroadcast() + ", "
- + "BT state: " + bluetoothState);
+ final int state =
+ intent.getIntExtra(
+ BluetoothHeadset.EXTRA_STATE,
+ BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+ Log.d(
+ Config.LOGTAG,
+ "BluetoothHeadsetBroadcastReceiver.onReceive: "
+ + "a=ACTION_AUDIO_STATE_CHANGED, "
+ + "s="
+ + stateToString(state)
+ + ", "
+ + "sb="
+ + isInitialStickyBroadcast()
+ + ", "
+ + "BT state: "
+ + bluetoothState);
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
cancelTimer();
if (bluetoothState == State.SCO_CONNECTING) {
@@ -531,14 +552,18 @@ public class AppRTCBluetoothManager {
scoConnectionAttempts = 0;
updateAudioDeviceState();
} else {
- Log.w(Config.LOGTAG, "Unexpected state BluetoothHeadset.STATE_AUDIO_CONNECTED");
+ Log.w(
+ Config.LOGTAG,
+ "Unexpected state BluetoothHeadset.STATE_AUDIO_CONNECTED");
}
} else if (state == BluetoothHeadset.STATE_AUDIO_CONNECTING) {
Log.d(Config.LOGTAG, "+++ Bluetooth audio SCO is now connecting...");
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
Log.d(Config.LOGTAG, "+++ Bluetooth audio SCO is now disconnected");
if (isInitialStickyBroadcast()) {
- Log.d(Config.LOGTAG, "Ignore STATE_AUDIO_DISCONNECTED initial sticky broadcast.");
+ Log.d(
+ Config.LOGTAG,
+ "Ignore STATE_AUDIO_DISCONNECTED initial sticky broadcast.");
return;
}
updateAudioDeviceState();
@@ -547,4 +572,4 @@ public class AppRTCBluetoothManager {
Log.d(Config.LOGTAG, "onReceive done: BT state=" + bluetoothState);
}
}
-}
+}