• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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