Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | package com.android.server.audio; |
| 17 | |
| 18 | import android.annotation.NonNull; |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 19 | import android.annotation.Nullable; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 20 | import android.bluetooth.BluetoothA2dp; |
| 21 | import android.bluetooth.BluetoothAdapter; |
| 22 | import android.bluetooth.BluetoothClass; |
| 23 | import android.bluetooth.BluetoothCodecConfig; |
| 24 | import android.bluetooth.BluetoothCodecStatus; |
| 25 | import android.bluetooth.BluetoothDevice; |
| 26 | import android.bluetooth.BluetoothHeadset; |
| 27 | import android.bluetooth.BluetoothHearingAid; |
| 28 | import android.bluetooth.BluetoothProfile; |
| 29 | import android.content.Intent; |
| 30 | import android.media.AudioManager; |
| 31 | import android.media.AudioSystem; |
| 32 | import android.os.Binder; |
| 33 | import android.os.IBinder; |
| 34 | import android.os.RemoteException; |
| 35 | import android.os.UserHandle; |
| 36 | import android.provider.Settings; |
| 37 | import android.util.Log; |
| 38 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 39 | import com.android.internal.annotations.GuardedBy; |
| 40 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 41 | import java.util.ArrayList; |
| 42 | import java.util.List; |
| 43 | import java.util.NoSuchElementException; |
| 44 | import java.util.Objects; |
| 45 | |
| 46 | /** |
| 47 | * @hide |
| 48 | * Class to encapsulate all communication with Bluetooth services |
| 49 | */ |
| 50 | public class BtHelper { |
| 51 | |
| 52 | private static final String TAG = "AS.BtHelper"; |
| 53 | |
| 54 | private final @NonNull AudioDeviceBroker mDeviceBroker; |
| 55 | |
| 56 | BtHelper(@NonNull AudioDeviceBroker broker) { |
| 57 | mDeviceBroker = broker; |
| 58 | } |
| 59 | |
| 60 | // List of clients having issued a SCO start request |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 61 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 62 | private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 63 | |
| 64 | // BluetoothHeadset API to control SCO connection |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 65 | private @Nullable BluetoothHeadset mBluetoothHeadset; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 66 | |
| 67 | // Bluetooth headset device |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 68 | private @Nullable BluetoothDevice mBluetoothHeadsetDevice; |
| 69 | |
| 70 | private @Nullable BluetoothHearingAid mHearingAid; |
| 71 | |
| 72 | // Reference to BluetoothA2dp to query for AbsoluteVolume. |
| 73 | private @Nullable BluetoothA2dp mA2dp; |
| 74 | |
| 75 | // If absolute volume is supported in AVRCP device |
| 76 | private boolean mAvrcpAbsVolSupported = false; |
| 77 | |
| 78 | // Current connection state indicated by bluetooth headset |
| 79 | private int mScoConnectionState; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 80 | |
| 81 | // Indicate if SCO audio connection is currently active and if the initiator is |
| 82 | // audio service (internal) or bluetooth headset (external) |
| 83 | private int mScoAudioState; |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 84 | |
| 85 | // Indicates the mode used for SCO audio connection. The mode is virtual call if the request |
| 86 | // originated from an app targeting an API version before JB MR2 and raw audio after that. |
| 87 | private int mScoAudioMode; |
| 88 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 89 | // SCO audio state is not active |
| 90 | private static final int SCO_STATE_INACTIVE = 0; |
| 91 | // SCO audio activation request waiting for headset service to connect |
| 92 | private static final int SCO_STATE_ACTIVATE_REQ = 1; |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 93 | // SCO audio state is active due to an action in BT handsfree (either voice recognition or |
| 94 | // in call audio) |
| 95 | private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 96 | // SCO audio state is active or starting due to a request from AudioManager API |
| 97 | private static final int SCO_STATE_ACTIVE_INTERNAL = 3; |
| 98 | // SCO audio deactivation request waiting for headset service to connect |
| 99 | private static final int SCO_STATE_DEACTIVATE_REQ = 4; |
| 100 | // SCO audio deactivation in progress, waiting for Bluetooth audio intent |
| 101 | private static final int SCO_STATE_DEACTIVATING = 5; |
| 102 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 103 | // SCO audio mode is undefined |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 104 | /*package*/ static final int SCO_MODE_UNDEFINED = -1; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 105 | // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall()) |
| 106 | /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0; |
| 107 | // SCO audio mode is raw audio (BluetoothHeadset.connectAudio()) |
| 108 | private static final int SCO_MODE_RAW = 1; |
| 109 | // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition()) |
| 110 | private static final int SCO_MODE_VR = 2; |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 111 | // max valid SCO audio mode values |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 112 | private static final int SCO_MODE_MAX = 2; |
| 113 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 114 | private static final int BT_HEARING_AID_GAIN_MIN = -128; |
| 115 | |
Andy Hung | 6983695 | 2020-03-26 01:00:15 -0700 | [diff] [blame] | 116 | /** |
| 117 | * Returns a string representation of the scoAudioMode. |
| 118 | */ |
| 119 | public static String scoAudioModeToString(int scoAudioMode) { |
| 120 | switch (scoAudioMode) { |
| 121 | case SCO_MODE_UNDEFINED: |
| 122 | return "SCO_MODE_UNDEFINED"; |
| 123 | case SCO_MODE_VIRTUAL_CALL: |
| 124 | return "SCO_MODE_VIRTUAL_CALL"; |
| 125 | case SCO_MODE_RAW: |
| 126 | return "SCO_MODE_RAW"; |
| 127 | case SCO_MODE_VR: |
| 128 | return "SCO_MODE_VR"; |
| 129 | default: |
| 130 | return "SCO_MODE_(" + scoAudioMode + ")"; |
| 131 | } |
| 132 | } |
| 133 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 134 | //---------------------------------------------------------------------- |
| 135 | /*package*/ static class BluetoothA2dpDeviceInfo { |
| 136 | private final @NonNull BluetoothDevice mBtDevice; |
| 137 | private final int mVolume; |
Jean-Michel Trivi | e9e4a3d | 2020-04-22 21:38:07 -0700 | [diff] [blame] | 138 | private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 139 | |
| 140 | BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) { |
| 141 | this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT); |
| 142 | } |
| 143 | |
| 144 | BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) { |
| 145 | mBtDevice = btDevice; |
| 146 | mVolume = volume; |
| 147 | mCodec = codec; |
| 148 | } |
| 149 | |
| 150 | public @NonNull BluetoothDevice getBtDevice() { |
| 151 | return mBtDevice; |
| 152 | } |
| 153 | |
| 154 | public int getVolume() { |
| 155 | return mVolume; |
| 156 | } |
| 157 | |
Jean-Michel Trivi | e9e4a3d | 2020-04-22 21:38:07 -0700 | [diff] [blame] | 158 | public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 159 | return mCodec; |
| 160 | } |
Jean-Michel Trivi | b3580a8 | 2019-06-24 10:39:19 -0700 | [diff] [blame] | 161 | |
| 162 | // redefine equality op so we can match messages intended for this device |
| 163 | @Override |
| 164 | public boolean equals(Object o) { |
Jean-Michel Trivi | e9e4a3d | 2020-04-22 21:38:07 -0700 | [diff] [blame] | 165 | if (o == null) { |
| 166 | return false; |
| 167 | } |
| 168 | if (this == o) { |
| 169 | return true; |
| 170 | } |
| 171 | if (o instanceof BluetoothA2dpDeviceInfo) { |
| 172 | return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice()); |
| 173 | } |
| 174 | return false; |
Jean-Michel Trivi | b3580a8 | 2019-06-24 10:39:19 -0700 | [diff] [blame] | 175 | } |
Jean-Michel Trivi | e9e4a3d | 2020-04-22 21:38:07 -0700 | [diff] [blame] | 176 | |
| 177 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 178 | } |
| 179 | |
Aniket Kumar Lata | d7c9598 | 2019-02-26 16:23:05 -0800 | [diff] [blame] | 180 | // A2DP device events |
| 181 | /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0; |
| 182 | /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1; |
| 183 | |
| 184 | /*package*/ static String a2dpDeviceEventToString(int event) { |
| 185 | switch (event) { |
| 186 | case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE"; |
| 187 | case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE"; |
| 188 | default: |
| 189 | return new String("invalid event:" + event); |
| 190 | } |
| 191 | } |
| 192 | |
Jack He | cb49964 | 2019-04-03 00:48:49 -0700 | [diff] [blame] | 193 | /*package*/ @NonNull static String getName(@NonNull BluetoothDevice device) { |
| 194 | final String deviceName = device.getName(); |
| 195 | if (deviceName == null) { |
| 196 | return ""; |
| 197 | } |
| 198 | return deviceName; |
| 199 | } |
| 200 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 201 | //---------------------------------------------------------------------- |
| 202 | // Interface for AudioDeviceBroker |
| 203 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 204 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 205 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 206 | /*package*/ synchronized void onSystemReady() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 207 | mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; |
| 208 | resetBluetoothSco(); |
| 209 | getBluetoothHeadset(); |
| 210 | |
| 211 | //FIXME: this is to maintain compatibility with deprecated intent |
| 212 | // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. |
| 213 | Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); |
| 214 | newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, |
| 215 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 216 | sendStickyBroadcastToAll(newIntent); |
| 217 | |
| 218 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); |
| 219 | if (adapter != null) { |
| 220 | adapter.getProfileProxy(mDeviceBroker.getContext(), |
| 221 | mBluetoothProfileServiceListener, BluetoothProfile.A2DP); |
| 222 | adapter.getProfileProxy(mDeviceBroker.getContext(), |
| 223 | mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID); |
| 224 | } |
| 225 | } |
| 226 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 227 | /*package*/ synchronized void onAudioServerDiedRestoreA2dp() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 228 | final int forMed = mDeviceBroker.getBluetoothA2dpEnabled() |
| 229 | ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP; |
| 230 | mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()"); |
| 231 | } |
| 232 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 233 | /*package*/ synchronized boolean isAvrcpAbsoluteVolumeSupported() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 234 | return (mA2dp != null && mAvrcpAbsVolSupported); |
| 235 | } |
| 236 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 237 | /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 238 | mAvrcpAbsVolSupported = supported; |
Jean-Michel Trivi | f734525 | 2019-05-29 14:25:02 -0700 | [diff] [blame] | 239 | Log.i(TAG, "setAvrcpAbsoluteVolumeSupported supported=" + supported); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 240 | } |
| 241 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 242 | /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) { |
| 243 | if (mA2dp == null) { |
| 244 | if (AudioService.DEBUG_VOL) { |
Jean-Michel Trivi | f734525 | 2019-05-29 14:25:02 -0700 | [diff] [blame] | 245 | AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent( |
| 246 | "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG)); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 247 | return; |
| 248 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 249 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 250 | if (!mAvrcpAbsVolSupported) { |
Jean-Michel Trivi | f734525 | 2019-05-29 14:25:02 -0700 | [diff] [blame] | 251 | AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent( |
| 252 | "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG)); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 253 | return; |
| 254 | } |
| 255 | if (AudioService.DEBUG_VOL) { |
| 256 | Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index); |
| 257 | } |
| 258 | AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( |
Jean-Michel Trivi | 734819a | 2019-02-04 15:44:28 -0800 | [diff] [blame] | 259 | AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index)); |
| 260 | mA2dp.setAvrcpAbsoluteVolume(index); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 261 | } |
| 262 | |
Jean-Michel Trivi | e9e4a3d | 2020-04-22 21:38:07 -0700 | [diff] [blame] | 263 | /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec( |
| 264 | @NonNull BluetoothDevice device) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 265 | if (mA2dp == null) { |
| 266 | return AudioSystem.AUDIO_FORMAT_DEFAULT; |
| 267 | } |
| 268 | final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device); |
| 269 | if (btCodecStatus == null) { |
| 270 | return AudioSystem.AUDIO_FORMAT_DEFAULT; |
| 271 | } |
| 272 | final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig(); |
| 273 | if (btCodecConfig == null) { |
| 274 | return AudioSystem.AUDIO_FORMAT_DEFAULT; |
| 275 | } |
Jean-Michel Trivi | e9e4a3d | 2020-04-22 21:38:07 -0700 | [diff] [blame] | 276 | return AudioSystem.bluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 277 | } |
| 278 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 279 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 280 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 281 | /*package*/ synchronized void receiveBtEvent(Intent intent) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 282 | final String action = intent.getAction(); |
| 283 | if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { |
| 284 | BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); |
| 285 | setBtScoActiveDevice(btDevice); |
| 286 | } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { |
| 287 | boolean broadcast = false; |
| 288 | int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 289 | int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); |
| 290 | // broadcast intent if the connection was initated by AudioService |
| 291 | if (!mScoClients.isEmpty() |
| 292 | && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL |
| 293 | || mScoAudioState == SCO_STATE_ACTIVATE_REQ |
| 294 | || mScoAudioState == SCO_STATE_DEACTIVATE_REQ |
| 295 | || mScoAudioState == SCO_STATE_DEACTIVATING)) { |
| 296 | broadcast = true; |
| 297 | } |
| 298 | switch (btState) { |
| 299 | case BluetoothHeadset.STATE_AUDIO_CONNECTED: |
| 300 | scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; |
| 301 | if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL |
| 302 | && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { |
| 303 | mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; |
| 304 | } |
| 305 | mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent"); |
| 306 | break; |
| 307 | case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: |
| 308 | mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent"); |
| 309 | scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; |
Eric Laurent | 4394d3b | 2020-05-10 17:10:06 -0700 | [diff] [blame] | 310 | // There are two cases where we want to immediately reconnect audio: |
| 311 | // 1) If a new start request was received while disconnecting: this was |
| 312 | // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ. |
| 313 | // 2) If audio was connected then disconnected via Bluetooth APIs and |
| 314 | // we still have pending activation requests by apps: this is indicated by |
| 315 | // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty. |
| 316 | if (mScoAudioState == SCO_STATE_ACTIVATE_REQ |
| 317 | || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL |
| 318 | && !mScoClients.isEmpty())) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 319 | if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null |
| 320 | && connectBluetoothScoAudioHelper(mBluetoothHeadset, |
| 321 | mBluetoothHeadsetDevice, mScoAudioMode)) { |
| 322 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
| 323 | broadcast = false; |
| 324 | break; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 325 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 326 | } |
| 327 | // Tear down SCO if disconnected from external |
Eric Laurent | 4394d3b | 2020-05-10 17:10:06 -0700 | [diff] [blame] | 328 | if (mScoAudioState == SCO_STATE_DEACTIVATING) { |
| 329 | clearAllScoClients(0, false); |
| 330 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 331 | mScoAudioState = SCO_STATE_INACTIVE; |
| 332 | break; |
| 333 | case BluetoothHeadset.STATE_AUDIO_CONNECTING: |
| 334 | if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL |
| 335 | && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { |
| 336 | mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; |
| 337 | } |
Myles Watson | 866b1bb | 2019-12-03 11:18:27 -0800 | [diff] [blame] | 338 | broadcast = false; |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 339 | break; |
| 340 | default: |
| 341 | // do not broadcast CONNECTING or invalid state |
| 342 | broadcast = false; |
| 343 | break; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 344 | } |
| 345 | if (broadcast) { |
| 346 | broadcastScoConnectionState(scoAudioState); |
| 347 | //FIXME: this is to maintain compatibility with deprecated intent |
| 348 | // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. |
| 349 | Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); |
| 350 | newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); |
| 351 | sendStickyBroadcastToAll(newIntent); |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /** |
| 357 | * |
| 358 | * @return false if SCO isn't connected |
| 359 | */ |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 360 | /*package*/ synchronized boolean isBluetoothScoOn() { |
| 361 | if ((mBluetoothHeadset != null) |
| 362 | && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) |
| 363 | != BluetoothHeadset.STATE_AUDIO_CONNECTED)) { |
| 364 | Log.w(TAG, "isBluetoothScoOn(true) returning false because " |
| 365 | + mBluetoothHeadsetDevice + " is not in audio connected mode"); |
| 366 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 367 | } |
| 368 | return true; |
| 369 | } |
| 370 | |
| 371 | /** |
| 372 | * Disconnect all SCO connections started by {@link AudioManager} except those started by |
| 373 | * {@param exceptPid} |
| 374 | * |
| 375 | * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept |
| 376 | */ |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 377 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 378 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 379 | /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { |
| 380 | checkScoAudioState(); |
| 381 | if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { |
| 382 | return; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 383 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 384 | clearAllScoClients(exceptPid, true); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 385 | } |
| 386 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 387 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 388 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 389 | /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 390 | @NonNull String eventSource) { |
| 391 | ScoClient client = getScoClient(cb, true); |
| 392 | // The calling identity must be cleared before calling ScoClient.incCount(). |
| 393 | // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs |
| 394 | // and this must be done on behalf of system server to make sure permissions are granted. |
| 395 | // The caller identity must be cleared after getScoClient() because it is needed if a new |
| 396 | // client is created. |
| 397 | final long ident = Binder.clearCallingIdentity(); |
| 398 | try { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 399 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 400 | client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 401 | } catch (NullPointerException e) { |
| 402 | Log.e(TAG, "Null ScoClient", e); |
| 403 | } |
| 404 | Binder.restoreCallingIdentity(ident); |
| 405 | } |
| 406 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 407 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 408 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 409 | /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, |
| 410 | @NonNull String eventSource) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 411 | ScoClient client = getScoClient(cb, false); |
| 412 | // The calling identity must be cleared before calling ScoClient.decCount(). |
| 413 | // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs |
| 414 | // and this must be done on behalf of system server to make sure permissions are granted. |
| 415 | final long ident = Binder.clearCallingIdentity(); |
| 416 | if (client != null) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 417 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 418 | client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, |
| 419 | SCO_MODE_VIRTUAL_CALL); |
| 420 | // If a disconnection is pending, the client will be removed whne clearAllScoClients() |
| 421 | // is called form receiveBtEvent() |
| 422 | if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ |
| 423 | && mScoAudioState != SCO_STATE_DEACTIVATING) { |
| 424 | client.remove(false /*stop */, true /*unregister*/); |
| 425 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 426 | } |
| 427 | Binder.restoreCallingIdentity(ident); |
| 428 | } |
| 429 | |
| 430 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 431 | /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { |
| 432 | if (mHearingAid == null) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 433 | if (AudioService.DEBUG_VOL) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 434 | Log.i(TAG, "setHearingAidVolume: null mHearingAid"); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 435 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 436 | return; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 437 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 438 | //hearing aid expect volume value in range -128dB to 0dB |
| 439 | int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10, |
| 440 | AudioSystem.DEVICE_OUT_HEARING_AID); |
| 441 | if (gainDB < BT_HEARING_AID_GAIN_MIN) { |
| 442 | gainDB = BT_HEARING_AID_GAIN_MIN; |
| 443 | } |
| 444 | if (AudioService.DEBUG_VOL) { |
| 445 | Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx=" |
| 446 | + index + " gain=" + gainDB); |
| 447 | } |
| 448 | AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( |
| 449 | AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB)); |
| 450 | mHearingAid.setVolume(gainDB); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 451 | } |
| 452 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 453 | /*package*/ synchronized void onBroadcastScoConnectionState(int state) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 454 | if (state == mScoConnectionState) { |
| 455 | return; |
| 456 | } |
| 457 | Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); |
| 458 | newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); |
| 459 | newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, |
| 460 | mScoConnectionState); |
| 461 | sendStickyBroadcastToAll(newIntent); |
| 462 | mScoConnectionState = state; |
| 463 | } |
| 464 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 465 | /*package*/ synchronized void disconnectAllBluetoothProfiles() { |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 466 | mDeviceBroker.postDisconnectA2dp(); |
| 467 | mDeviceBroker.postDisconnectA2dpSink(); |
| 468 | mDeviceBroker.postDisconnectHeadset(); |
| 469 | mDeviceBroker.postDisconnectHearingAid(); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 470 | } |
| 471 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 472 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 473 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 474 | /*package*/ synchronized void resetBluetoothSco() { |
| 475 | clearAllScoClients(0, false); |
| 476 | mScoAudioState = SCO_STATE_INACTIVE; |
| 477 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 478 | AudioSystem.setParameters("A2dpSuspended=false"); |
| 479 | mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); |
| 480 | } |
| 481 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 482 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 483 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 484 | /*package*/ synchronized void disconnectHeadset() { |
| 485 | setBtScoActiveDevice(null); |
| 486 | mBluetoothHeadset = null; |
| 487 | } |
| 488 | |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 489 | /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { |
| 490 | mA2dp = a2dp; |
| 491 | final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); |
| 492 | if (deviceList.isEmpty()) { |
| 493 | return; |
| 494 | } |
| 495 | final BluetoothDevice btDevice = deviceList.get(0); |
Jean-Michel Trivi | b3580a8 | 2019-06-24 10:39:19 -0700 | [diff] [blame] | 496 | // the device is guaranteed CONNECTED |
| 497 | mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice, |
| 498 | BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1); |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 499 | } |
| 500 | |
| 501 | /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { |
| 502 | final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); |
| 503 | if (deviceList.isEmpty()) { |
| 504 | return; |
| 505 | } |
| 506 | final BluetoothDevice btDevice = deviceList.get(0); |
| 507 | final @BluetoothProfile.BtProfileState int state = |
| 508 | profile.getConnectionState(btDevice); |
| 509 | mDeviceBroker.postSetA2dpSourceConnectionState( |
| 510 | state, new BluetoothA2dpDeviceInfo(btDevice)); |
| 511 | } |
| 512 | |
| 513 | /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { |
| 514 | mHearingAid = hearingAid; |
| 515 | final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); |
| 516 | if (deviceList.isEmpty()) { |
| 517 | return; |
| 518 | } |
| 519 | final BluetoothDevice btDevice = deviceList.get(0); |
| 520 | final @BluetoothProfile.BtProfileState int state = |
| 521 | mHearingAid.getConnectionState(btDevice); |
Jean-Michel Trivi | fc86cfa | 2019-03-01 10:15:47 -0800 | [diff] [blame] | 522 | mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 523 | btDevice, state, |
| 524 | /*suppressNoisyIntent*/ false, |
| 525 | /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, |
| 526 | /*eventSource*/ "mBluetoothProfileServiceListener"); |
| 527 | } |
| 528 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 529 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 530 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 531 | /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { |
| 532 | // Discard timeout message |
| 533 | mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); |
| 534 | mBluetoothHeadset = headset; |
| 535 | setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice()); |
| 536 | // Refresh SCO audio state |
| 537 | checkScoAudioState(); |
| 538 | if (mScoAudioState != SCO_STATE_ACTIVATE_REQ |
| 539 | && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { |
| 540 | return; |
| 541 | } |
| 542 | boolean status = false; |
| 543 | if (mBluetoothHeadsetDevice != null) { |
| 544 | switch (mScoAudioState) { |
| 545 | case SCO_STATE_ACTIVATE_REQ: |
| 546 | status = connectBluetoothScoAudioHelper( |
| 547 | mBluetoothHeadset, |
| 548 | mBluetoothHeadsetDevice, mScoAudioMode); |
| 549 | if (status) { |
| 550 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
| 551 | } |
| 552 | break; |
| 553 | case SCO_STATE_DEACTIVATE_REQ: |
| 554 | status = disconnectBluetoothScoAudioHelper( |
| 555 | mBluetoothHeadset, |
| 556 | mBluetoothHeadsetDevice, mScoAudioMode); |
| 557 | if (status) { |
| 558 | mScoAudioState = SCO_STATE_DEACTIVATING; |
| 559 | } |
| 560 | break; |
| 561 | } |
| 562 | } |
| 563 | if (!status) { |
| 564 | mScoAudioState = SCO_STATE_INACTIVE; |
| 565 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 566 | } |
| 567 | } |
| 568 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 569 | //---------------------------------------------------------------------- |
| 570 | private void broadcastScoConnectionState(int state) { |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 571 | mDeviceBroker.postBroadcastScoConnectionState(state); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 572 | } |
| 573 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 574 | private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { |
| 575 | if (btDevice == null) { |
| 576 | return true; |
| 577 | } |
| 578 | String address = btDevice.getAddress(); |
| 579 | BluetoothClass btClass = btDevice.getBluetoothClass(); |
| 580 | int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; |
| 581 | int[] outDeviceTypes = { |
| 582 | AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, |
| 583 | AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, |
| 584 | AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
| 585 | }; |
| 586 | if (btClass != null) { |
| 587 | switch (btClass.getDeviceClass()) { |
| 588 | case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: |
| 589 | case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: |
| 590 | outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET }; |
| 591 | break; |
| 592 | case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: |
| 593 | outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; |
| 594 | break; |
| 595 | } |
| 596 | } |
| 597 | if (!BluetoothAdapter.checkBluetoothAddress(address)) { |
| 598 | address = ""; |
| 599 | } |
Jack He | cb49964 | 2019-04-03 00:48:49 -0700 | [diff] [blame] | 600 | String btDeviceName = getName(btDevice); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 601 | boolean result = false; |
| 602 | if (isActive) { |
| 603 | result |= mDeviceBroker.handleDeviceConnection( |
| 604 | isActive, outDeviceTypes[0], address, btDeviceName); |
| 605 | } else { |
| 606 | for (int outDeviceType : outDeviceTypes) { |
| 607 | result |= mDeviceBroker.handleDeviceConnection( |
| 608 | isActive, outDeviceType, address, btDeviceName); |
| 609 | } |
| 610 | } |
| 611 | // handleDeviceConnection() && result to make sure the method get executed |
| 612 | result = mDeviceBroker.handleDeviceConnection( |
| 613 | isActive, inDevice, address, btDeviceName) && result; |
| 614 | return result; |
| 615 | } |
| 616 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 617 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 618 | //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 619 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 620 | private void setBtScoActiveDevice(BluetoothDevice btDevice) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 621 | Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); |
| 622 | final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; |
| 623 | if (Objects.equals(btDevice, previousActiveDevice)) { |
| 624 | return; |
| 625 | } |
| 626 | if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) { |
| 627 | Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device " |
| 628 | + previousActiveDevice); |
| 629 | } |
| 630 | if (!handleBtScoActiveDeviceChange(btDevice, true)) { |
| 631 | Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice); |
| 632 | // set mBluetoothHeadsetDevice to null when failing to add new device |
| 633 | btDevice = null; |
| 634 | } |
| 635 | mBluetoothHeadsetDevice = btDevice; |
| 636 | if (mBluetoothHeadsetDevice == null) { |
| 637 | resetBluetoothSco(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 638 | } |
| 639 | } |
| 640 | |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 641 | // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async |
| 642 | // methods inside listener. |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 643 | private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = |
| 644 | new BluetoothProfile.ServiceListener() { |
| 645 | public void onServiceConnected(int profile, BluetoothProfile proxy) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 646 | switch(profile) { |
| 647 | case BluetoothProfile.A2DP: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 648 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 649 | "BT profile service: connecting A2DP profile")); |
| 650 | mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 651 | break; |
| 652 | |
| 653 | case BluetoothProfile.A2DP_SINK: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 654 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 655 | "BT profile service: connecting A2DP_SINK profile")); |
| 656 | mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 657 | break; |
| 658 | |
| 659 | case BluetoothProfile.HEADSET: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 660 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 661 | "BT profile service: connecting HEADSET profile")); |
| 662 | mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 663 | break; |
| 664 | |
| 665 | case BluetoothProfile.HEARING_AID: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 666 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 667 | "BT profile service: connecting HEARING_AID profile")); |
| 668 | mDeviceBroker.postBtHearingAidProfileConnected( |
| 669 | (BluetoothHearingAid) proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 670 | break; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 671 | default: |
| 672 | break; |
| 673 | } |
| 674 | } |
| 675 | public void onServiceDisconnected(int profile) { |
| 676 | |
| 677 | switch (profile) { |
| 678 | case BluetoothProfile.A2DP: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 679 | mDeviceBroker.postDisconnectA2dp(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 680 | break; |
| 681 | |
| 682 | case BluetoothProfile.A2DP_SINK: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 683 | mDeviceBroker.postDisconnectA2dpSink(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 684 | break; |
| 685 | |
| 686 | case BluetoothProfile.HEADSET: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 687 | mDeviceBroker.postDisconnectHeadset(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 688 | break; |
| 689 | |
| 690 | case BluetoothProfile.HEARING_AID: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 691 | mDeviceBroker.postDisconnectHearingAid(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 692 | break; |
| 693 | |
| 694 | default: |
| 695 | break; |
| 696 | } |
| 697 | } |
| 698 | }; |
| 699 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 700 | //---------------------------------------------------------------------- |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 701 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 702 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 703 | /*package*/ synchronized void scoClientDied(Object obj) { |
| 704 | final ScoClient client = (ScoClient) obj; |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 705 | client.remove(true /*stop*/, false /*unregister*/); |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 706 | Log.w(TAG, "SCO client died"); |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 707 | } |
| 708 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 709 | private class ScoClient implements IBinder.DeathRecipient { |
| 710 | private IBinder mCb; // To be notified of client's death |
| 711 | private int mCreatorPid; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 712 | |
| 713 | ScoClient(IBinder cb) { |
| 714 | mCb = cb; |
| 715 | mCreatorPid = Binder.getCallingPid(); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 716 | } |
| 717 | |
| 718 | public void registerDeathRecipient() { |
| 719 | try { |
| 720 | mCb.linkToDeath(this, 0); |
| 721 | } catch (RemoteException e) { |
| 722 | Log.w(TAG, "ScoClient could not link to " + mCb + " binder death"); |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | public void unregisterDeathRecipient() { |
| 727 | try { |
| 728 | mCb.unlinkToDeath(this, 0); |
| 729 | } catch (NoSuchElementException e) { |
| 730 | Log.w(TAG, "ScoClient could not not unregistered to binder"); |
| 731 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 732 | } |
| 733 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 734 | @Override |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 735 | public void binderDied() { |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 736 | // process this from DeviceBroker's message queue to take the right locks since |
| 737 | // this event can impact SCO mode and requires querying audio mode stack |
| 738 | mDeviceBroker.postScoClientDied(this); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 739 | } |
| 740 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 741 | IBinder getBinder() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 742 | return mCb; |
| 743 | } |
| 744 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 745 | int getPid() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 746 | return mCreatorPid; |
| 747 | } |
| 748 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 749 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 750 | //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 751 | @GuardedBy("BtHelper.this") |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 752 | private boolean requestScoState(int state, int scoAudioMode) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 753 | checkScoAudioState(); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 754 | if (mScoClients.size() != 1) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 755 | Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 756 | + ", num SCO clients=" + mScoClients.size()); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 757 | return true; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 758 | } |
| 759 | if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { |
| 760 | // Make sure that the state transitions to CONNECTING even if we cannot initiate |
| 761 | // the connection. |
| 762 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); |
| 763 | // Accept SCO audio activation only in NORMAL audio mode or if the mode is |
| 764 | // currently controlled by the same client process. |
Jean-Michel Trivi | 5bbcd44 | 2019-04-18 12:07:34 -0700 | [diff] [blame] | 765 | final int modeOwnerPid = mDeviceBroker.getModeOwnerPid(); |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 766 | if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { |
| 767 | Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " |
| 768 | + modeOwnerPid + " != creatorPid " + mCreatorPid); |
| 769 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 770 | return false; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 771 | } |
| 772 | switch (mScoAudioState) { |
| 773 | case SCO_STATE_INACTIVE: |
| 774 | mScoAudioMode = scoAudioMode; |
| 775 | if (scoAudioMode == SCO_MODE_UNDEFINED) { |
| 776 | mScoAudioMode = SCO_MODE_VIRTUAL_CALL; |
| 777 | if (mBluetoothHeadsetDevice != null) { |
| 778 | mScoAudioMode = Settings.Global.getInt( |
| 779 | mDeviceBroker.getContentResolver(), |
| 780 | "bluetooth_sco_channel_" |
| 781 | + mBluetoothHeadsetDevice.getAddress(), |
| 782 | SCO_MODE_VIRTUAL_CALL); |
| 783 | if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { |
| 784 | mScoAudioMode = SCO_MODE_VIRTUAL_CALL; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 785 | } |
| 786 | } |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 787 | } |
| 788 | if (mBluetoothHeadset == null) { |
| 789 | if (getBluetoothHeadset()) { |
| 790 | mScoAudioState = SCO_STATE_ACTIVATE_REQ; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 791 | } else { |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 792 | Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" |
| 793 | + " connection, mScoAudioMode=" + mScoAudioMode); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 794 | broadcastScoConnectionState( |
| 795 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 796 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 797 | } |
| 798 | break; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 799 | } |
| 800 | if (mBluetoothHeadsetDevice == null) { |
| 801 | Log.w(TAG, "requestScoState: no active device while connecting," |
| 802 | + " mScoAudioMode=" + mScoAudioMode); |
| 803 | broadcastScoConnectionState( |
| 804 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 805 | return false; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 806 | } |
| 807 | if (connectBluetoothScoAudioHelper(mBluetoothHeadset, |
| 808 | mBluetoothHeadsetDevice, mScoAudioMode)) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 809 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 810 | } else { |
| 811 | Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice |
| 812 | + " failed, mScoAudioMode=" + mScoAudioMode); |
| 813 | broadcastScoConnectionState( |
| 814 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 815 | return false; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 816 | } |
| 817 | break; |
| 818 | case SCO_STATE_DEACTIVATING: |
| 819 | mScoAudioState = SCO_STATE_ACTIVATE_REQ; |
| 820 | break; |
| 821 | case SCO_STATE_DEACTIVATE_REQ: |
| 822 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
| 823 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); |
| 824 | break; |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 825 | case SCO_STATE_ACTIVE_INTERNAL: |
| 826 | Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return"); |
| 827 | break; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 828 | default: |
| 829 | Log.w(TAG, "requestScoState: failed to connect in state " |
| 830 | + mScoAudioState + ", scoAudioMode=" + scoAudioMode); |
| 831 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 832 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 833 | } |
| 834 | } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { |
| 835 | switch (mScoAudioState) { |
| 836 | case SCO_STATE_ACTIVE_INTERNAL: |
| 837 | if (mBluetoothHeadset == null) { |
| 838 | if (getBluetoothHeadset()) { |
| 839 | mScoAudioState = SCO_STATE_DEACTIVATE_REQ; |
| 840 | } else { |
| 841 | Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" |
| 842 | + " disconnection, mScoAudioMode=" + mScoAudioMode); |
| 843 | mScoAudioState = SCO_STATE_INACTIVE; |
| 844 | broadcastScoConnectionState( |
| 845 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 846 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 847 | } |
| 848 | break; |
| 849 | } |
| 850 | if (mBluetoothHeadsetDevice == null) { |
| 851 | mScoAudioState = SCO_STATE_INACTIVE; |
| 852 | broadcastScoConnectionState( |
| 853 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 854 | break; |
| 855 | } |
| 856 | if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset, |
| 857 | mBluetoothHeadsetDevice, mScoAudioMode)) { |
| 858 | mScoAudioState = SCO_STATE_DEACTIVATING; |
| 859 | } else { |
| 860 | mScoAudioState = SCO_STATE_INACTIVE; |
| 861 | broadcastScoConnectionState( |
| 862 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 863 | } |
| 864 | break; |
| 865 | case SCO_STATE_ACTIVATE_REQ: |
| 866 | mScoAudioState = SCO_STATE_INACTIVE; |
| 867 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 868 | break; |
| 869 | default: |
| 870 | Log.w(TAG, "requestScoState: failed to disconnect in state " |
| 871 | + mScoAudioState + ", scoAudioMode=" + scoAudioMode); |
| 872 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 873 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 874 | } |
| 875 | } |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 876 | return true; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 877 | } |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 878 | |
| 879 | @GuardedBy("BtHelper.this") |
| 880 | void remove(boolean stop, boolean unregister) { |
| 881 | if (unregister) { |
| 882 | unregisterDeathRecipient(); |
| 883 | } |
| 884 | if (stop) { |
| 885 | requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, |
| 886 | SCO_MODE_VIRTUAL_CALL); |
| 887 | } |
| 888 | mScoClients.remove(this); |
| 889 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 890 | } |
| 891 | |
| 892 | //----------------------------------------------------- |
| 893 | // Utilities |
| 894 | private void sendStickyBroadcastToAll(Intent intent) { |
| 895 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); |
| 896 | final long ident = Binder.clearCallingIdentity(); |
| 897 | try { |
| 898 | mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| 899 | } finally { |
| 900 | Binder.restoreCallingIdentity(ident); |
| 901 | } |
| 902 | } |
| 903 | |
| 904 | private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, |
| 905 | BluetoothDevice device, int scoAudioMode) { |
| 906 | switch (scoAudioMode) { |
| 907 | case SCO_MODE_RAW: |
| 908 | return bluetoothHeadset.disconnectAudio(); |
| 909 | case SCO_MODE_VIRTUAL_CALL: |
| 910 | return bluetoothHeadset.stopScoUsingVirtualVoiceCall(); |
| 911 | case SCO_MODE_VR: |
| 912 | return bluetoothHeadset.stopVoiceRecognition(device); |
| 913 | default: |
| 914 | return false; |
| 915 | } |
| 916 | } |
| 917 | |
| 918 | private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, |
| 919 | BluetoothDevice device, int scoAudioMode) { |
| 920 | switch (scoAudioMode) { |
| 921 | case SCO_MODE_RAW: |
| 922 | return bluetoothHeadset.connectAudio(); |
| 923 | case SCO_MODE_VIRTUAL_CALL: |
| 924 | return bluetoothHeadset.startScoUsingVirtualVoiceCall(); |
| 925 | case SCO_MODE_VR: |
| 926 | return bluetoothHeadset.startVoiceRecognition(device); |
| 927 | default: |
| 928 | return false; |
| 929 | } |
| 930 | } |
| 931 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 932 | private void checkScoAudioState() { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 933 | if (mBluetoothHeadset != null |
| 934 | && mBluetoothHeadsetDevice != null |
| 935 | && mScoAudioState == SCO_STATE_INACTIVE |
| 936 | && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) |
| 937 | != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { |
| 938 | mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 939 | } |
| 940 | } |
| 941 | |
| 942 | |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 943 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 944 | private ScoClient getScoClient(IBinder cb, boolean create) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 945 | for (ScoClient existingClient : mScoClients) { |
| 946 | if (existingClient.getBinder() == cb) { |
| 947 | return existingClient; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 948 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 949 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 950 | if (create) { |
| 951 | ScoClient newClient = new ScoClient(cb); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 952 | newClient.registerDeathRecipient(); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 953 | mScoClients.add(newClient); |
| 954 | return newClient; |
| 955 | } |
| 956 | return null; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 957 | } |
| 958 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 959 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 960 | //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 961 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 962 | private void clearAllScoClients(int exceptPid, boolean stopSco) { |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 963 | final ArrayList<ScoClient> clients = new ArrayList<ScoClient>(); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 964 | for (ScoClient cl : mScoClients) { |
| 965 | if (cl.getPid() != exceptPid) { |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 966 | clients.add(cl); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 967 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 968 | } |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 969 | for (ScoClient cl : clients) { |
| 970 | cl.remove(stopSco, true /*unregister*/); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 971 | } |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 972 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 973 | } |
| 974 | |
| 975 | private boolean getBluetoothHeadset() { |
| 976 | boolean result = false; |
| 977 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); |
| 978 | if (adapter != null) { |
| 979 | result = adapter.getProfileProxy(mDeviceBroker.getContext(), |
| 980 | mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); |
| 981 | } |
| 982 | // If we could not get a bluetooth headset proxy, send a failure message |
| 983 | // without delay to reset the SCO audio state and clear SCO clients. |
| 984 | // If we could get a proxy, send a delayed failure message that will reset our state |
| 985 | // in case we don't receive onServiceConnected(). |
| 986 | mDeviceBroker.handleFailureToConnectToBtHeadsetService( |
| 987 | result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0); |
| 988 | return result; |
| 989 | } |
| 990 | |
Andy Hung | 6983695 | 2020-03-26 01:00:15 -0700 | [diff] [blame] | 991 | /** |
| 992 | * Returns the String equivalent of the btCodecType. |
| 993 | * |
| 994 | * This uses an "ENCODING_" prefix for consistency with Audio; |
| 995 | * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth. |
| 996 | */ |
| 997 | public static String bluetoothCodecToEncodingString(int btCodecType) { |
| 998 | switch (btCodecType) { |
| 999 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC: |
| 1000 | return "ENCODING_SBC"; |
| 1001 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC: |
| 1002 | return "ENCODING_AAC"; |
| 1003 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX: |
| 1004 | return "ENCODING_APTX"; |
| 1005 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: |
| 1006 | return "ENCODING_APTX_HD"; |
| 1007 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: |
| 1008 | return "ENCODING_LDAC"; |
| 1009 | default: |
| 1010 | return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; |
| 1011 | } |
| 1012 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 1013 | } |