blob: d07da93cd35af68e67f115e6e72de69f4bbe0bd1 [file] [log] [blame]
Jason Monk7ce96b92015-02-02 11:27:58 -05001/*
2 * Copyright (C) 2011 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.settingslib.bluetooth;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothClass;
21import android.bluetooth.BluetoothDevice;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.util.Log;
27
28import com.android.settingslib.R;
29
30import java.util.ArrayList;
31import java.util.Collection;
32import java.util.HashMap;
33import java.util.Map;
34import java.util.Set;
35
36/**
37 * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
38 * API and dispatches the event on the UI thread to the right class in the
39 * Settings.
40 */
Fan Zhang82dd3b02016-12-27 13:13:00 -080041public class BluetoothEventManager {
Jason Monk7ce96b92015-02-02 11:27:58 -050042 private static final String TAG = "BluetoothEventManager";
43
44 private final LocalBluetoothAdapter mLocalAdapter;
45 private final CachedBluetoothDeviceManager mDeviceManager;
46 private LocalBluetoothProfileManager mProfileManager;
47 private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
48 private final Map<String, Handler> mHandlerMap;
49 private Context mContext;
50
51 private final Collection<BluetoothCallback> mCallbacks =
52 new ArrayList<BluetoothCallback>();
53
Jason Monk744cf642015-05-19 12:04:41 -040054 private android.os.Handler mReceiverHandler;
55
Jason Monk7ce96b92015-02-02 11:27:58 -050056 interface Handler {
57 void onReceive(Context context, Intent intent, BluetoothDevice device);
58 }
59
Jason Monk744cf642015-05-19 12:04:41 -040060 private void addHandler(String action, Handler handler) {
Jason Monk7ce96b92015-02-02 11:27:58 -050061 mHandlerMap.put(action, handler);
62 mAdapterIntentFilter.addAction(action);
63 }
64
65 void addProfileHandler(String action, Handler handler) {
66 mHandlerMap.put(action, handler);
67 mProfileIntentFilter.addAction(action);
68 }
69
70 // Set profile manager after construction due to circular dependency
71 void setProfileManager(LocalBluetoothProfileManager manager) {
72 mProfileManager = manager;
73 }
74
75 BluetoothEventManager(LocalBluetoothAdapter adapter,
76 CachedBluetoothDeviceManager deviceManager, Context context) {
77 mLocalAdapter = adapter;
78 mDeviceManager = deviceManager;
79 mAdapterIntentFilter = new IntentFilter();
80 mProfileIntentFilter = new IntentFilter();
81 mHandlerMap = new HashMap<String, Handler>();
82 mContext = context;
83
84 // Bluetooth on/off broadcasts
85 addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
Jason Monkbe3c5db2015-02-04 13:00:55 -050086 // Generic connected/not broadcast
87 addHandler(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED,
88 new ConnectionStateChangedHandler());
Jason Monk7ce96b92015-02-02 11:27:58 -050089
90 // Discovery broadcasts
91 addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
92 addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
93 addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
94 addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
95 addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
Jason Monk8c495be2015-06-03 16:00:24 -040096 addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler());
Jason Monk7ce96b92015-02-02 11:27:58 -050097
98 // Pairing broadcasts
99 addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
100 addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
101
102 // Fine-grained state broadcasts
103 addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
104 addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
Jack He6258aae2017-06-29 17:01:23 -0700105 addHandler(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, new BatteryLevelChangedHandler());
Jason Monk7ce96b92015-02-02 11:27:58 -0500106
107 // Dock event broadcasts
108 addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
109
Jason Monk744cf642015-05-19 12:04:41 -0400110 mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
Jason Monk7ce96b92015-02-02 11:27:58 -0500111 }
112
113 void registerProfileIntentReceiver() {
Jason Monk744cf642015-05-19 12:04:41 -0400114 mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
115 }
116
117 public void setReceiverHandler(android.os.Handler handler) {
118 mContext.unregisterReceiver(mBroadcastReceiver);
119 mReceiverHandler = handler;
120 mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
121 registerProfileIntentReceiver();
Jason Monk7ce96b92015-02-02 11:27:58 -0500122 }
123
124 /** Register to start receiving callbacks for Bluetooth events. */
125 public void registerCallback(BluetoothCallback callback) {
126 synchronized (mCallbacks) {
127 mCallbacks.add(callback);
128 }
129 }
130
131 /** Unregister to stop receiving callbacks for Bluetooth events. */
132 public void unregisterCallback(BluetoothCallback callback) {
133 synchronized (mCallbacks) {
134 mCallbacks.remove(callback);
135 }
136 }
137
138 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
139 @Override
140 public void onReceive(Context context, Intent intent) {
141 String action = intent.getAction();
142 BluetoothDevice device = intent
143 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
144
145 Handler handler = mHandlerMap.get(action);
146 if (handler != null) {
147 handler.onReceive(context, intent, device);
148 }
149 }
150 };
151
152 private class AdapterStateChangedHandler implements Handler {
153 public void onReceive(Context context, Intent intent,
154 BluetoothDevice device) {
155 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
156 BluetoothAdapter.ERROR);
157 // update local profiles and get paired devices
158 mLocalAdapter.setBluetoothStateInt(state);
159 // send callback to update UI and possibly start scanning
160 synchronized (mCallbacks) {
161 for (BluetoothCallback callback : mCallbacks) {
162 callback.onBluetoothStateChanged(state);
163 }
164 }
165 // Inform CachedDeviceManager that the adapter state has changed
166 mDeviceManager.onBluetoothStateChanged(state);
167 }
168 }
169
170 private class ScanningStateChangedHandler implements Handler {
171 private final boolean mStarted;
172
173 ScanningStateChangedHandler(boolean started) {
174 mStarted = started;
175 }
176 public void onReceive(Context context, Intent intent,
177 BluetoothDevice device) {
178 synchronized (mCallbacks) {
179 for (BluetoothCallback callback : mCallbacks) {
180 callback.onScanningStateChanged(mStarted);
181 }
182 }
183 mDeviceManager.onScanningStateChanged(mStarted);
184 }
185 }
186
187 private class DeviceFoundHandler implements Handler {
188 public void onReceive(Context context, Intent intent,
189 BluetoothDevice device) {
190 short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
191 BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
192 String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
193 // TODO Pick up UUID. They should be available for 2.1 devices.
194 // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
195 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
196 if (cachedDevice == null) {
197 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
198 Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
199 + cachedDevice);
Jason Monk7ce96b92015-02-02 11:27:58 -0500200 }
201 cachedDevice.setRssi(rssi);
202 cachedDevice.setBtClass(btClass);
203 cachedDevice.setNewName(name);
204 cachedDevice.setVisible(true);
205 }
206 }
207
Jason Monkbe3c5db2015-02-04 13:00:55 -0500208 private class ConnectionStateChangedHandler implements Handler {
209 @Override
210 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
211 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
212 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
213 BluetoothAdapter.ERROR);
214 dispatchConnectionStateChanged(cachedDevice, state);
215 }
216 }
217
218 private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
219 synchronized (mCallbacks) {
220 for (BluetoothCallback callback : mCallbacks) {
221 callback.onConnectionStateChanged(cachedDevice, state);
222 }
223 }
224 }
225
226 void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
Jason Monk7ce96b92015-02-02 11:27:58 -0500227 synchronized (mCallbacks) {
228 for (BluetoothCallback callback : mCallbacks) {
229 callback.onDeviceAdded(cachedDevice);
230 }
231 }
232 }
233
234 private class DeviceDisappearedHandler implements Handler {
235 public void onReceive(Context context, Intent intent,
236 BluetoothDevice device) {
237 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
238 if (cachedDevice == null) {
239 Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
240 return;
241 }
242 if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
243 synchronized (mCallbacks) {
244 for (BluetoothCallback callback : mCallbacks) {
245 callback.onDeviceDeleted(cachedDevice);
246 }
247 }
248 }
249 }
250 }
251
252 private class NameChangedHandler implements Handler {
253 public void onReceive(Context context, Intent intent,
254 BluetoothDevice device) {
255 mDeviceManager.onDeviceNameUpdated(device);
256 }
257 }
258
259 private class BondStateChangedHandler implements Handler {
260 public void onReceive(Context context, Intent intent,
261 BluetoothDevice device) {
262 if (device == null) {
263 Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
264 return;
265 }
266 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
267 BluetoothDevice.ERROR);
268 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
269 if (cachedDevice == null) {
270 Log.w(TAG, "CachedBluetoothDevice for device " + device +
271 " not found, calling readPairedDevices().");
Jakub Pawlowski3eb490f2016-07-18 09:18:09 -0700272 if (readPairedDevices()) {
273 cachedDevice = mDeviceManager.findDevice(device);
Jason Monk7ce96b92015-02-02 11:27:58 -0500274 }
Jakub Pawlowski3eb490f2016-07-18 09:18:09 -0700275
Jason Monk7ce96b92015-02-02 11:27:58 -0500276 if (cachedDevice == null) {
Jakub Pawlowski3eb490f2016-07-18 09:18:09 -0700277 Log.w(TAG, "Got bonding state changed for " + device +
278 ", but we have no record of that device.");
279
280 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
281 dispatchDeviceAdded(cachedDevice);
Jason Monk7ce96b92015-02-02 11:27:58 -0500282 }
283 }
284
285 synchronized (mCallbacks) {
286 for (BluetoothCallback callback : mCallbacks) {
287 callback.onDeviceBondStateChanged(cachedDevice, bondState);
288 }
289 }
290 cachedDevice.onBondingStateChanged(bondState);
291
292 if (bondState == BluetoothDevice.BOND_NONE) {
293 int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
294 BluetoothDevice.ERROR);
295
296 showUnbondMessage(context, cachedDevice.getName(), reason);
297 }
298 }
299
300 /**
301 * Called when we have reached the unbonded state.
302 *
303 * @param reason one of the error reasons from
304 * BluetoothDevice.UNBOND_REASON_*
305 */
306 private void showUnbondMessage(Context context, String name, int reason) {
307 int errorMsg;
308
309 switch(reason) {
310 case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
311 errorMsg = R.string.bluetooth_pairing_pin_error_message;
312 break;
313 case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
314 errorMsg = R.string.bluetooth_pairing_rejected_error_message;
315 break;
316 case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
317 errorMsg = R.string.bluetooth_pairing_device_down_error_message;
318 break;
319 case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
320 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
321 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
322 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
323 errorMsg = R.string.bluetooth_pairing_error_message;
324 break;
325 default:
326 Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
327 return;
328 }
329 Utils.showError(context, name, errorMsg);
330 }
331 }
332
333 private class ClassChangedHandler implements Handler {
334 public void onReceive(Context context, Intent intent,
335 BluetoothDevice device) {
336 mDeviceManager.onBtClassChanged(device);
337 }
338 }
339
340 private class UuidChangedHandler implements Handler {
341 public void onReceive(Context context, Intent intent,
342 BluetoothDevice device) {
343 mDeviceManager.onUuidChanged(device);
344 }
345 }
346
347 private class PairingCancelHandler implements Handler {
348 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
349 if (device == null) {
350 Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
351 return;
352 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500353 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
Pavlin Radoslavovd2b033ae2015-06-04 11:46:22 -0700354 if (cachedDevice == null) {
355 Log.e(TAG, "ACTION_PAIRING_CANCEL with no cached device");
356 return;
357 }
358 int errorMsg = R.string.bluetooth_pairing_error_message;
Sanket Padawe4d08a482015-07-07 14:12:15 -0700359 if (context != null && cachedDevice != null) {
360 Utils.showError(context, cachedDevice.getName(), errorMsg);
361 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500362 }
363 }
364
365 private class DockEventHandler implements Handler {
366 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
367 // Remove if unpair device upon undocking
368 int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
369 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
370 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
371 if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
372 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
373 if (cachedDevice != null) {
374 cachedDevice.setVisible(false);
375 }
376 }
377 }
378 }
379 }
Jack He6258aae2017-06-29 17:01:23 -0700380
381 private class BatteryLevelChangedHandler implements Handler {
382 public void onReceive(Context context, Intent intent,
383 BluetoothDevice device) {
384 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
385 if (cachedDevice != null) {
386 cachedDevice.refresh();
387 }
388 }
389 }
390
Jason Monk7ce96b92015-02-02 11:27:58 -0500391 boolean readPairedDevices() {
392 Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
393 if (bondedDevices == null) {
394 return false;
395 }
396
397 boolean deviceAdded = false;
398 for (BluetoothDevice device : bondedDevices) {
399 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
400 if (cachedDevice == null) {
401 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
402 dispatchDeviceAdded(cachedDevice);
403 deviceAdded = true;
404 }
405 }
406
407 return deviceAdded;
408 }
409}