Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +4 −5 Original line number Diff line number Diff line Loading @@ -99,7 +99,6 @@ import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; import com.android.bluetooth.btservice.activityattribution.ActivityAttributionService; import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreService; Loading Loading @@ -2449,10 +2448,10 @@ public class AdapterService extends Service { HashSet<Class> leAudioUnicastProfiles = Config.geLeAudioUnicastProfiles(); if (supportedProfileServices.containsAll(leAudioUnicastProfiles)) { return BluetoothStatusCodes.SUCCESS; return BluetoothStatusCodes.FEATURE_SUPPORTED; } return BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED; return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED; } @Override Loading @@ -2463,10 +2462,10 @@ public class AdapterService extends Service { } if (service.mAdapterProperties.isLePeriodicAdvertisingSyncTransferSenderSupported()) { return BluetoothStatusCodes.SUCCESS; return BluetoothStatusCodes.FEATURE_SUPPORTED; } return BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED; return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED; } @Override Loading android/app/src/com/android/bluetooth/hfp/BluetoothHeadsetProxy.java +12 −7 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.bluetooth.hfp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothManager; Loading Loading @@ -73,7 +72,12 @@ public class BluetoothHeadsetProxy { return mBluetoothHeadset.getAudioState(device); } public boolean connectAudio() { /** * Proxy function that calls {@link BluetoothHeadset#connectAudio()}. * * @return whether the connection request was successful */ public int connectAudio() { return mBluetoothHeadset.connectAudio(); } Loading @@ -85,11 +89,12 @@ public class BluetoothHeadsetProxy { return mBluetoothHeadset.getActiveDevice(); } public boolean isAudioOn() { return mBluetoothHeadset.isAudioOn(); } public boolean disconnectAudio() { /** * Proxy function that calls {@link BluetoothHeadset#disconnectAudio()}. * * @return whether the disconnection request was successful */ public int disconnectAudio() { return mBluetoothHeadset.disconnectAudio(); } Loading android/app/src/com/android/bluetooth/hfp/HeadsetService.java +62 −53 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothHeadset; import android.content.AttributionSource; Loading Loading @@ -683,6 +684,7 @@ public class HeadsetService extends ProfileService { HeadsetService service = getService(source); boolean defaultValue = false; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.isAudioConnected(device); } receiver.send(defaultValue); Loading @@ -698,6 +700,7 @@ public class HeadsetService extends ProfileService { HeadsetService service = getService(source); int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.getAudioState(device); } receiver.send(defaultValue); Loading @@ -710,8 +713,9 @@ public class HeadsetService extends ProfileService { public void connectAudio(AttributionSource source, SynchronousResultReceiver receiver) { try { HeadsetService service = getService(source); boolean defaultValue = false; int defaultValue = BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.connectAudio(); } receiver.send(defaultValue); Loading @@ -724,8 +728,9 @@ public class HeadsetService extends ProfileService { public void disconnectAudio(AttributionSource source, SynchronousResultReceiver receiver) { try { HeadsetService service = getService(source); boolean defaultValue = false; int defaultValue = BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.disconnectAudio(); } receiver.send(defaultValue); Loading Loading @@ -882,6 +887,7 @@ public class HeadsetService extends ProfileService { HeadsetService service = getService(source); boolean defaultValue = false; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.isInbandRingingEnabled(); } receiver.send(defaultValue); Loading Loading @@ -1122,7 +1128,7 @@ public class HeadsetService extends ProfileService { // Audio should not be on when no audio mode is active if (isAudioOn()) { // Disconnect audio so that API user can try later boolean status = disconnectAudio(); int status = disconnectAudio(); Log.w(TAG, "startVoiceRecognition: audio is still active, please wait for audio to" + " be disconnected, disconnectAudio() returned " + status + ", active device is " + mActiveDevice); Loading Loading @@ -1327,8 +1333,10 @@ public class HeadsetService extends ProfileService { } } if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (!disconnectAudio(mActiveDevice)) { Log.w(TAG, "removeActiveDevice: disconnectAudio failed on " + mActiveDevice); int disconnectStatus = disconnectAudio(mActiveDevice); if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "removeActiveDevice: disconnectAudio failed on " + mActiveDevice + " with status code " + disconnectStatus); } } mActiveDevice = null; Loading Loading @@ -1366,9 +1374,10 @@ public class HeadsetService extends ProfileService { BluetoothDevice previousActiveDevice = mActiveDevice; mActiveDevice = device; if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (!disconnectAudio(previousActiveDevice)) { int disconnectStatus = disconnectAudio(previousActiveDevice); if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { Log.e(TAG, "setActiveDevice: fail to disconnectAudio from " + previousActiveDevice); + previousActiveDevice + " with status code " + disconnectStatus); if (previousActiveDevice == null) { removeActiveDevice(); } else { Loading @@ -1380,8 +1389,10 @@ public class HeadsetService extends ProfileService { broadcastActiveDevice(mActiveDevice); } else if (shouldPersistAudio()) { broadcastActiveDevice(mActiveDevice); if (!connectAudio(mActiveDevice)) { Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice); int connectStatus = connectAudio(mActiveDevice); if (connectStatus != BluetoothStatusCodes.SUCCESS) { Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice + " with status code " + connectStatus); if (previousActiveDevice == null) { removeActiveDevice(); } else { Loading @@ -1408,45 +1419,46 @@ public class HeadsetService extends ProfileService { } } boolean connectAudio() { int connectAudio() { synchronized (mStateMachines) { BluetoothDevice device = mActiveDevice; if (device == null) { Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString()); return false; return BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES; } return connectAudio(device); } } boolean connectAudio(BluetoothDevice device) { int connectAudio(BluetoothDevice device) { Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString()); synchronized (mStateMachines) { if (!isScoAcceptable(device)) { Log.w(TAG, "connectAudio, rejected SCO request to " + device); return false; } final HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting"); return false; return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; } int scoConnectionAllowedState = isScoAcceptable(device); if (scoConnectionAllowedState != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "connectAudio, rejected SCO request to " + device); return scoConnectionAllowedState; } if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { Log.w(TAG, "connectAudio: profile not connected"); return false; return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; } if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { logD("connectAudio: audio is not idle for device " + device); return true; return BluetoothStatusCodes.SUCCESS; } if (isAudioOn()) { Log.w(TAG, "connectAudio: audio is not idle, current audio devices are " + Arrays.toString(getNonIdleAudioDevices().toArray())); return false; return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED; } stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); } return true; return BluetoothStatusCodes.SUCCESS; } private List<BluetoothDevice> getNonIdleAudioDevices() { Loading @@ -1461,38 +1473,30 @@ public class HeadsetService extends ProfileService { return devices; } boolean disconnectAudio() { boolean result = false; int disconnectAudio() { synchronized (mStateMachines) { for (BluetoothDevice device : getNonIdleAudioDevices()) { if (disconnectAudio(device)) { result = true; } else { Log.e(TAG, "disconnectAudio() from " + device + " failed"); } List<BluetoothDevice> activeAudioDevices = getNonIdleAudioDevices(); BluetoothDevice activeAudioDevice = activeAudioDevices.get(activeAudioDevices.size() - 1); return disconnectAudio(activeAudioDevice); } } if (!result) { logD("disconnectAudio() no active audio connection"); } return result; } boolean disconnectAudio(BluetoothDevice device) { int disconnectAudio(BluetoothDevice device) { synchronized (mStateMachines) { Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString()); final HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting"); return false; return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; } if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device); return false; return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED; } stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); } return true; return BluetoothStatusCodes.SUCCESS; } boolean isVirtualCallStarted() { Loading Loading @@ -1522,7 +1526,7 @@ public class HeadsetService extends ProfileService { // Audio should not be on when no audio mode is active if (isAudioOn()) { // Disconnect audio so that API user can try later boolean status = disconnectAudio(); int status = disconnectAudio(); Log.w(TAG, "startScoUsingVirtualVoiceCall: audio is still active, please wait for " + "audio to be disconnected, disconnectAudio() returned " + status + ", active device is " + mActiveDevice); Loading Loading @@ -1684,7 +1688,7 @@ public class HeadsetService extends ProfileService { // Audio should not be on when no audio mode is active if (isAudioOn()) { // Disconnect audio so that user can try later boolean status = disconnectAudio(); int status = disconnectAudio(); Log.w(TAG, "startVoiceRecognitionByHeadset: audio is still active, please wait for" + " audio to be disconnected, disconnectAudio() returned " + status + ", active device is " + mActiveDevice); Loading Loading @@ -1739,9 +1743,10 @@ public class HeadsetService extends ProfileService { mVoiceRecognitionTimeoutEvent = null; } if (mVoiceRecognitionStarted) { if (!disconnectAudio()) { int disconnectStatus = disconnectAudio(); if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from " + fromDevice); + fromDevice + " with status code " + disconnectStatus); } mVoiceRecognitionStarted = false; } Loading Loading @@ -1943,10 +1948,12 @@ public class HeadsetService extends ProfileService { if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (mActiveDevice != null && !mActiveDevice.equals(device) && shouldPersistAudio()) { if (!connectAudio(mActiveDevice)) { int connectStatus = connectAudio(mActiveDevice); if (connectStatus != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect" + " audio to new " + "active device " + mActiveDevice + ", after " + device + " is disconnected from SCO"); + ", after " + device + " is disconnected from SCO due to" + " status code " + connectStatus); } } } Loading Loading @@ -2020,36 +2027,38 @@ public class HeadsetService extends ProfileService { } /** * Checks if SCO should be connected at current system state * Checks if SCO should be connected at current system state. Returns * {@link BluetoothStatusCodes#SUCCESS} if SCO is allowed to be connected or an error code on * failure. * * @param device device for SCO to be connected * @return true if SCO is allowed to be connected * @return whether SCO can be connected */ public boolean isScoAcceptable(BluetoothDevice device) { public int isScoAcceptable(BluetoothDevice device) { synchronized (mStateMachines) { if (device == null || !device.equals(mActiveDevice)) { Log.w(TAG, "isScoAcceptable: rejected SCO since " + device + " is not the current active device " + mActiveDevice); return false; return BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE; } if (mForceScoAudio) { return true; return BluetoothStatusCodes.SUCCESS; } if (!mAudioRouteAllowed) { Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed"); return false; return BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED; } if (mVoiceRecognitionStarted || mVirtualCallStarted) { return true; return BluetoothStatusCodes.SUCCESS; } if (shouldCallAudioBeActive()) { return true; return BluetoothStatusCodes.SUCCESS; } Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" + mSystemInterface.isRinging() + ", inbandRinging=" + isInbandRingingEnabled() + ", isVirtualCallStarted=" + mVirtualCallStarted); return false; return BluetoothStatusCodes.ERROR_CALL_ACTIVE; } } Loading android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +3 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.hfp.BluetoothHfpProtoEnums; import android.content.Intent; import android.media.AudioManager; Loading Loading @@ -1110,7 +1111,7 @@ public class HeadsetStateMachine extends StateMachine { stateLogD("processAudioEvent, state=" + state); switch (state) { case HeadsetHalConstants.AUDIO_STATE_CONNECTED: if (!mHeadsetService.isScoAcceptable(mDevice)) { if (mHeadsetService.isScoAcceptable(mDevice) != BluetoothStatusCodes.SUCCESS) { stateLogW("processAudioEvent: reject incoming audio connection"); if (!mNativeInterface.disconnectAudio(mDevice)) { stateLogE("processAudioEvent: failed to disconnect audio"); Loading @@ -1124,7 +1125,7 @@ public class HeadsetStateMachine extends StateMachine { transitionTo(mAudioOn); break; case HeadsetHalConstants.AUDIO_STATE_CONNECTING: if (!mHeadsetService.isScoAcceptable(mDevice)) { if (mHeadsetService.isScoAcceptable(mDevice) != BluetoothStatusCodes.SUCCESS) { stateLogW("processAudioEvent: reject incoming pending audio connection"); if (!mNativeInterface.disconnectAudio(mDevice)) { stateLogE("processAudioEvent: failed to disconnect pending audio"); Loading android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +32 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +4 −5 Original line number Diff line number Diff line Loading @@ -99,7 +99,6 @@ import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; import com.android.bluetooth.btservice.activityattribution.ActivityAttributionService; import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreService; Loading Loading @@ -2449,10 +2448,10 @@ public class AdapterService extends Service { HashSet<Class> leAudioUnicastProfiles = Config.geLeAudioUnicastProfiles(); if (supportedProfileServices.containsAll(leAudioUnicastProfiles)) { return BluetoothStatusCodes.SUCCESS; return BluetoothStatusCodes.FEATURE_SUPPORTED; } return BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED; return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED; } @Override Loading @@ -2463,10 +2462,10 @@ public class AdapterService extends Service { } if (service.mAdapterProperties.isLePeriodicAdvertisingSyncTransferSenderSupported()) { return BluetoothStatusCodes.SUCCESS; return BluetoothStatusCodes.FEATURE_SUPPORTED; } return BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED; return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED; } @Override Loading
android/app/src/com/android/bluetooth/hfp/BluetoothHeadsetProxy.java +12 −7 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.bluetooth.hfp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothManager; Loading Loading @@ -73,7 +72,12 @@ public class BluetoothHeadsetProxy { return mBluetoothHeadset.getAudioState(device); } public boolean connectAudio() { /** * Proxy function that calls {@link BluetoothHeadset#connectAudio()}. * * @return whether the connection request was successful */ public int connectAudio() { return mBluetoothHeadset.connectAudio(); } Loading @@ -85,11 +89,12 @@ public class BluetoothHeadsetProxy { return mBluetoothHeadset.getActiveDevice(); } public boolean isAudioOn() { return mBluetoothHeadset.isAudioOn(); } public boolean disconnectAudio() { /** * Proxy function that calls {@link BluetoothHeadset#disconnectAudio()}. * * @return whether the disconnection request was successful */ public int disconnectAudio() { return mBluetoothHeadset.disconnectAudio(); } Loading
android/app/src/com/android/bluetooth/hfp/HeadsetService.java +62 −53 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothHeadset; import android.content.AttributionSource; Loading Loading @@ -683,6 +684,7 @@ public class HeadsetService extends ProfileService { HeadsetService service = getService(source); boolean defaultValue = false; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.isAudioConnected(device); } receiver.send(defaultValue); Loading @@ -698,6 +700,7 @@ public class HeadsetService extends ProfileService { HeadsetService service = getService(source); int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.getAudioState(device); } receiver.send(defaultValue); Loading @@ -710,8 +713,9 @@ public class HeadsetService extends ProfileService { public void connectAudio(AttributionSource source, SynchronousResultReceiver receiver) { try { HeadsetService service = getService(source); boolean defaultValue = false; int defaultValue = BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.connectAudio(); } receiver.send(defaultValue); Loading @@ -724,8 +728,9 @@ public class HeadsetService extends ProfileService { public void disconnectAudio(AttributionSource source, SynchronousResultReceiver receiver) { try { HeadsetService service = getService(source); boolean defaultValue = false; int defaultValue = BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.disconnectAudio(); } receiver.send(defaultValue); Loading Loading @@ -882,6 +887,7 @@ public class HeadsetService extends ProfileService { HeadsetService service = getService(source); boolean defaultValue = false; if (service != null) { enforceBluetoothPrivilegedPermission(service); defaultValue = service.isInbandRingingEnabled(); } receiver.send(defaultValue); Loading Loading @@ -1122,7 +1128,7 @@ public class HeadsetService extends ProfileService { // Audio should not be on when no audio mode is active if (isAudioOn()) { // Disconnect audio so that API user can try later boolean status = disconnectAudio(); int status = disconnectAudio(); Log.w(TAG, "startVoiceRecognition: audio is still active, please wait for audio to" + " be disconnected, disconnectAudio() returned " + status + ", active device is " + mActiveDevice); Loading Loading @@ -1327,8 +1333,10 @@ public class HeadsetService extends ProfileService { } } if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (!disconnectAudio(mActiveDevice)) { Log.w(TAG, "removeActiveDevice: disconnectAudio failed on " + mActiveDevice); int disconnectStatus = disconnectAudio(mActiveDevice); if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "removeActiveDevice: disconnectAudio failed on " + mActiveDevice + " with status code " + disconnectStatus); } } mActiveDevice = null; Loading Loading @@ -1366,9 +1374,10 @@ public class HeadsetService extends ProfileService { BluetoothDevice previousActiveDevice = mActiveDevice; mActiveDevice = device; if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (!disconnectAudio(previousActiveDevice)) { int disconnectStatus = disconnectAudio(previousActiveDevice); if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { Log.e(TAG, "setActiveDevice: fail to disconnectAudio from " + previousActiveDevice); + previousActiveDevice + " with status code " + disconnectStatus); if (previousActiveDevice == null) { removeActiveDevice(); } else { Loading @@ -1380,8 +1389,10 @@ public class HeadsetService extends ProfileService { broadcastActiveDevice(mActiveDevice); } else if (shouldPersistAudio()) { broadcastActiveDevice(mActiveDevice); if (!connectAudio(mActiveDevice)) { Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice); int connectStatus = connectAudio(mActiveDevice); if (connectStatus != BluetoothStatusCodes.SUCCESS) { Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice + " with status code " + connectStatus); if (previousActiveDevice == null) { removeActiveDevice(); } else { Loading @@ -1408,45 +1419,46 @@ public class HeadsetService extends ProfileService { } } boolean connectAudio() { int connectAudio() { synchronized (mStateMachines) { BluetoothDevice device = mActiveDevice; if (device == null) { Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString()); return false; return BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES; } return connectAudio(device); } } boolean connectAudio(BluetoothDevice device) { int connectAudio(BluetoothDevice device) { Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString()); synchronized (mStateMachines) { if (!isScoAcceptable(device)) { Log.w(TAG, "connectAudio, rejected SCO request to " + device); return false; } final HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting"); return false; return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; } int scoConnectionAllowedState = isScoAcceptable(device); if (scoConnectionAllowedState != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "connectAudio, rejected SCO request to " + device); return scoConnectionAllowedState; } if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { Log.w(TAG, "connectAudio: profile not connected"); return false; return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; } if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { logD("connectAudio: audio is not idle for device " + device); return true; return BluetoothStatusCodes.SUCCESS; } if (isAudioOn()) { Log.w(TAG, "connectAudio: audio is not idle, current audio devices are " + Arrays.toString(getNonIdleAudioDevices().toArray())); return false; return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED; } stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); } return true; return BluetoothStatusCodes.SUCCESS; } private List<BluetoothDevice> getNonIdleAudioDevices() { Loading @@ -1461,38 +1473,30 @@ public class HeadsetService extends ProfileService { return devices; } boolean disconnectAudio() { boolean result = false; int disconnectAudio() { synchronized (mStateMachines) { for (BluetoothDevice device : getNonIdleAudioDevices()) { if (disconnectAudio(device)) { result = true; } else { Log.e(TAG, "disconnectAudio() from " + device + " failed"); } List<BluetoothDevice> activeAudioDevices = getNonIdleAudioDevices(); BluetoothDevice activeAudioDevice = activeAudioDevices.get(activeAudioDevices.size() - 1); return disconnectAudio(activeAudioDevice); } } if (!result) { logD("disconnectAudio() no active audio connection"); } return result; } boolean disconnectAudio(BluetoothDevice device) { int disconnectAudio(BluetoothDevice device) { synchronized (mStateMachines) { Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString()); final HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting"); return false; return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; } if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device); return false; return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED; } stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); } return true; return BluetoothStatusCodes.SUCCESS; } boolean isVirtualCallStarted() { Loading Loading @@ -1522,7 +1526,7 @@ public class HeadsetService extends ProfileService { // Audio should not be on when no audio mode is active if (isAudioOn()) { // Disconnect audio so that API user can try later boolean status = disconnectAudio(); int status = disconnectAudio(); Log.w(TAG, "startScoUsingVirtualVoiceCall: audio is still active, please wait for " + "audio to be disconnected, disconnectAudio() returned " + status + ", active device is " + mActiveDevice); Loading Loading @@ -1684,7 +1688,7 @@ public class HeadsetService extends ProfileService { // Audio should not be on when no audio mode is active if (isAudioOn()) { // Disconnect audio so that user can try later boolean status = disconnectAudio(); int status = disconnectAudio(); Log.w(TAG, "startVoiceRecognitionByHeadset: audio is still active, please wait for" + " audio to be disconnected, disconnectAudio() returned " + status + ", active device is " + mActiveDevice); Loading Loading @@ -1739,9 +1743,10 @@ public class HeadsetService extends ProfileService { mVoiceRecognitionTimeoutEvent = null; } if (mVoiceRecognitionStarted) { if (!disconnectAudio()) { int disconnectStatus = disconnectAudio(); if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from " + fromDevice); + fromDevice + " with status code " + disconnectStatus); } mVoiceRecognitionStarted = false; } Loading Loading @@ -1943,10 +1948,12 @@ public class HeadsetService extends ProfileService { if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (mActiveDevice != null && !mActiveDevice.equals(device) && shouldPersistAudio()) { if (!connectAudio(mActiveDevice)) { int connectStatus = connectAudio(mActiveDevice); if (connectStatus != BluetoothStatusCodes.SUCCESS) { Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect" + " audio to new " + "active device " + mActiveDevice + ", after " + device + " is disconnected from SCO"); + ", after " + device + " is disconnected from SCO due to" + " status code " + connectStatus); } } } Loading Loading @@ -2020,36 +2027,38 @@ public class HeadsetService extends ProfileService { } /** * Checks if SCO should be connected at current system state * Checks if SCO should be connected at current system state. Returns * {@link BluetoothStatusCodes#SUCCESS} if SCO is allowed to be connected or an error code on * failure. * * @param device device for SCO to be connected * @return true if SCO is allowed to be connected * @return whether SCO can be connected */ public boolean isScoAcceptable(BluetoothDevice device) { public int isScoAcceptable(BluetoothDevice device) { synchronized (mStateMachines) { if (device == null || !device.equals(mActiveDevice)) { Log.w(TAG, "isScoAcceptable: rejected SCO since " + device + " is not the current active device " + mActiveDevice); return false; return BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE; } if (mForceScoAudio) { return true; return BluetoothStatusCodes.SUCCESS; } if (!mAudioRouteAllowed) { Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed"); return false; return BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED; } if (mVoiceRecognitionStarted || mVirtualCallStarted) { return true; return BluetoothStatusCodes.SUCCESS; } if (shouldCallAudioBeActive()) { return true; return BluetoothStatusCodes.SUCCESS; } Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" + mSystemInterface.isRinging() + ", inbandRinging=" + isInbandRingingEnabled() + ", isVirtualCallStarted=" + mVirtualCallStarted); return false; return BluetoothStatusCodes.ERROR_CALL_ACTIVE; } } Loading
android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +3 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.hfp.BluetoothHfpProtoEnums; import android.content.Intent; import android.media.AudioManager; Loading Loading @@ -1110,7 +1111,7 @@ public class HeadsetStateMachine extends StateMachine { stateLogD("processAudioEvent, state=" + state); switch (state) { case HeadsetHalConstants.AUDIO_STATE_CONNECTED: if (!mHeadsetService.isScoAcceptable(mDevice)) { if (mHeadsetService.isScoAcceptable(mDevice) != BluetoothStatusCodes.SUCCESS) { stateLogW("processAudioEvent: reject incoming audio connection"); if (!mNativeInterface.disconnectAudio(mDevice)) { stateLogE("processAudioEvent: failed to disconnect audio"); Loading @@ -1124,7 +1125,7 @@ public class HeadsetStateMachine extends StateMachine { transitionTo(mAudioOn); break; case HeadsetHalConstants.AUDIO_STATE_CONNECTING: if (!mHeadsetService.isScoAcceptable(mDevice)) { if (mHeadsetService.isScoAcceptable(mDevice) != BluetoothStatusCodes.SUCCESS) { stateLogW("processAudioEvent: reject incoming pending audio connection"); if (!mNativeInterface.disconnectAudio(mDevice)) { stateLogE("processAudioEvent: failed to disconnect pending audio"); Loading
android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +32 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes