1 /* 2 * Copyright (C) 2018 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 17 package com.android.server.telecom.bluetooth; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothHeadset; 21 import android.bluetooth.BluetoothHearingAid; 22 import android.bluetooth.BluetoothLeAudio; 23 import android.bluetooth.BluetoothProfile; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.telecom.Log; 29 import android.telecom.Logging.Session; 30 31 import com.android.internal.os.SomeArgs; 32 33 import static com.android.server.telecom.bluetooth.BluetoothRouteManager.BT_AUDIO_IS_ON; 34 import static com.android.server.telecom.bluetooth.BluetoothRouteManager.BT_AUDIO_LOST; 35 36 37 public class BluetoothStateReceiver extends BroadcastReceiver { 38 private static final String LOG_TAG = BluetoothStateReceiver.class.getSimpleName(); 39 public static final IntentFilter INTENT_FILTER; 40 static { 41 INTENT_FILTER = new IntentFilter(); 42 INTENT_FILTER.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 43 INTENT_FILTER.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 44 INTENT_FILTER.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 45 INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); 46 INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); 47 INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); 48 INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); 49 } 50 51 // If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since 52 // other apps could be turning it on and off. We don't want to interfere. 53 private boolean mIsInCall = false; 54 private final BluetoothRouteManager mBluetoothRouteManager; 55 private final BluetoothDeviceManager mBluetoothDeviceManager; 56 onReceive(Context context, Intent intent)57 public void onReceive(Context context, Intent intent) { 58 Log.startSession("BSR.oR"); 59 try { 60 String action = intent.getAction(); 61 switch (action) { 62 case BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED: 63 handleAudioStateChanged(intent); 64 break; 65 case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: 66 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: 67 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: 68 handleConnectionStateChanged(intent); 69 break; 70 case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED: 71 case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED: 72 case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED: 73 handleActiveDeviceChanged(intent); 74 break; 75 } 76 } finally { 77 Log.endSession(); 78 } 79 } 80 handleAudioStateChanged(Intent intent)81 private void handleAudioStateChanged(Intent intent) { 82 int bluetoothHeadsetAudioState = 83 intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 84 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 85 BluetoothDevice device = 86 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 87 if (device == null) { 88 Log.w(LOG_TAG, "Got null device from broadcast. " + 89 "Ignoring."); 90 return; 91 } 92 93 Log.i(LOG_TAG, "Device %s transitioned to audio state %d", 94 device.getAddress(), bluetoothHeadsetAudioState); 95 Session session = Log.createSubsession(); 96 SomeArgs args = SomeArgs.obtain(); 97 args.arg1 = session; 98 args.arg2 = device.getAddress(); 99 switch (bluetoothHeadsetAudioState) { 100 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 101 if (!mIsInCall) { 102 Log.i(LOG_TAG, "Ignoring BT audio on since we're not in a call"); 103 return; 104 } 105 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args); 106 break; 107 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 108 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args); 109 break; 110 } 111 } 112 handleConnectionStateChanged(Intent intent)113 private void handleConnectionStateChanged(Intent intent) { 114 int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 115 BluetoothHeadset.STATE_DISCONNECTED); 116 BluetoothDevice device = 117 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 118 119 if (device == null) { 120 Log.w(LOG_TAG, "Got null device from broadcast. " + 121 "Ignoring."); 122 return; 123 } 124 125 int deviceType; 126 if (BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 127 deviceType = BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO; 128 } else if (BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 129 deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID; 130 } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 131 deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEADSET; 132 } else { 133 Log.w(LOG_TAG, "handleConnectionStateChanged: %s invalid device type", device); 134 return; 135 } 136 137 Log.i(LOG_TAG, "%s device %s changed state to %d", 138 BluetoothDeviceManager.getDeviceTypeString(deviceType), 139 device.getAddress(), bluetoothHeadsetState); 140 141 if (bluetoothHeadsetState == BluetoothProfile.STATE_CONNECTED) { 142 mBluetoothDeviceManager.onDeviceConnected(device, deviceType); 143 } else if (bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTED 144 || bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTING) { 145 mBluetoothDeviceManager.onDeviceDisconnected(device, deviceType); 146 } 147 } 148 handleActiveDeviceChanged(Intent intent)149 private void handleActiveDeviceChanged(Intent intent) { 150 BluetoothDevice device = 151 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 152 153 int deviceType; 154 if (BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) { 155 deviceType = BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO; 156 } else if (BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) { 157 deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID; 158 } else if (BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) { 159 deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEADSET; 160 } else { 161 Log.w(LOG_TAG, "handleActiveDeviceChanged: %s invalid device type", device); 162 return; 163 } 164 165 Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device, 166 BluetoothDeviceManager.getDeviceTypeString(deviceType)); 167 168 mBluetoothRouteManager.onActiveDeviceChanged(device, deviceType); 169 if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID || 170 deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) { 171 Session session = Log.createSubsession(); 172 SomeArgs args = SomeArgs.obtain(); 173 args.arg1 = session; 174 if (device == null) { 175 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args); 176 } else { 177 if (!mIsInCall) { 178 Log.i(LOG_TAG, "Ignoring audio on since we're not in a call"); 179 return; 180 } 181 args.arg2 = device.getAddress(); 182 183 if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) { 184 /* In Le Audio case, once device got Active, the Telecom needs to make sure it 185 * is set as communication device before we can say that BT_AUDIO_IS_ON 186 */ 187 if (!mBluetoothDeviceManager.setLeAudioCommunicationDevice()) { 188 Log.w(LOG_TAG, 189 "Device %s cannot be use as LE audio communication device.", 190 device); 191 return; 192 } 193 } else { 194 /* deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID */ 195 if (!mBluetoothDeviceManager.setHearingAidCommunicationDevice()) { 196 Log.w(LOG_TAG, 197 "Device %s cannot be use as hearing aid communication device.", 198 device); 199 } else { 200 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args); 201 } 202 } 203 } 204 } 205 } 206 getBluetoothDeviceManager()207 public BluetoothDeviceManager getBluetoothDeviceManager() { 208 return mBluetoothDeviceManager; 209 } 210 BluetoothStateReceiver(BluetoothDeviceManager deviceManager, BluetoothRouteManager routeManager)211 public BluetoothStateReceiver(BluetoothDeviceManager deviceManager, 212 BluetoothRouteManager routeManager) { 213 mBluetoothDeviceManager = deviceManager; 214 mBluetoothRouteManager = routeManager; 215 } 216 setIsInCall(boolean isInCall)217 public void setIsInCall(boolean isInCall) { 218 mIsInCall = isInCall; 219 } 220 } 221