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() { |
Eric Laurent | 1dd060f | 2020-05-13 17:43:24 -0700 | [diff] [blame] | 361 | if (mBluetoothHeadset == null) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 362 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 363 | } |
Eric Laurent | 1dd060f | 2020-05-13 17:43:24 -0700 | [diff] [blame] | 364 | return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) |
| 365 | == BluetoothHeadset.STATE_AUDIO_CONNECTED; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 366 | } |
| 367 | |
| 368 | /** |
| 369 | * Disconnect all SCO connections started by {@link AudioManager} except those started by |
| 370 | * {@param exceptPid} |
| 371 | * |
| 372 | * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept |
| 373 | */ |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 374 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 375 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 376 | /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { |
| 377 | checkScoAudioState(); |
| 378 | if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { |
| 379 | return; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 380 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 381 | clearAllScoClients(exceptPid, true); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 382 | } |
| 383 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 384 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 385 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 386 | /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 387 | @NonNull String eventSource) { |
| 388 | ScoClient client = getScoClient(cb, true); |
| 389 | // The calling identity must be cleared before calling ScoClient.incCount(). |
| 390 | // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs |
| 391 | // and this must be done on behalf of system server to make sure permissions are granted. |
| 392 | // The caller identity must be cleared after getScoClient() because it is needed if a new |
| 393 | // client is created. |
| 394 | final long ident = Binder.clearCallingIdentity(); |
| 395 | try { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 396 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 397 | client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 398 | } catch (NullPointerException e) { |
| 399 | Log.e(TAG, "Null ScoClient", e); |
| 400 | } |
| 401 | Binder.restoreCallingIdentity(ident); |
| 402 | } |
| 403 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 404 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 405 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 406 | /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, |
| 407 | @NonNull String eventSource) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 408 | ScoClient client = getScoClient(cb, false); |
| 409 | // The calling identity must be cleared before calling ScoClient.decCount(). |
| 410 | // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs |
| 411 | // and this must be done on behalf of system server to make sure permissions are granted. |
| 412 | final long ident = Binder.clearCallingIdentity(); |
| 413 | if (client != null) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 414 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 415 | client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, |
| 416 | SCO_MODE_VIRTUAL_CALL); |
| 417 | // If a disconnection is pending, the client will be removed whne clearAllScoClients() |
| 418 | // is called form receiveBtEvent() |
| 419 | if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ |
| 420 | && mScoAudioState != SCO_STATE_DEACTIVATING) { |
| 421 | client.remove(false /*stop */, true /*unregister*/); |
| 422 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 423 | } |
| 424 | Binder.restoreCallingIdentity(ident); |
| 425 | } |
| 426 | |
| 427 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 428 | /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { |
| 429 | if (mHearingAid == null) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 430 | if (AudioService.DEBUG_VOL) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 431 | Log.i(TAG, "setHearingAidVolume: null mHearingAid"); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 432 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 433 | return; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 434 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 435 | //hearing aid expect volume value in range -128dB to 0dB |
| 436 | int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10, |
| 437 | AudioSystem.DEVICE_OUT_HEARING_AID); |
| 438 | if (gainDB < BT_HEARING_AID_GAIN_MIN) { |
| 439 | gainDB = BT_HEARING_AID_GAIN_MIN; |
| 440 | } |
| 441 | if (AudioService.DEBUG_VOL) { |
| 442 | Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx=" |
| 443 | + index + " gain=" + gainDB); |
| 444 | } |
| 445 | AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( |
| 446 | AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB)); |
| 447 | mHearingAid.setVolume(gainDB); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 448 | } |
| 449 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 450 | /*package*/ synchronized void onBroadcastScoConnectionState(int state) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 451 | if (state == mScoConnectionState) { |
| 452 | return; |
| 453 | } |
| 454 | Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); |
| 455 | newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); |
| 456 | newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, |
| 457 | mScoConnectionState); |
| 458 | sendStickyBroadcastToAll(newIntent); |
| 459 | mScoConnectionState = state; |
| 460 | } |
| 461 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 462 | /*package*/ synchronized void disconnectAllBluetoothProfiles() { |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 463 | mDeviceBroker.postDisconnectA2dp(); |
| 464 | mDeviceBroker.postDisconnectA2dpSink(); |
| 465 | mDeviceBroker.postDisconnectHeadset(); |
| 466 | mDeviceBroker.postDisconnectHearingAid(); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 467 | } |
| 468 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 469 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 470 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 471 | /*package*/ synchronized void resetBluetoothSco() { |
| 472 | clearAllScoClients(0, false); |
| 473 | mScoAudioState = SCO_STATE_INACTIVE; |
| 474 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 475 | AudioSystem.setParameters("A2dpSuspended=false"); |
| 476 | mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); |
| 477 | } |
| 478 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 479 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 480 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 481 | /*package*/ synchronized void disconnectHeadset() { |
| 482 | setBtScoActiveDevice(null); |
| 483 | mBluetoothHeadset = null; |
| 484 | } |
| 485 | |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 486 | /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { |
| 487 | mA2dp = a2dp; |
| 488 | final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); |
| 489 | if (deviceList.isEmpty()) { |
| 490 | return; |
| 491 | } |
| 492 | final BluetoothDevice btDevice = deviceList.get(0); |
Jean-Michel Trivi | b3580a8 | 2019-06-24 10:39:19 -0700 | [diff] [blame] | 493 | // the device is guaranteed CONNECTED |
| 494 | mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice, |
| 495 | BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1); |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 496 | } |
| 497 | |
| 498 | /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { |
| 499 | final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); |
| 500 | if (deviceList.isEmpty()) { |
| 501 | return; |
| 502 | } |
| 503 | final BluetoothDevice btDevice = deviceList.get(0); |
| 504 | final @BluetoothProfile.BtProfileState int state = |
| 505 | profile.getConnectionState(btDevice); |
| 506 | mDeviceBroker.postSetA2dpSourceConnectionState( |
| 507 | state, new BluetoothA2dpDeviceInfo(btDevice)); |
| 508 | } |
| 509 | |
| 510 | /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { |
| 511 | mHearingAid = hearingAid; |
| 512 | final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); |
| 513 | if (deviceList.isEmpty()) { |
| 514 | return; |
| 515 | } |
| 516 | final BluetoothDevice btDevice = deviceList.get(0); |
| 517 | final @BluetoothProfile.BtProfileState int state = |
| 518 | mHearingAid.getConnectionState(btDevice); |
Jean-Michel Trivi | fc86cfa | 2019-03-01 10:15:47 -0800 | [diff] [blame] | 519 | mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 520 | btDevice, state, |
| 521 | /*suppressNoisyIntent*/ false, |
| 522 | /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, |
| 523 | /*eventSource*/ "mBluetoothProfileServiceListener"); |
| 524 | } |
| 525 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 526 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 527 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 528 | /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { |
| 529 | // Discard timeout message |
| 530 | mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); |
| 531 | mBluetoothHeadset = headset; |
| 532 | setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice()); |
| 533 | // Refresh SCO audio state |
| 534 | checkScoAudioState(); |
| 535 | if (mScoAudioState != SCO_STATE_ACTIVATE_REQ |
| 536 | && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { |
| 537 | return; |
| 538 | } |
| 539 | boolean status = false; |
| 540 | if (mBluetoothHeadsetDevice != null) { |
| 541 | switch (mScoAudioState) { |
| 542 | case SCO_STATE_ACTIVATE_REQ: |
| 543 | status = connectBluetoothScoAudioHelper( |
| 544 | mBluetoothHeadset, |
| 545 | mBluetoothHeadsetDevice, mScoAudioMode); |
| 546 | if (status) { |
| 547 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
| 548 | } |
| 549 | break; |
| 550 | case SCO_STATE_DEACTIVATE_REQ: |
| 551 | status = disconnectBluetoothScoAudioHelper( |
| 552 | mBluetoothHeadset, |
| 553 | mBluetoothHeadsetDevice, mScoAudioMode); |
| 554 | if (status) { |
| 555 | mScoAudioState = SCO_STATE_DEACTIVATING; |
| 556 | } |
| 557 | break; |
| 558 | } |
| 559 | } |
| 560 | if (!status) { |
| 561 | mScoAudioState = SCO_STATE_INACTIVE; |
| 562 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 563 | } |
| 564 | } |
| 565 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 566 | //---------------------------------------------------------------------- |
| 567 | private void broadcastScoConnectionState(int state) { |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 568 | mDeviceBroker.postBroadcastScoConnectionState(state); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 569 | } |
| 570 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 571 | private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { |
| 572 | if (btDevice == null) { |
| 573 | return true; |
| 574 | } |
| 575 | String address = btDevice.getAddress(); |
| 576 | BluetoothClass btClass = btDevice.getBluetoothClass(); |
| 577 | int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; |
| 578 | int[] outDeviceTypes = { |
| 579 | AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, |
| 580 | AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, |
| 581 | AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
| 582 | }; |
| 583 | if (btClass != null) { |
| 584 | switch (btClass.getDeviceClass()) { |
| 585 | case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: |
| 586 | case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: |
| 587 | outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET }; |
| 588 | break; |
| 589 | case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: |
| 590 | outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; |
| 591 | break; |
| 592 | } |
| 593 | } |
| 594 | if (!BluetoothAdapter.checkBluetoothAddress(address)) { |
| 595 | address = ""; |
| 596 | } |
Jack He | cb49964 | 2019-04-03 00:48:49 -0700 | [diff] [blame] | 597 | String btDeviceName = getName(btDevice); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 598 | boolean result = false; |
| 599 | if (isActive) { |
| 600 | result |= mDeviceBroker.handleDeviceConnection( |
| 601 | isActive, outDeviceTypes[0], address, btDeviceName); |
| 602 | } else { |
| 603 | for (int outDeviceType : outDeviceTypes) { |
| 604 | result |= mDeviceBroker.handleDeviceConnection( |
| 605 | isActive, outDeviceType, address, btDeviceName); |
| 606 | } |
| 607 | } |
| 608 | // handleDeviceConnection() && result to make sure the method get executed |
| 609 | result = mDeviceBroker.handleDeviceConnection( |
| 610 | isActive, inDevice, address, btDeviceName) && result; |
| 611 | return result; |
| 612 | } |
| 613 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 614 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 615 | //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 616 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 617 | private void setBtScoActiveDevice(BluetoothDevice btDevice) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 618 | Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); |
| 619 | final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; |
| 620 | if (Objects.equals(btDevice, previousActiveDevice)) { |
| 621 | return; |
| 622 | } |
| 623 | if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) { |
| 624 | Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device " |
| 625 | + previousActiveDevice); |
| 626 | } |
| 627 | if (!handleBtScoActiveDeviceChange(btDevice, true)) { |
| 628 | Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice); |
| 629 | // set mBluetoothHeadsetDevice to null when failing to add new device |
| 630 | btDevice = null; |
| 631 | } |
| 632 | mBluetoothHeadsetDevice = btDevice; |
| 633 | if (mBluetoothHeadsetDevice == null) { |
| 634 | resetBluetoothSco(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 635 | } |
| 636 | } |
| 637 | |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 638 | // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async |
| 639 | // methods inside listener. |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 640 | private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = |
| 641 | new BluetoothProfile.ServiceListener() { |
| 642 | public void onServiceConnected(int profile, BluetoothProfile proxy) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 643 | switch(profile) { |
| 644 | case BluetoothProfile.A2DP: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 645 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 646 | "BT profile service: connecting A2DP profile")); |
| 647 | mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 648 | break; |
| 649 | |
| 650 | case BluetoothProfile.A2DP_SINK: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 651 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 652 | "BT profile service: connecting A2DP_SINK profile")); |
| 653 | mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 654 | break; |
| 655 | |
| 656 | case BluetoothProfile.HEADSET: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 657 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 658 | "BT profile service: connecting HEADSET profile")); |
| 659 | mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 660 | break; |
| 661 | |
| 662 | case BluetoothProfile.HEARING_AID: |
Jean-Michel Trivi | 43ff4f3 | 2019-02-07 12:08:39 -0800 | [diff] [blame] | 663 | AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( |
| 664 | "BT profile service: connecting HEARING_AID profile")); |
| 665 | mDeviceBroker.postBtHearingAidProfileConnected( |
| 666 | (BluetoothHearingAid) proxy); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 667 | break; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 668 | default: |
| 669 | break; |
| 670 | } |
| 671 | } |
| 672 | public void onServiceDisconnected(int profile) { |
| 673 | |
| 674 | switch (profile) { |
| 675 | case BluetoothProfile.A2DP: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 676 | mDeviceBroker.postDisconnectA2dp(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 677 | break; |
| 678 | |
| 679 | case BluetoothProfile.A2DP_SINK: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 680 | mDeviceBroker.postDisconnectA2dpSink(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 681 | break; |
| 682 | |
| 683 | case BluetoothProfile.HEADSET: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 684 | mDeviceBroker.postDisconnectHeadset(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 685 | break; |
| 686 | |
| 687 | case BluetoothProfile.HEARING_AID: |
Jean-Michel Trivi | b946515 | 2019-02-06 15:20:19 -0800 | [diff] [blame] | 688 | mDeviceBroker.postDisconnectHearingAid(); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 689 | break; |
| 690 | |
| 691 | default: |
| 692 | break; |
| 693 | } |
| 694 | } |
| 695 | }; |
| 696 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 697 | //---------------------------------------------------------------------- |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 698 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 699 | @GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 700 | /*package*/ synchronized void scoClientDied(Object obj) { |
| 701 | final ScoClient client = (ScoClient) obj; |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 702 | client.remove(true /*stop*/, false /*unregister*/); |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 703 | Log.w(TAG, "SCO client died"); |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 704 | } |
| 705 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 706 | private class ScoClient implements IBinder.DeathRecipient { |
| 707 | private IBinder mCb; // To be notified of client's death |
| 708 | private int mCreatorPid; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 709 | |
| 710 | ScoClient(IBinder cb) { |
| 711 | mCb = cb; |
| 712 | mCreatorPid = Binder.getCallingPid(); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 713 | } |
| 714 | |
| 715 | public void registerDeathRecipient() { |
| 716 | try { |
| 717 | mCb.linkToDeath(this, 0); |
| 718 | } catch (RemoteException e) { |
| 719 | Log.w(TAG, "ScoClient could not link to " + mCb + " binder death"); |
| 720 | } |
| 721 | } |
| 722 | |
| 723 | public void unregisterDeathRecipient() { |
| 724 | try { |
| 725 | mCb.unlinkToDeath(this, 0); |
| 726 | } catch (NoSuchElementException e) { |
| 727 | Log.w(TAG, "ScoClient could not not unregistered to binder"); |
| 728 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 729 | } |
| 730 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 731 | @Override |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 732 | public void binderDied() { |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 733 | // process this from DeviceBroker's message queue to take the right locks since |
| 734 | // this event can impact SCO mode and requires querying audio mode stack |
| 735 | mDeviceBroker.postScoClientDied(this); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 736 | } |
| 737 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 738 | IBinder getBinder() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 739 | return mCb; |
| 740 | } |
| 741 | |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 742 | int getPid() { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 743 | return mCreatorPid; |
| 744 | } |
| 745 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 746 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 747 | //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 748 | @GuardedBy("BtHelper.this") |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 749 | private boolean requestScoState(int state, int scoAudioMode) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 750 | checkScoAudioState(); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 751 | if (mScoClients.size() != 1) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 752 | Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 753 | + ", num SCO clients=" + mScoClients.size()); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 754 | return true; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 755 | } |
| 756 | if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { |
| 757 | // Make sure that the state transitions to CONNECTING even if we cannot initiate |
| 758 | // the connection. |
| 759 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); |
| 760 | // Accept SCO audio activation only in NORMAL audio mode or if the mode is |
| 761 | // currently controlled by the same client process. |
Jean-Michel Trivi | 5bbcd44 | 2019-04-18 12:07:34 -0700 | [diff] [blame] | 762 | final int modeOwnerPid = mDeviceBroker.getModeOwnerPid(); |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 763 | if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { |
| 764 | Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " |
| 765 | + modeOwnerPid + " != creatorPid " + mCreatorPid); |
| 766 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 767 | return false; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 768 | } |
| 769 | switch (mScoAudioState) { |
| 770 | case SCO_STATE_INACTIVE: |
| 771 | mScoAudioMode = scoAudioMode; |
| 772 | if (scoAudioMode == SCO_MODE_UNDEFINED) { |
| 773 | mScoAudioMode = SCO_MODE_VIRTUAL_CALL; |
| 774 | if (mBluetoothHeadsetDevice != null) { |
| 775 | mScoAudioMode = Settings.Global.getInt( |
| 776 | mDeviceBroker.getContentResolver(), |
| 777 | "bluetooth_sco_channel_" |
| 778 | + mBluetoothHeadsetDevice.getAddress(), |
| 779 | SCO_MODE_VIRTUAL_CALL); |
| 780 | if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { |
| 781 | mScoAudioMode = SCO_MODE_VIRTUAL_CALL; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 782 | } |
| 783 | } |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 784 | } |
| 785 | if (mBluetoothHeadset == null) { |
| 786 | if (getBluetoothHeadset()) { |
| 787 | mScoAudioState = SCO_STATE_ACTIVATE_REQ; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 788 | } else { |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 789 | Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" |
| 790 | + " connection, mScoAudioMode=" + mScoAudioMode); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 791 | broadcastScoConnectionState( |
| 792 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 793 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 794 | } |
| 795 | break; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 796 | } |
| 797 | if (mBluetoothHeadsetDevice == null) { |
| 798 | Log.w(TAG, "requestScoState: no active device while connecting," |
| 799 | + " mScoAudioMode=" + mScoAudioMode); |
| 800 | broadcastScoConnectionState( |
| 801 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 802 | return false; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 803 | } |
| 804 | if (connectBluetoothScoAudioHelper(mBluetoothHeadset, |
| 805 | mBluetoothHeadsetDevice, mScoAudioMode)) { |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 806 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 807 | } else { |
| 808 | Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice |
| 809 | + " failed, mScoAudioMode=" + mScoAudioMode); |
| 810 | broadcastScoConnectionState( |
| 811 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 812 | return false; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 813 | } |
| 814 | break; |
| 815 | case SCO_STATE_DEACTIVATING: |
| 816 | mScoAudioState = SCO_STATE_ACTIVATE_REQ; |
| 817 | break; |
| 818 | case SCO_STATE_DEACTIVATE_REQ: |
| 819 | mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; |
| 820 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); |
| 821 | break; |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 822 | case SCO_STATE_ACTIVE_INTERNAL: |
| 823 | Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return"); |
| 824 | break; |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 825 | default: |
| 826 | Log.w(TAG, "requestScoState: failed to connect in state " |
| 827 | + mScoAudioState + ", scoAudioMode=" + scoAudioMode); |
| 828 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 829 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 830 | } |
| 831 | } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { |
| 832 | switch (mScoAudioState) { |
| 833 | case SCO_STATE_ACTIVE_INTERNAL: |
| 834 | if (mBluetoothHeadset == null) { |
| 835 | if (getBluetoothHeadset()) { |
| 836 | mScoAudioState = SCO_STATE_DEACTIVATE_REQ; |
| 837 | } else { |
| 838 | Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" |
| 839 | + " disconnection, mScoAudioMode=" + mScoAudioMode); |
| 840 | mScoAudioState = SCO_STATE_INACTIVE; |
| 841 | broadcastScoConnectionState( |
| 842 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 843 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 844 | } |
| 845 | break; |
| 846 | } |
| 847 | if (mBluetoothHeadsetDevice == null) { |
| 848 | mScoAudioState = SCO_STATE_INACTIVE; |
| 849 | broadcastScoConnectionState( |
| 850 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 851 | break; |
| 852 | } |
| 853 | if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset, |
| 854 | mBluetoothHeadsetDevice, mScoAudioMode)) { |
| 855 | mScoAudioState = SCO_STATE_DEACTIVATING; |
| 856 | } else { |
| 857 | mScoAudioState = SCO_STATE_INACTIVE; |
| 858 | broadcastScoConnectionState( |
| 859 | AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 860 | } |
| 861 | break; |
| 862 | case SCO_STATE_ACTIVATE_REQ: |
| 863 | mScoAudioState = SCO_STATE_INACTIVE; |
| 864 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
| 865 | break; |
| 866 | default: |
| 867 | Log.w(TAG, "requestScoState: failed to disconnect in state " |
| 868 | + mScoAudioState + ", scoAudioMode=" + scoAudioMode); |
| 869 | broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 870 | return false; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 871 | } |
| 872 | } |
jiabin | cbf33b2 | 2019-04-25 13:35:56 -0700 | [diff] [blame] | 873 | return true; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 874 | } |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 875 | |
| 876 | @GuardedBy("BtHelper.this") |
| 877 | void remove(boolean stop, boolean unregister) { |
| 878 | if (unregister) { |
| 879 | unregisterDeathRecipient(); |
| 880 | } |
| 881 | if (stop) { |
| 882 | requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, |
| 883 | SCO_MODE_VIRTUAL_CALL); |
| 884 | } |
| 885 | mScoClients.remove(this); |
| 886 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 887 | } |
| 888 | |
| 889 | //----------------------------------------------------- |
| 890 | // Utilities |
| 891 | private void sendStickyBroadcastToAll(Intent intent) { |
| 892 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); |
| 893 | final long ident = Binder.clearCallingIdentity(); |
| 894 | try { |
| 895 | mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| 896 | } finally { |
| 897 | Binder.restoreCallingIdentity(ident); |
| 898 | } |
| 899 | } |
| 900 | |
| 901 | private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, |
| 902 | BluetoothDevice device, int scoAudioMode) { |
| 903 | switch (scoAudioMode) { |
| 904 | case SCO_MODE_RAW: |
| 905 | return bluetoothHeadset.disconnectAudio(); |
| 906 | case SCO_MODE_VIRTUAL_CALL: |
| 907 | return bluetoothHeadset.stopScoUsingVirtualVoiceCall(); |
| 908 | case SCO_MODE_VR: |
| 909 | return bluetoothHeadset.stopVoiceRecognition(device); |
| 910 | default: |
| 911 | return false; |
| 912 | } |
| 913 | } |
| 914 | |
| 915 | private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, |
| 916 | BluetoothDevice device, int scoAudioMode) { |
| 917 | switch (scoAudioMode) { |
| 918 | case SCO_MODE_RAW: |
| 919 | return bluetoothHeadset.connectAudio(); |
| 920 | case SCO_MODE_VIRTUAL_CALL: |
| 921 | return bluetoothHeadset.startScoUsingVirtualVoiceCall(); |
| 922 | case SCO_MODE_VR: |
| 923 | return bluetoothHeadset.startVoiceRecognition(device); |
| 924 | default: |
| 925 | return false; |
| 926 | } |
| 927 | } |
| 928 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 929 | private void checkScoAudioState() { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 930 | if (mBluetoothHeadset != null |
| 931 | && mBluetoothHeadsetDevice != null |
| 932 | && mScoAudioState == SCO_STATE_INACTIVE |
| 933 | && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) |
| 934 | != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { |
| 935 | mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 936 | } |
| 937 | } |
| 938 | |
| 939 | |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 940 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 941 | private ScoClient getScoClient(IBinder cb, boolean create) { |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 942 | for (ScoClient existingClient : mScoClients) { |
| 943 | if (existingClient.getBinder() == cb) { |
| 944 | return existingClient; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 945 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 946 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 947 | if (create) { |
| 948 | ScoClient newClient = new ScoClient(cb); |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 949 | newClient.registerDeathRecipient(); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 950 | mScoClients.add(newClient); |
| 951 | return newClient; |
| 952 | } |
| 953 | return null; |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 954 | } |
| 955 | |
Jean-Michel Trivi | 218da16 | 2019-04-05 16:05:04 -0700 | [diff] [blame] | 956 | // @GuardedBy("AudioDeviceBroker.mSetModeLock") |
| 957 | //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") |
| 958 | @GuardedBy("BtHelper.this") |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 959 | private void clearAllScoClients(int exceptPid, boolean stopSco) { |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 960 | final ArrayList<ScoClient> clients = new ArrayList<ScoClient>(); |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 961 | for (ScoClient cl : mScoClients) { |
| 962 | if (cl.getPid() != exceptPid) { |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 963 | clients.add(cl); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 964 | } |
Jean-Michel Trivi | c4b9c96 | 2019-02-01 09:58:54 -0800 | [diff] [blame] | 965 | } |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 966 | for (ScoClient cl : clients) { |
| 967 | cl.remove(stopSco, true /*unregister*/); |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 968 | } |
Seulki Shin | feb9b89 | 2020-03-27 14:05:16 -0700 | [diff] [blame] | 969 | |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 970 | } |
| 971 | |
| 972 | private boolean getBluetoothHeadset() { |
| 973 | boolean result = false; |
| 974 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); |
| 975 | if (adapter != null) { |
| 976 | result = adapter.getProfileProxy(mDeviceBroker.getContext(), |
| 977 | mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); |
| 978 | } |
| 979 | // If we could not get a bluetooth headset proxy, send a failure message |
| 980 | // without delay to reset the SCO audio state and clear SCO clients. |
| 981 | // If we could get a proxy, send a delayed failure message that will reset our state |
| 982 | // in case we don't receive onServiceConnected(). |
| 983 | mDeviceBroker.handleFailureToConnectToBtHeadsetService( |
| 984 | result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0); |
| 985 | return result; |
| 986 | } |
| 987 | |
Andy Hung | 6983695 | 2020-03-26 01:00:15 -0700 | [diff] [blame] | 988 | /** |
| 989 | * Returns the String equivalent of the btCodecType. |
| 990 | * |
| 991 | * This uses an "ENCODING_" prefix for consistency with Audio; |
| 992 | * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth. |
| 993 | */ |
| 994 | public static String bluetoothCodecToEncodingString(int btCodecType) { |
| 995 | switch (btCodecType) { |
| 996 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC: |
| 997 | return "ENCODING_SBC"; |
| 998 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC: |
| 999 | return "ENCODING_AAC"; |
| 1000 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX: |
| 1001 | return "ENCODING_APTX"; |
| 1002 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: |
| 1003 | return "ENCODING_APTX_HD"; |
| 1004 | case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: |
| 1005 | return "ENCODING_LDAC"; |
| 1006 | default: |
| 1007 | return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; |
| 1008 | } |
| 1009 | } |
Jean-Michel Trivi | 5885037 | 2018-09-14 16:01:28 -0700 | [diff] [blame] | 1010 | } |