blob: 740ce4316fbb9f08194111d48f85fd8bc1d62c53 [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());
105
106 // Dock event broadcasts
107 addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
108
Jason Monk744cf642015-05-19 12:04:41 -0400109 mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
Manu Viswanadhand1992952016-10-25 20:38:23 +0530110 mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
Jason Monk7ce96b92015-02-02 11:27:58 -0500111 }
112
113 void registerProfileIntentReceiver() {
Manu Viswanadhand1992952016-10-25 20:38:23 +0530114 mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
Jason Monk744cf642015-05-19 12:04:41 -0400115 }
116
117 public void setReceiverHandler(android.os.Handler handler) {
118 mContext.unregisterReceiver(mBroadcastReceiver);
Manu Viswanadhand1992952016-10-25 20:38:23 +0530119 mContext.unregisterReceiver(mProfileBroadcastReceiver);
Jason Monk744cf642015-05-19 12:04:41 -0400120 mReceiverHandler = handler;
121 mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
122 registerProfileIntentReceiver();
Jason Monk7ce96b92015-02-02 11:27:58 -0500123 }
124
125 /** Register to start receiving callbacks for Bluetooth events. */
126 public void registerCallback(BluetoothCallback callback) {
127 synchronized (mCallbacks) {
128 mCallbacks.add(callback);
129 }
130 }
131
132 /** Unregister to stop receiving callbacks for Bluetooth events. */
133 public void unregisterCallback(BluetoothCallback callback) {
134 synchronized (mCallbacks) {
135 mCallbacks.remove(callback);
136 }
137 }
138
139 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
140 @Override
141 public void onReceive(Context context, Intent intent) {
142 String action = intent.getAction();
143 BluetoothDevice device = intent
144 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
145
146 Handler handler = mHandlerMap.get(action);
147 if (handler != null) {
148 handler.onReceive(context, intent, device);
149 }
150 }
151 };
152
Manu Viswanadhand1992952016-10-25 20:38:23 +0530153 private final BroadcastReceiver mProfileBroadcastReceiver = new BroadcastReceiver() {
154 @Override
155 public void onReceive(Context context, Intent intent) {
156 String action = intent.getAction();
157 BluetoothDevice device = intent
158 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
159
160 Handler handler = mHandlerMap.get(action);
161 if (handler != null) {
162 handler.onReceive(context, intent, device);
163 }
164 }
165 };
166
Jason Monk7ce96b92015-02-02 11:27:58 -0500167 private class AdapterStateChangedHandler implements Handler {
168 public void onReceive(Context context, Intent intent,
169 BluetoothDevice device) {
170 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
171 BluetoothAdapter.ERROR);
Manu Viswanadhand1992952016-10-25 20:38:23 +0530172 // Reregister Profile Broadcast Receiver as part of TURN OFF
173 if (state == BluetoothAdapter.STATE_OFF)
174 {
175 context.unregisterReceiver(mProfileBroadcastReceiver);
176 registerProfileIntentReceiver();
177 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500178 // update local profiles and get paired devices
179 mLocalAdapter.setBluetoothStateInt(state);
180 // send callback to update UI and possibly start scanning
181 synchronized (mCallbacks) {
182 for (BluetoothCallback callback : mCallbacks) {
183 callback.onBluetoothStateChanged(state);
184 }
185 }
186 // Inform CachedDeviceManager that the adapter state has changed
187 mDeviceManager.onBluetoothStateChanged(state);
188 }
189 }
190
191 private class ScanningStateChangedHandler implements Handler {
192 private final boolean mStarted;
193
194 ScanningStateChangedHandler(boolean started) {
195 mStarted = started;
196 }
197 public void onReceive(Context context, Intent intent,
198 BluetoothDevice device) {
199 synchronized (mCallbacks) {
200 for (BluetoothCallback callback : mCallbacks) {
201 callback.onScanningStateChanged(mStarted);
202 }
203 }
204 mDeviceManager.onScanningStateChanged(mStarted);
205 }
206 }
207
208 private class DeviceFoundHandler implements Handler {
209 public void onReceive(Context context, Intent intent,
210 BluetoothDevice device) {
211 short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
212 BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
213 String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
214 // TODO Pick up UUID. They should be available for 2.1 devices.
215 // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
216 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
217 if (cachedDevice == null) {
218 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
219 Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
220 + cachedDevice);
Jason Monk7ce96b92015-02-02 11:27:58 -0500221 }
222 cachedDevice.setRssi(rssi);
223 cachedDevice.setBtClass(btClass);
224 cachedDevice.setNewName(name);
225 cachedDevice.setVisible(true);
226 }
227 }
228
Jason Monkbe3c5db2015-02-04 13:00:55 -0500229 private class ConnectionStateChangedHandler implements Handler {
230 @Override
231 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
232 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
233 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
234 BluetoothAdapter.ERROR);
235 dispatchConnectionStateChanged(cachedDevice, state);
236 }
237 }
238
239 private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
240 synchronized (mCallbacks) {
241 for (BluetoothCallback callback : mCallbacks) {
242 callback.onConnectionStateChanged(cachedDevice, state);
243 }
244 }
245 }
246
247 void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
Jason Monk7ce96b92015-02-02 11:27:58 -0500248 synchronized (mCallbacks) {
249 for (BluetoothCallback callback : mCallbacks) {
250 callback.onDeviceAdded(cachedDevice);
251 }
252 }
253 }
254
255 private class DeviceDisappearedHandler implements Handler {
256 public void onReceive(Context context, Intent intent,
257 BluetoothDevice device) {
258 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
259 if (cachedDevice == null) {
260 Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
261 return;
262 }
263 if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
264 synchronized (mCallbacks) {
265 for (BluetoothCallback callback : mCallbacks) {
266 callback.onDeviceDeleted(cachedDevice);
267 }
268 }
269 }
270 }
271 }
272
273 private class NameChangedHandler implements Handler {
274 public void onReceive(Context context, Intent intent,
275 BluetoothDevice device) {
276 mDeviceManager.onDeviceNameUpdated(device);
277 }
278 }
279
280 private class BondStateChangedHandler implements Handler {
281 public void onReceive(Context context, Intent intent,
282 BluetoothDevice device) {
283 if (device == null) {
284 Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
285 return;
286 }
287 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
288 BluetoothDevice.ERROR);
289 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
290 if (cachedDevice == null) {
291 Log.w(TAG, "CachedBluetoothDevice for device " + device +
292 " not found, calling readPairedDevices().");
Jakub Pawlowski3eb490f2016-07-18 09:18:09 -0700293 if (readPairedDevices()) {
294 cachedDevice = mDeviceManager.findDevice(device);
Jason Monk7ce96b92015-02-02 11:27:58 -0500295 }
Jakub Pawlowski3eb490f2016-07-18 09:18:09 -0700296
Jason Monk7ce96b92015-02-02 11:27:58 -0500297 if (cachedDevice == null) {
Jakub Pawlowski3eb490f2016-07-18 09:18:09 -0700298 Log.w(TAG, "Got bonding state changed for " + device +
299 ", but we have no record of that device.");
300
301 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
302 dispatchDeviceAdded(cachedDevice);
Jason Monk7ce96b92015-02-02 11:27:58 -0500303 }
304 }
305
306 synchronized (mCallbacks) {
307 for (BluetoothCallback callback : mCallbacks) {
308 callback.onDeviceBondStateChanged(cachedDevice, bondState);
309 }
310 }
311 cachedDevice.onBondingStateChanged(bondState);
312
313 if (bondState == BluetoothDevice.BOND_NONE) {
314 int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
315 BluetoothDevice.ERROR);
316
317 showUnbondMessage(context, cachedDevice.getName(), reason);
318 }
319 }
320
321 /**
322 * Called when we have reached the unbonded state.
323 *
324 * @param reason one of the error reasons from
325 * BluetoothDevice.UNBOND_REASON_*
326 */
327 private void showUnbondMessage(Context context, String name, int reason) {
328 int errorMsg;
329
330 switch(reason) {
331 case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
332 errorMsg = R.string.bluetooth_pairing_pin_error_message;
333 break;
334 case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
335 errorMsg = R.string.bluetooth_pairing_rejected_error_message;
336 break;
337 case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
338 errorMsg = R.string.bluetooth_pairing_device_down_error_message;
339 break;
340 case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
341 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
342 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
343 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
344 errorMsg = R.string.bluetooth_pairing_error_message;
345 break;
346 default:
347 Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
348 return;
349 }
350 Utils.showError(context, name, errorMsg);
351 }
352 }
353
354 private class ClassChangedHandler implements Handler {
355 public void onReceive(Context context, Intent intent,
356 BluetoothDevice device) {
357 mDeviceManager.onBtClassChanged(device);
358 }
359 }
360
361 private class UuidChangedHandler implements Handler {
362 public void onReceive(Context context, Intent intent,
363 BluetoothDevice device) {
364 mDeviceManager.onUuidChanged(device);
365 }
366 }
367
368 private class PairingCancelHandler implements Handler {
369 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
370 if (device == null) {
371 Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
372 return;
373 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500374 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
Pavlin Radoslavovd2b033ae2015-06-04 11:46:22 -0700375 if (cachedDevice == null) {
376 Log.e(TAG, "ACTION_PAIRING_CANCEL with no cached device");
377 return;
378 }
379 int errorMsg = R.string.bluetooth_pairing_error_message;
Sanket Padawe4d08a482015-07-07 14:12:15 -0700380 if (context != null && cachedDevice != null) {
381 Utils.showError(context, cachedDevice.getName(), errorMsg);
382 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500383 }
384 }
385
386 private class DockEventHandler implements Handler {
387 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
388 // Remove if unpair device upon undocking
389 int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
390 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
391 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
392 if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
393 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
394 if (cachedDevice != null) {
395 cachedDevice.setVisible(false);
396 }
397 }
398 }
399 }
400 }
401 boolean readPairedDevices() {
402 Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
403 if (bondedDevices == null) {
404 return false;
405 }
406
407 boolean deviceAdded = false;
408 for (BluetoothDevice device : bondedDevices) {
409 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
410 if (cachedDevice == null) {
411 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
412 dispatchDeviceAdded(cachedDevice);
413 deviceAdded = true;
414 }
415 }
416
417 return deviceAdded;
418 }
419}