1/* 2 * Copyright (C) 2016 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 17package com.android.server.telecom.bluetooth; 18 19import android.bluetooth.BluetoothDevice; 20import android.bluetooth.BluetoothHeadset; 21import android.bluetooth.BluetoothProfile; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.telecom.Log; 27 28import com.android.server.telecom.BluetoothAdapterProxy; 29import com.android.server.telecom.BluetoothHeadsetProxy; 30import com.android.server.telecom.TelecomSystem; 31 32import java.util.Collection; 33import java.util.LinkedHashMap; 34import java.util.LinkedList; 35import java.util.List; 36import java.util.Map; 37import java.util.Objects; 38import java.util.stream.Collectors; 39 40public class BluetoothDeviceManager { 41 private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 42 new BluetoothProfile.ServiceListener() { 43 @Override 44 public void onServiceConnected(int profile, BluetoothProfile proxy) { 45 Log.startSession("BMSL.oSC"); 46 try { 47 synchronized (mLock) { 48 if (profile == BluetoothProfile.HEADSET) { 49 mBluetoothHeadsetService = 50 new BluetoothHeadsetProxy((BluetoothHeadset) proxy); 51 Log.i(this, "- Got BluetoothHeadset: " + mBluetoothHeadsetService); 52 } else { 53 Log.w(this, "Connected to non-headset bluetooth service." + 54 " Not changing bluetooth headset."); 55 } 56 } 57 } finally { 58 Log.endSession(); 59 } 60 } 61 62 @Override 63 public void onServiceDisconnected(int profile) { 64 Log.startSession("BMSL.oSD"); 65 try { 66 synchronized (mLock) { 67 mBluetoothHeadsetService = null; 68 Log.i(BluetoothDeviceManager.this, "Lost BluetoothHeadset service. " + 69 "Removing all tracked devices."); 70 List<BluetoothDevice> devicesToRemove = new LinkedList<>( 71 mConnectedDevicesByAddress.values()); 72 mConnectedDevicesByAddress.clear(); 73 for (BluetoothDevice device : devicesToRemove) { 74 mBluetoothRouteManager.onDeviceLost(device); 75 } 76 } 77 } finally { 78 Log.endSession(); 79 } 80 } 81 }; 82 83 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 84 @Override 85 public void onReceive(Context context, Intent intent) { 86 Log.startSession("BM.oR"); 87 try { 88 String action = intent.getAction(); 89 90 if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 91 int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 92 BluetoothHeadset.STATE_DISCONNECTED); 93 BluetoothDevice device = 94 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 95 96 if (device == null) { 97 Log.w(BluetoothDeviceManager.this, "Got null device from broadcast. " + 98 "Ignoring."); 99 return; 100 } 101 102 Log.i(BluetoothDeviceManager.this, "Device %s changed state to %d", 103 device.getAddress(), bluetoothHeadsetState); 104 105 synchronized (mLock) { 106 if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) { 107 if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) { 108 mConnectedDevicesByAddress.put(device.getAddress(), device); 109 mBluetoothRouteManager.onDeviceAdded(device); 110 } 111 } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED 112 || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) { 113 if (mConnectedDevicesByAddress.containsKey(device.getAddress())) { 114 mConnectedDevicesByAddress.remove(device.getAddress()); 115 mBluetoothRouteManager.onDeviceLost(device); 116 } 117 } 118 } 119 } 120 } finally { 121 Log.endSession(); 122 } 123 } 124 }; 125 126 private final LinkedHashMap<String, BluetoothDevice> mConnectedDevicesByAddress = 127 new LinkedHashMap<>(); 128 private final TelecomSystem.SyncRoot mLock; 129 130 private BluetoothRouteManager mBluetoothRouteManager; 131 private BluetoothHeadsetProxy mBluetoothHeadsetService; 132 133 public BluetoothDeviceManager(Context context, BluetoothAdapterProxy bluetoothAdapter, 134 TelecomSystem.SyncRoot lock) { 135 mLock = lock; 136 137 if (bluetoothAdapter != null) { 138 bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener, 139 BluetoothProfile.HEADSET); 140 } 141 IntentFilter intentFilter = 142 new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 143 context.registerReceiver(mReceiver, intentFilter); 144 } 145 146 public void setBluetoothRouteManager(BluetoothRouteManager brm) { 147 mBluetoothRouteManager = brm; 148 } 149 150 public int getNumConnectedDevices() { 151 return mConnectedDevicesByAddress.size(); 152 } 153 154 public String getMostRecentlyConnectedDevice(String excludeAddress) { 155 String result = null; 156 synchronized (mLock) { 157 for (String addr : mConnectedDevicesByAddress.keySet()) { 158 if (!Objects.equals(addr, excludeAddress)) { 159 result = addr; 160 } 161 } 162 } 163 return result; 164 } 165 166 public BluetoothHeadsetProxy getHeadsetService() { 167 return mBluetoothHeadsetService; 168 } 169 170 public void setHeadsetServiceForTesting(BluetoothHeadsetProxy bluetoothHeadset) { 171 mBluetoothHeadsetService = bluetoothHeadset; 172 } 173} 174