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