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.BluetoothProfile; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.telecom.Log; 28 import android.telecom.Logging.Session; 29 30 import com.android.internal.os.SomeArgs; 31 32 import static com.android.server.telecom.bluetooth.BluetoothRouteManager.BT_AUDIO_IS_ON; 33 import static com.android.server.telecom.bluetooth.BluetoothRouteManager.BT_AUDIO_LOST; 34 35 36 public class BluetoothStateReceiver extends BroadcastReceiver { 37 private static final String LOG_TAG = BluetoothStateReceiver.class.getSimpleName(); 38 public static final IntentFilter INTENT_FILTER; 39 static { 40 INTENT_FILTER = new IntentFilter(); 41 INTENT_FILTER.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 42 INTENT_FILTER.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 43 INTENT_FILTER.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 44 INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); 45 INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); 46 } 47 48 // If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since 49 // other apps could be turning it on and off. We don't want to interfere. 50 private boolean mIsInCall = false; 51 private final BluetoothRouteManager mBluetoothRouteManager; 52 private final BluetoothDeviceManager mBluetoothDeviceManager; 53 onReceive(Context context, Intent intent)54 public void onReceive(Context context, Intent intent) { 55 Log.startSession("BSR.oR"); 56 try { 57 String action = intent.getAction(); 58 switch (action) { 59 case BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED: 60 handleAudioStateChanged(intent); 61 break; 62 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: 63 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: 64 handleConnectionStateChanged(intent); 65 break; 66 case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED: 67 case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED: 68 handleActiveDeviceChanged(intent); 69 break; 70 } 71 } finally { 72 Log.endSession(); 73 } 74 } 75 handleAudioStateChanged(Intent intent)76 private void handleAudioStateChanged(Intent intent) { 77 int bluetoothHeadsetAudioState = 78 intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 79 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 80 BluetoothDevice device = 81 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 82 if (device == null) { 83 Log.w(LOG_TAG, "Got null device from broadcast. " + 84 "Ignoring."); 85 return; 86 } 87 88 Log.i(LOG_TAG, "Device %s transitioned to audio state %d", 89 device.getAddress(), bluetoothHeadsetAudioState); 90 Session session = Log.createSubsession(); 91 SomeArgs args = SomeArgs.obtain(); 92 args.arg1 = session; 93 args.arg2 = device.getAddress(); 94 switch (bluetoothHeadsetAudioState) { 95 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 96 if (!mIsInCall) { 97 Log.i(LOG_TAG, "Ignoring BT audio on since we're not in a call"); 98 return; 99 } 100 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args); 101 break; 102 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 103 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args); 104 break; 105 } 106 } 107 handleConnectionStateChanged(Intent intent)108 private void handleConnectionStateChanged(Intent intent) { 109 int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 110 BluetoothHeadset.STATE_DISCONNECTED); 111 BluetoothDevice device = 112 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 113 114 if (device == null) { 115 Log.w(LOG_TAG, "Got null device from broadcast. " + 116 "Ignoring."); 117 return; 118 } 119 120 Log.i(LOG_TAG, "Device %s changed state to %d", 121 device.getAddress(), bluetoothHeadsetState); 122 123 boolean isHearingAid = BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED 124 .equals(intent.getAction()); 125 if (bluetoothHeadsetState == BluetoothProfile.STATE_CONNECTED) { 126 mBluetoothDeviceManager.onDeviceConnected(device, isHearingAid); 127 } else if (bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTED 128 || bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTING) { 129 mBluetoothDeviceManager.onDeviceDisconnected(device, isHearingAid); 130 } 131 } 132 handleActiveDeviceChanged(Intent intent)133 private void handleActiveDeviceChanged(Intent intent) { 134 BluetoothDevice device = 135 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 136 boolean isHearingAid = 137 BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction()); 138 Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device, 139 isHearingAid ? "hearing aid" : "HFP"); 140 141 mBluetoothRouteManager.onActiveDeviceChanged(device, isHearingAid); 142 if (isHearingAid) { 143 Session session = Log.createSubsession(); 144 SomeArgs args = SomeArgs.obtain(); 145 args.arg1 = session; 146 if (device == null) { 147 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args); 148 } else { 149 if (!mIsInCall) { 150 Log.i(LOG_TAG, "Ignoring hearing aid audio on since we're not in a call"); 151 return; 152 } 153 args.arg2 = device.getAddress(); 154 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args); 155 } 156 } 157 } 158 getBluetoothDeviceManager()159 public BluetoothDeviceManager getBluetoothDeviceManager() { 160 return mBluetoothDeviceManager; 161 } 162 BluetoothStateReceiver(BluetoothDeviceManager deviceManager, BluetoothRouteManager routeManager)163 public BluetoothStateReceiver(BluetoothDeviceManager deviceManager, 164 BluetoothRouteManager routeManager) { 165 mBluetoothDeviceManager = deviceManager; 166 mBluetoothRouteManager = routeManager; 167 } 168 setIsInCall(boolean isInCall)169 public void setIsInCall(boolean isInCall) { 170 mIsInCall = isInCall; 171 } 172 } 173