blob: 94db95a6f33c58ec389cedade47aea3b10a579af [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
2 * Copyright (C) 2008 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.systemui.statusbar.policy;
18
Sudheer Shankab6fc9312016-01-27 19:59:03 +000019import android.app.ActivityManager;
John Spurlockaf8d6c42014-05-07 17:49:08 -040020import android.bluetooth.BluetoothAdapter;
Jason Monk9ef03b42017-06-13 12:49:55 -040021import android.bluetooth.BluetoothDevice;
22import android.bluetooth.BluetoothProfile;
John Spurlockaf8d6c42014-05-07 17:49:08 -040023import android.content.Context;
Jason Monk744cf642015-05-19 12:04:41 -040024import android.os.Handler;
Jason Monk4ae97d32014-12-17 10:14:33 -050025import android.os.Looper;
Jason Monk744cf642015-05-19 12:04:41 -040026import android.os.Message;
Sudheer Shankab6fc9312016-01-27 19:59:03 +000027import android.os.UserHandle;
28import android.os.UserManager;
John Spurlock486b78e2014-07-07 08:37:56 -040029import android.util.Log;
John Spurlockaf8d6c42014-05-07 17:49:08 -040030
Jason Monkbe3c5db2015-02-04 13:00:55 -050031import com.android.settingslib.bluetooth.BluetoothCallback;
32import com.android.settingslib.bluetooth.CachedBluetoothDevice;
33import com.android.settingslib.bluetooth.LocalBluetoothManager;
Jason Monk6a73e632017-03-17 11:08:30 -040034import com.android.systemui.Dependency;
John Spurlock486b78e2014-07-07 08:37:56 -040035
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
Jason Monk9ef03b42017-06-13 12:49:55 -040038import java.lang.ref.WeakReference;
John Spurlockaf8d6c42014-05-07 17:49:08 -040039import java.util.ArrayList;
Jason Monkbe3c5db2015-02-04 13:00:55 -050040import java.util.Collection;
Jason Monk9ef03b42017-06-13 12:49:55 -040041import java.util.WeakHashMap;
John Spurlockaf8d6c42014-05-07 17:49:08 -040042
Jason Monkbe3c5db2015-02-04 13:00:55 -050043public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
44 CachedBluetoothDevice.Callback {
John Spurlock486b78e2014-07-07 08:37:56 -040045 private static final String TAG = "BluetoothController";
46 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jason Monk4ae97d32014-12-17 10:14:33 -050047
Jason Monkbe3c5db2015-02-04 13:00:55 -050048 private final LocalBluetoothManager mLocalBluetoothManager;
Sudheer Shankab6fc9312016-01-27 19:59:03 +000049 private final UserManager mUserManager;
50 private final int mCurrentUser;
Jason Monk9ef03b42017-06-13 12:49:55 -040051 private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState =
52 new WeakHashMap<>();
53 private final Handler mBgHandler;
Jason Monk4ae97d32014-12-17 10:14:33 -050054
John Spurlockd1c86e22014-06-01 00:04:53 -040055 private boolean mEnabled;
Jason Monka7d92b62015-05-27 10:20:37 -040056 private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jason Monkbe3c5db2015-02-04 13:00:55 -050057 private CachedBluetoothDevice mLastDevice;
John Spurlockaf8d6c42014-05-07 17:49:08 -040058
Jason Monk9ef03b42017-06-13 12:49:55 -040059 private final H mHandler = new H(Looper.getMainLooper());
Jason Monkfac25382016-07-19 14:13:37 -040060 private int mState;
Jason Monk744cf642015-05-19 12:04:41 -040061
Jason Monk4ae97d32014-12-17 10:14:33 -050062 public BluetoothControllerImpl(Context context, Looper bgLooper) {
Jason Monk6a73e632017-03-17 11:08:30 -040063 mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class);
Jason Monk9ef03b42017-06-13 12:49:55 -040064 mBgHandler = new Handler(bgLooper);
Jason Monkbe3c5db2015-02-04 13:00:55 -050065 if (mLocalBluetoothManager != null) {
Jason Monk9ef03b42017-06-13 12:49:55 -040066 mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler);
Jason Monkbe3c5db2015-02-04 13:00:55 -050067 mLocalBluetoothManager.getEventManager().registerCallback(this);
68 onBluetoothStateChanged(
69 mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
John Spurlockaf8d6c42014-05-07 17:49:08 -040070 }
Sudheer Shankab6fc9312016-01-27 19:59:03 +000071 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
72 mCurrentUser = ActivityManager.getCurrentUser();
73 }
74
75 @Override
76 public boolean canConfigBluetooth() {
77 return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH,
phweissbd8d0332017-06-12 11:38:09 +020078 UserHandle.of(mCurrentUser))
79 && !mUserManager.hasUserRestriction(UserManager.DISALLOW_BLUETOOTH,
Sudheer Shankab6fc9312016-01-27 19:59:03 +000080 UserHandle.of(mCurrentUser));
John Spurlockaf8d6c42014-05-07 17:49:08 -040081 }
82
John Spurlock486b78e2014-07-07 08:37:56 -040083 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
84 pw.println("BluetoothController state:");
Jason Monkbe3c5db2015-02-04 13:00:55 -050085 pw.print(" mLocalBluetoothManager="); pw.println(mLocalBluetoothManager);
Jason Monk09389a92015-05-19 16:06:52 -040086 if (mLocalBluetoothManager == null) {
87 return;
88 }
John Spurlock486b78e2014-07-07 08:37:56 -040089 pw.print(" mEnabled="); pw.println(mEnabled);
Jason Monka7d92b62015-05-27 10:20:37 -040090 pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState));
John Spurlock486b78e2014-07-07 08:37:56 -040091 pw.print(" mLastDevice="); pw.println(mLastDevice);
Adrian Roosa4b54862016-06-17 14:28:27 -070092 pw.print(" mCallbacks.size="); pw.println(mHandler.mCallbacks.size());
Jason Monkbe3c5db2015-02-04 13:00:55 -050093 pw.println(" Bluetooth Devices:");
94 for (CachedBluetoothDevice device :
95 mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
96 pw.println(" " + getDeviceString(device));
John Spurlock486b78e2014-07-07 08:37:56 -040097 }
98 }
99
Jason Monka7d92b62015-05-27 10:20:37 -0400100 private static String stateToString(int state) {
101 switch (state) {
102 case BluetoothAdapter.STATE_CONNECTED:
103 return "CONNECTED";
104 case BluetoothAdapter.STATE_CONNECTING:
105 return "CONNECTING";
106 case BluetoothAdapter.STATE_DISCONNECTED:
107 return "DISCONNECTED";
108 case BluetoothAdapter.STATE_DISCONNECTING:
109 return "DISCONNECTING";
110 }
111 return "UNKNOWN(" + state + ")";
112 }
113
Jason Monkbe3c5db2015-02-04 13:00:55 -0500114 private String getDeviceString(CachedBluetoothDevice device) {
115 return device.getName() + " " + device.getBondState() + " " + device.isConnected();
John Spurlock486b78e2014-07-07 08:37:56 -0400116 }
117
Jason Monka7d92b62015-05-27 10:20:37 -0400118 @Override
Jason Monk9ef03b42017-06-13 12:49:55 -0400119 public int getBondState(CachedBluetoothDevice device) {
120 return getCachedState(device).mBondState;
121 }
122
123 @Override
Jason Monk3fd0b142017-08-30 18:11:21 -0400124 public CachedBluetoothDevice getLastDevice() {
125 return mLastDevice;
126 }
127
128 @Override
Jason Monk9ef03b42017-06-13 12:49:55 -0400129 public int getMaxConnectionState(CachedBluetoothDevice device) {
130 return getCachedState(device).mMaxConnectionState;
131 }
132
133 @Override
Jason Monk88529052016-11-04 13:29:58 -0400134 public void addCallback(Callback cb) {
Adrian Roosa4b54862016-06-17 14:28:27 -0700135 mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget();
Jason Monk744cf642015-05-19 12:04:41 -0400136 mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400137 }
138
139 @Override
Jason Monk88529052016-11-04 13:29:58 -0400140 public void removeCallback(Callback cb) {
Adrian Roosa4b54862016-06-17 14:28:27 -0700141 mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400142 }
143
144 @Override
145 public boolean isBluetoothEnabled() {
Jason Monkbe3c5db2015-02-04 13:00:55 -0500146 return mEnabled;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400147 }
148
149 @Override
Jason Monkfac25382016-07-19 14:13:37 -0400150 public int getBluetoothState() {
151 return mState;
152 }
153
154 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400155 public boolean isBluetoothConnected() {
Jason Monka7d92b62015-05-27 10:20:37 -0400156 return mConnectionState == BluetoothAdapter.STATE_CONNECTED;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400157 }
158
159 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400160 public boolean isBluetoothConnecting() {
Jason Monka7d92b62015-05-27 10:20:37 -0400161 return mConnectionState == BluetoothAdapter.STATE_CONNECTING;
John Spurlockd1c86e22014-06-01 00:04:53 -0400162 }
163
164 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400165 public void setBluetoothEnabled(boolean enabled) {
Jason Monkbe3c5db2015-02-04 13:00:55 -0500166 if (mLocalBluetoothManager != null) {
167 mLocalBluetoothManager.getBluetoothAdapter().setBluetoothEnabled(enabled);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400168 }
169 }
170
171 @Override
172 public boolean isBluetoothSupported() {
Jason Monkbe3c5db2015-02-04 13:00:55 -0500173 return mLocalBluetoothManager != null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400174 }
175
John Spurlock486b78e2014-07-07 08:37:56 -0400176 @Override
Jason Monkbe3c5db2015-02-04 13:00:55 -0500177 public void connect(final CachedBluetoothDevice device) {
178 if (mLocalBluetoothManager == null || device == null) return;
179 device.connect(true);
John Spurlock486b78e2014-07-07 08:37:56 -0400180 }
181
182 @Override
Jason Monkbe3c5db2015-02-04 13:00:55 -0500183 public void disconnect(CachedBluetoothDevice device) {
184 if (mLocalBluetoothManager == null || device == null) return;
185 device.disconnect();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400186 }
187
188 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400189 public String getLastDeviceName() {
Jason Monkbe3c5db2015-02-04 13:00:55 -0500190 return mLastDevice != null ? mLastDevice.getName() : null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400191 }
192
Jason Monkbe3c5db2015-02-04 13:00:55 -0500193 @Override
194 public Collection<CachedBluetoothDevice> getDevices() {
195 return mLocalBluetoothManager != null
196 ? mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
197 : null;
Jason Monk4630c892014-12-08 16:36:16 -0500198 }
199
Jason Monkbe3c5db2015-02-04 13:00:55 -0500200 private void updateConnected() {
Jason Monk90970562015-06-19 10:04:49 -0400201 // Make sure our connection state is up to date.
202 int state = mLocalBluetoothManager.getBluetoothAdapter().getConnectionState();
Jason Monk6a73e632017-03-17 11:08:30 -0400203 if (mLastDevice != null && !mLastDevice.isConnected()) {
204 // Clear out last device if no longer connected.
205 mLastDevice = null;
Jason Monk90970562015-06-19 10:04:49 -0400206 }
Jason Monk6a73e632017-03-17 11:08:30 -0400207 // If any of the devices are in a higher state than the adapter, move the adapter into
208 // that state.
Jason Monkbe3c5db2015-02-04 13:00:55 -0500209 for (CachedBluetoothDevice device : getDevices()) {
Jason Monk6a73e632017-03-17 11:08:30 -0400210 int maxDeviceState = device.getMaxConnectionState();
211 if (maxDeviceState > state) {
212 state = maxDeviceState;
213 }
214 if (mLastDevice == null && device.isConnected()) {
215 // Set as last connected device only if we don't have one.
John Spurlock486b78e2014-07-07 08:37:56 -0400216 mLastDevice = device;
John Spurlock486b78e2014-07-07 08:37:56 -0400217 }
John Spurlock486b78e2014-07-07 08:37:56 -0400218 }
Jason Monk6a73e632017-03-17 11:08:30 -0400219
220 if (mLastDevice == null && state == BluetoothAdapter.STATE_CONNECTED) {
Jason Monkbba73172015-08-12 16:17:34 -0400221 // If somehow we think we are connected, but have no connected devices, we aren't
222 // connected.
Jason Monk6a73e632017-03-17 11:08:30 -0400223 state = BluetoothAdapter.STATE_DISCONNECTED;
224 }
225 if (state != mConnectionState) {
226 mConnectionState = state;
Jason Monkbba73172015-08-12 16:17:34 -0400227 mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
228 }
John Spurlock486b78e2014-07-07 08:37:56 -0400229 }
230
Jason Monkbe3c5db2015-02-04 13:00:55 -0500231 @Override
232 public void onBluetoothStateChanged(int bluetoothState) {
Jason Monkfac25382016-07-19 14:13:37 -0400233 mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
234 || bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
235 mState = bluetoothState;
Jason Monk744cf642015-05-19 12:04:41 -0400236 mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
John Spurlock486b78e2014-07-07 08:37:56 -0400237 }
238
Jason Monkbe3c5db2015-02-04 13:00:55 -0500239 @Override
240 public void onScanningStateChanged(boolean started) {
241 // Don't care.
242 }
Jason Monk4ae97d32014-12-17 10:14:33 -0500243
Jason Monkbe3c5db2015-02-04 13:00:55 -0500244 @Override
245 public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
246 cachedDevice.registerCallback(this);
247 updateConnected();
Jason Monk744cf642015-05-19 12:04:41 -0400248 mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500249 }
Jason Monk4ae97d32014-12-17 10:14:33 -0500250
Jason Monkbe3c5db2015-02-04 13:00:55 -0500251 @Override
252 public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
Jason Monk9ef03b42017-06-13 12:49:55 -0400253 mCachedState.remove(cachedDevice);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500254 updateConnected();
Jason Monk744cf642015-05-19 12:04:41 -0400255 mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500256 }
257
258 @Override
259 public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
Jason Monk9ef03b42017-06-13 12:49:55 -0400260 mCachedState.remove(cachedDevice);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500261 updateConnected();
Jason Monk744cf642015-05-19 12:04:41 -0400262 mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500263 }
264
265 @Override
266 public void onDeviceAttributesChanged() {
267 updateConnected();
Jason Monk744cf642015-05-19 12:04:41 -0400268 mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500269 }
270
271 @Override
272 public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
Jason Monk9ef03b42017-06-13 12:49:55 -0400273 mCachedState.remove(cachedDevice);
Jason Monkbe3c5db2015-02-04 13:00:55 -0500274 mLastDevice = cachedDevice;
275 updateConnected();
Jason Monk744cf642015-05-19 12:04:41 -0400276 mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
277 }
278
Pavlin Radoslavov1af33a12018-01-21 02:59:15 -0800279 @Override
280 public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
281
hughchen714b1c12018-03-23 15:54:29 +0800282 @Override
283 public void onProfileAudioStateChanged(int bluetoothProfile, int state) {}
284
Jason Monk9ef03b42017-06-13 12:49:55 -0400285 private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
286 ActuallyCachedState state = mCachedState.get(device);
287 if (state == null) {
288 state = new ActuallyCachedState(device, mHandler);
289 mBgHandler.post(state);
290 mCachedState.put(device, state);
291 return state;
292 }
293 return state;
294 }
295
296 private static class ActuallyCachedState implements Runnable {
297
298 private final WeakReference<CachedBluetoothDevice> mDevice;
299 private final Handler mUiHandler;
300 private int mBondState = BluetoothDevice.BOND_NONE;
301 private int mMaxConnectionState = BluetoothProfile.STATE_DISCONNECTED;
302
303 private ActuallyCachedState(CachedBluetoothDevice device, Handler uiHandler) {
304 mDevice = new WeakReference<>(device);
305 mUiHandler = uiHandler;
306 }
307
308 @Override
309 public void run() {
Jason Monk0f5d4022017-08-15 14:29:49 -0400310 CachedBluetoothDevice device = mDevice.get();
311 if (device != null) {
312 mBondState = device.getBondState();
313 mMaxConnectionState = device.getMaxConnectionState();
314 mUiHandler.removeMessages(H.MSG_PAIRED_DEVICES_CHANGED);
315 mUiHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
316 }
Jason Monk9ef03b42017-06-13 12:49:55 -0400317 }
318 }
319
Jason Monk744cf642015-05-19 12:04:41 -0400320 private final class H extends Handler {
Adrian Roosa4b54862016-06-17 14:28:27 -0700321 private final ArrayList<BluetoothController.Callback> mCallbacks = new ArrayList<>();
322
Jason Monk744cf642015-05-19 12:04:41 -0400323 private static final int MSG_PAIRED_DEVICES_CHANGED = 1;
324 private static final int MSG_STATE_CHANGED = 2;
Adrian Roosa4b54862016-06-17 14:28:27 -0700325 private static final int MSG_ADD_CALLBACK = 3;
326 private static final int MSG_REMOVE_CALLBACK = 4;
Jason Monk744cf642015-05-19 12:04:41 -0400327
Jason Monk9ef03b42017-06-13 12:49:55 -0400328 public H(Looper looper) {
329 super(looper);
330 }
331
Jason Monk744cf642015-05-19 12:04:41 -0400332 @Override
333 public void handleMessage(Message msg) {
334 switch (msg.what) {
335 case MSG_PAIRED_DEVICES_CHANGED:
336 firePairedDevicesChanged();
337 break;
338 case MSG_STATE_CHANGED:
339 fireStateChange();
340 break;
Adrian Roosa4b54862016-06-17 14:28:27 -0700341 case MSG_ADD_CALLBACK:
342 mCallbacks.add((BluetoothController.Callback) msg.obj);
343 break;
344 case MSG_REMOVE_CALLBACK:
345 mCallbacks.remove((BluetoothController.Callback) msg.obj);
346 break;
Jason Monk744cf642015-05-19 12:04:41 -0400347 }
348 }
349
350 private void firePairedDevicesChanged() {
351 for (BluetoothController.Callback cb : mCallbacks) {
352 cb.onBluetoothDevicesChanged();
353 }
354 }
355
356 private void fireStateChange() {
357 for (BluetoothController.Callback cb : mCallbacks) {
358 fireStateChange(cb);
359 }
360 }
361
362 private void fireStateChange(BluetoothController.Callback cb) {
Jason Monka7d92b62015-05-27 10:20:37 -0400363 cb.onBluetoothStateChange(mEnabled);
Jason Monk744cf642015-05-19 12:04:41 -0400364 }
John Spurlock486b78e2014-07-07 08:37:56 -0400365 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400366}