blob: 81e1e4517e941c7cce062b92906c032da4e277ef [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
John Spurlock486b78e2014-07-07 08:37:56 -040019import static android.bluetooth.BluetoothAdapter.ERROR;
20import static com.android.systemui.statusbar.policy.BluetoothUtil.connectionStateToString;
21import static com.android.systemui.statusbar.policy.BluetoothUtil.deviceToString;
John Spurlock486b78e2014-07-07 08:37:56 -040022import static com.android.systemui.statusbar.policy.BluetoothUtil.profileToString;
23import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToProfile;
24import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToString;
25import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString;
26
Jason Monk4630c892014-12-08 16:36:16 -050027import android.bluetooth.BluetoothA2dp;
28import android.bluetooth.BluetoothA2dpSink;
John Spurlockaf8d6c42014-05-07 17:49:08 -040029import android.bluetooth.BluetoothAdapter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040030import android.bluetooth.BluetoothDevice;
Jason Monk4630c892014-12-08 16:36:16 -050031import android.bluetooth.BluetoothHeadset;
32import android.bluetooth.BluetoothHeadsetClient;
33import android.bluetooth.BluetoothInputDevice;
John Spurlock486b78e2014-07-07 08:37:56 -040034import android.bluetooth.BluetoothManager;
Jason Monk4630c892014-12-08 16:36:16 -050035import android.bluetooth.BluetoothMap;
36import android.bluetooth.BluetoothPan;
John Spurlock486b78e2014-07-07 08:37:56 -040037import android.bluetooth.BluetoothProfile;
38import android.bluetooth.BluetoothProfile.ServiceListener;
John Spurlockaf8d6c42014-05-07 17:49:08 -040039import android.content.BroadcastReceiver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
Jason Monk4ae97d32014-12-17 10:14:33 -050043import android.os.Handler;
44import android.os.Looper;
45import android.os.Message;
John Spurlock486b78e2014-07-07 08:37:56 -040046import android.os.ParcelUuid;
47import android.util.ArrayMap;
48import android.util.ArraySet;
49import android.util.Log;
Jason Monk4630c892014-12-08 16:36:16 -050050import android.util.SparseArray;
John Spurlockaf8d6c42014-05-07 17:49:08 -040051
John Spurlock486b78e2014-07-07 08:37:56 -040052import com.android.systemui.statusbar.policy.BluetoothUtil.Profile;
53
54import java.io.FileDescriptor;
55import java.io.PrintWriter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040056import java.util.ArrayList;
John Spurlockaf8d6c42014-05-07 17:49:08 -040057import java.util.Set;
58
John Spurlock486b78e2014-07-07 08:37:56 -040059public class BluetoothControllerImpl implements BluetoothController {
60 private static final String TAG = "BluetoothController";
61 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jason Monk4630c892014-12-08 16:36:16 -050062 // This controls the order in which we check the states. Since a device can only have
63 // one state on screen, but can have multiple profiles, the later states override the
64 // value of earlier states. So if a device has a profile in CONNECTING and one in
65 // CONNECTED, it will show as CONNECTED, theoretically this shouldn't really happen often,
66 // but seemed worth noting.
67 private static final int[] CONNECTION_STATES = {
68 BluetoothProfile.STATE_DISCONNECTED,
69 BluetoothProfile.STATE_DISCONNECTING,
70 BluetoothProfile.STATE_CONNECTING,
71 BluetoothProfile.STATE_CONNECTED,
72 };
Jason Monk4ae97d32014-12-17 10:14:33 -050073 // Update all the BT device states.
74 private static final int MSG_UPDATE_CONNECTION_STATES = 1;
75 // Update just one BT device.
76 private static final int MSG_UPDATE_SINGLE_CONNECTION_STATE = 2;
77 // Update whether devices are bonded or not.
78 private static final int MSG_UPDATE_BONDED_DEVICES = 3;
79
80 private static final int MSG_ADD_PROFILE = 4;
81 private static final int MSG_REM_PROFILE = 5;
John Spurlockaf8d6c42014-05-07 17:49:08 -040082
John Spurlock486b78e2014-07-07 08:37:56 -040083 private final Context mContext;
John Spurlockd1c86e22014-06-01 00:04:53 -040084 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
John Spurlockaf8d6c42014-05-07 17:49:08 -040085 private final BluetoothAdapter mAdapter;
John Spurlock486b78e2014-07-07 08:37:56 -040086 private final Receiver mReceiver = new Receiver();
87 private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>();
Jason Monk4630c892014-12-08 16:36:16 -050088 private final SparseArray<BluetoothProfile> mProfiles = new SparseArray<>();
John Spurlockaf8d6c42014-05-07 17:49:08 -040089
Jason Monk4ae97d32014-12-17 10:14:33 -050090 private final H mHandler;
91
John Spurlockd1c86e22014-06-01 00:04:53 -040092 private boolean mEnabled;
93 private boolean mConnecting;
94 private BluetoothDevice mLastDevice;
John Spurlockaf8d6c42014-05-07 17:49:08 -040095
Jason Monk4ae97d32014-12-17 10:14:33 -050096 public BluetoothControllerImpl(Context context, Looper bgLooper) {
John Spurlock486b78e2014-07-07 08:37:56 -040097 mContext = context;
Jason Monk4ae97d32014-12-17 10:14:33 -050098 mHandler = new H(bgLooper);
99
John Spurlock486b78e2014-07-07 08:37:56 -0400100 final BluetoothManager bluetoothManager =
101 (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
102 mAdapter = bluetoothManager.getAdapter();
103 if (mAdapter == null) {
104 Log.w(TAG, "Default BT adapter not found");
105 return;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400106 }
John Spurlock486b78e2014-07-07 08:37:56 -0400107
108 mReceiver.register();
109 setAdapterState(mAdapter.getState());
Jason Monk4ae97d32014-12-17 10:14:33 -0500110 updateBondedDevices();
Jason Monk4630c892014-12-08 16:36:16 -0500111 bindAllProfiles();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400112 }
113
John Spurlock486b78e2014-07-07 08:37:56 -0400114 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
115 pw.println("BluetoothController state:");
116 pw.print(" mAdapter="); pw.println(mAdapter);
117 pw.print(" mEnabled="); pw.println(mEnabled);
118 pw.print(" mConnecting="); pw.println(mConnecting);
119 pw.print(" mLastDevice="); pw.println(mLastDevice);
120 pw.print(" mCallbacks.size="); pw.println(mCallbacks.size());
Jason Monk4630c892014-12-08 16:36:16 -0500121 pw.print(" mProfiles="); pw.println(profilesToString(mProfiles));
John Spurlock486b78e2014-07-07 08:37:56 -0400122 pw.print(" mDeviceInfo.size="); pw.println(mDeviceInfo.size());
123 for (int i = 0; i < mDeviceInfo.size(); i++) {
124 final BluetoothDevice device = mDeviceInfo.keyAt(i);
125 final DeviceInfo info = mDeviceInfo.valueAt(i);
126 pw.print(" "); pw.print(deviceToString(device));
127 pw.print('('); pw.print(uuidsToString(device)); pw.print(')');
128 pw.print(" "); pw.println(infoToString(info));
129 }
130 }
131
132 private static String infoToString(DeviceInfo info) {
133 return info == null ? null : ("connectionState=" +
Jason Monk4ae97d32014-12-17 10:14:33 -0500134 connectionStateToString(CONNECTION_STATES[info.connectionStateIndex])
135 + ",bonded=" + info.bonded + ",profiles="
136 + profilesToString(info.connectedProfiles));
Jason Monk4630c892014-12-08 16:36:16 -0500137 }
138
139 private static String profilesToString(SparseArray<?> profiles) {
140 final int N = profiles.size();
141 final StringBuffer buffer = new StringBuffer();
142 buffer.append('[');
143 for (int i = 0; i < N; i++) {
144 if (i != 0) {
145 buffer.append(',');
146 }
147 buffer.append(BluetoothUtil.profileToString(profiles.keyAt(i)));
148 }
149 buffer.append(']');
150 return buffer.toString();
John Spurlock486b78e2014-07-07 08:37:56 -0400151 }
152
John Spurlockd1c86e22014-06-01 00:04:53 -0400153 public void addStateChangedCallback(Callback cb) {
154 mCallbacks.add(cb);
John Spurlock486b78e2014-07-07 08:37:56 -0400155 fireStateChange(cb);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400156 }
157
158 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400159 public void removeStateChangedCallback(Callback cb) {
160 mCallbacks.remove(cb);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400161 }
162
163 @Override
164 public boolean isBluetoothEnabled() {
165 return mAdapter != null && mAdapter.isEnabled();
166 }
167
168 @Override
169 public boolean isBluetoothConnected() {
170 return mAdapter != null
171 && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
172 }
173
174 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400175 public boolean isBluetoothConnecting() {
176 return mAdapter != null
177 && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTING;
178 }
179
180 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400181 public void setBluetoothEnabled(boolean enabled) {
182 if (mAdapter != null) {
183 if (enabled) {
184 mAdapter.enable();
185 } else {
186 mAdapter.disable();
187 }
188 }
189 }
190
191 @Override
192 public boolean isBluetoothSupported() {
193 return mAdapter != null;
194 }
195
John Spurlock486b78e2014-07-07 08:37:56 -0400196 @Override
197 public ArraySet<PairedDevice> getPairedDevices() {
198 final ArraySet<PairedDevice> rt = new ArraySet<>();
199 for (int i = 0; i < mDeviceInfo.size(); i++) {
200 final BluetoothDevice device = mDeviceInfo.keyAt(i);
201 final DeviceInfo info = mDeviceInfo.valueAt(i);
202 if (!info.bonded) continue;
203 final PairedDevice paired = new PairedDevice();
204 paired.id = device.getAddress();
205 paired.tag = device;
206 paired.name = device.getAliasName();
Jason Monk4ae97d32014-12-17 10:14:33 -0500207 paired.state = connectionStateToPairedDeviceState(info.connectionStateIndex);
John Spurlock486b78e2014-07-07 08:37:56 -0400208 rt.add(paired);
209 }
210 return rt;
211 }
212
Jason Monk4ae97d32014-12-17 10:14:33 -0500213 private static int connectionStateToPairedDeviceState(int index) {
214 int state = CONNECTION_STATES[index];
John Spurlock486b78e2014-07-07 08:37:56 -0400215 if (state == BluetoothAdapter.STATE_CONNECTED) return PairedDevice.STATE_CONNECTED;
216 if (state == BluetoothAdapter.STATE_CONNECTING) return PairedDevice.STATE_CONNECTING;
217 if (state == BluetoothAdapter.STATE_DISCONNECTING) return PairedDevice.STATE_DISCONNECTING;
218 return PairedDevice.STATE_DISCONNECTED;
219 }
220
221 @Override
222 public void connect(final PairedDevice pd) {
223 connect(pd, true);
224 }
225
226 @Override
227 public void disconnect(PairedDevice pd) {
228 connect(pd, false);
229 }
230
231 private void connect(PairedDevice pd, final boolean connect) {
232 if (mAdapter == null || pd == null || pd.tag == null) return;
233 final BluetoothDevice device = (BluetoothDevice) pd.tag;
Jason Monk4630c892014-12-08 16:36:16 -0500234 final DeviceInfo info = mDeviceInfo.get(device);
John Spurlock486b78e2014-07-07 08:37:56 -0400235 final String action = connect ? "connect" : "disconnect";
236 if (DEBUG) Log.d(TAG, action + " " + deviceToString(device));
John Spurlock27bdff72014-07-16 21:14:50 -0400237 final ParcelUuid[] uuids = device.getUuids();
238 if (uuids == null) {
239 Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device));
240 return;
241 }
Jason Monk4630c892014-12-08 16:36:16 -0500242 SparseArray<Boolean> profiles = new SparseArray<>();
243 if (connect) {
244 // When connecting add every profile we can recognize by uuid.
245 for (ParcelUuid uuid : uuids) {
246 final int profile = uuidToProfile(uuid);
247 if (profile == 0) {
248 Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: "
249 + uuidToString(uuid));
250 continue;
251 }
252 final boolean connected = info.connectedProfiles.get(profile, false);
253 if (!connected) {
254 profiles.put(profile, true);
255 }
John Spurlock486b78e2014-07-07 08:37:56 -0400256 }
Jason Monk4630c892014-12-08 16:36:16 -0500257 } else {
258 // When disconnecting, just add every profile we know they are connected to.
259 profiles = info.connectedProfiles;
John Spurlock486b78e2014-07-07 08:37:56 -0400260 }
261 for (int i = 0; i < profiles.size(); i++) {
262 final int profile = profiles.keyAt(i);
Jason Monk4630c892014-12-08 16:36:16 -0500263 if (mProfiles.indexOfKey(profile) >= 0) {
264 final Profile p = BluetoothUtil.getProfile(mProfiles.get(profile));
265 final boolean ok = connect ? p.connect(device) : p.disconnect(device);
266 if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " "
267 + (ok ? "succeeded" : "failed"));
268 } else {
269 Log.w(TAG, "Unable get get Profile for " + profileToString(profile));
270 }
John Spurlock486b78e2014-07-07 08:37:56 -0400271 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400272 }
273
274 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400275 public String getLastDeviceName() {
John Spurlock486b78e2014-07-07 08:37:56 -0400276 return mLastDevice != null ? mLastDevice.getAliasName() : null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400277 }
278
Jason Monk4ae97d32014-12-17 10:14:33 -0500279 private void updateBondedDevices() {
280 mHandler.removeMessages(MSG_UPDATE_BONDED_DEVICES);
281 mHandler.sendEmptyMessage(MSG_UPDATE_BONDED_DEVICES);
282 }
283
284 private void updateConnectionStates() {
285 mHandler.removeMessages(MSG_UPDATE_CONNECTION_STATES);
286 mHandler.removeMessages(MSG_UPDATE_SINGLE_CONNECTION_STATE);
287 mHandler.sendEmptyMessage(MSG_UPDATE_CONNECTION_STATES);
288 }
289
290 private void updateConnectionState(BluetoothDevice device, int profile, int state) {
291 if (mHandler.hasMessages(MSG_UPDATE_CONNECTION_STATES)) {
292 // If we are about to update all the devices, then we don't need to update this one.
293 return;
294 }
295 mHandler.obtainMessage(MSG_UPDATE_SINGLE_CONNECTION_STATE, profile, state, device)
296 .sendToTarget();
297 }
298
299 private void handleUpdateBondedDevices() {
John Spurlock486b78e2014-07-07 08:37:56 -0400300 if (mAdapter == null) return;
301 final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
302 for (DeviceInfo info : mDeviceInfo.values()) {
303 info.bonded = false;
304 }
305 int bondedCount = 0;
306 BluetoothDevice lastBonded = null;
307 if (bondedDevices != null) {
308 for (BluetoothDevice bondedDevice : bondedDevices) {
309 final boolean bonded = bondedDevice.getBondState() != BluetoothDevice.BOND_NONE;
310 updateInfo(bondedDevice).bonded = bonded;
311 if (bonded) {
312 bondedCount++;
313 lastBonded = bondedDevice;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400314 }
315 }
316 }
John Spurlock486b78e2014-07-07 08:37:56 -0400317 if (mLastDevice == null && bondedCount == 1) {
318 mLastDevice = lastBonded;
319 }
Jason Monk4ae97d32014-12-17 10:14:33 -0500320 updateConnectionStates();
321 firePairedDevicesChanged();
322 }
323
324 private void handleUpdateConnectionStates() {
325 final int N = mDeviceInfo.size();
326 for (int i = 0; i < N; i++) {
327 BluetoothDevice device = mDeviceInfo.keyAt(i);
328 DeviceInfo info = updateInfo(device);
329 info.connectionStateIndex = 0;
330 info.connectedProfiles.clear();
331 for (int j = 0; j < mProfiles.size(); j++) {
332 int state = mProfiles.valueAt(j).getConnectionState(device);
333 handleUpdateConnectionState(device, mProfiles.keyAt(j), state);
334 }
335 }
336 handleConnectionChange();
337 firePairedDevicesChanged();
338 }
339
340 private void handleUpdateConnectionState(BluetoothDevice device, int profile, int state) {
341 if (DEBUG) Log.d(TAG, "updateConnectionState " + BluetoothUtil.deviceToString(device)
342 + " " + BluetoothUtil.profileToString(profile)
343 + " " + BluetoothUtil.connectionStateToString(state));
344 DeviceInfo info = updateInfo(device);
345 int stateIndex = 0;
346 for (int i = 0; i < CONNECTION_STATES.length; i++) {
347 if (CONNECTION_STATES[i] == state) {
348 stateIndex = i;
349 break;
350 }
351 }
352 info.profileStates.put(profile, stateIndex);
353
354 info.connectionStateIndex = 0;
355 final int N = info.profileStates.size();
356 for (int i = 0; i < N; i++) {
357 if (info.profileStates.valueAt(i) > info.connectionStateIndex) {
358 info.connectionStateIndex = info.profileStates.valueAt(i);
359 }
360 }
361 if (state == BluetoothProfile.STATE_CONNECTED) {
362 info.connectedProfiles.put(profile, true);
363 } else {
364 info.connectedProfiles.remove(profile);
365 }
366 }
367
368 private void handleConnectionChange() {
Jason Monk4630c892014-12-08 16:36:16 -0500369 // If we are no longer connected to the current device, see if we are connected to
370 // something else, so we don't display a name we aren't connected to.
371 if (mLastDevice != null &&
Jason Monk4ae97d32014-12-17 10:14:33 -0500372 CONNECTION_STATES[mDeviceInfo.get(mLastDevice).connectionStateIndex]
373 != BluetoothProfile.STATE_CONNECTED) {
Jason Monk4630c892014-12-08 16:36:16 -0500374 // Make sure we don't keep this device while it isn't connected.
375 mLastDevice = null;
376 // Look for anything else connected.
377 final int size = mDeviceInfo.size();
378 for (int i = 0; i < size; i++) {
379 BluetoothDevice device = mDeviceInfo.keyAt(i);
380 DeviceInfo info = mDeviceInfo.valueAt(i);
Jason Monk4ae97d32014-12-17 10:14:33 -0500381 if (CONNECTION_STATES[info.connectionStateIndex]
382 == BluetoothProfile.STATE_CONNECTED) {
Jason Monk4630c892014-12-08 16:36:16 -0500383 mLastDevice = device;
384 break;
385 }
386 }
387 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400388 }
389
Jason Monk4630c892014-12-08 16:36:16 -0500390 private void bindAllProfiles() {
391 // Note: This needs to contain all of the types that can be returned by BluetoothUtil
392 // otherwise we can't find the profiles we need when we connect/disconnect.
393 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
394 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP_SINK);
395 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.AVRCP_CONTROLLER);
396 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
397 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET_CLIENT);
398 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.INPUT_DEVICE);
399 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.MAP);
400 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.PAN);
401 // Note Health is not in this list because health devices aren't 'connected'.
402 // If profiles are expanded to use more than just connection state and connect/disconnect
403 // then it should be added.
404 }
405
John Spurlock486b78e2014-07-07 08:37:56 -0400406 private void firePairedDevicesChanged() {
John Spurlockd1c86e22014-06-01 00:04:53 -0400407 for (Callback cb : mCallbacks) {
John Spurlock486b78e2014-07-07 08:37:56 -0400408 cb.onBluetoothPairedDevicesChanged();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400409 }
410 }
John Spurlockccb6b9a2014-05-17 15:54:40 -0400411
John Spurlock486b78e2014-07-07 08:37:56 -0400412 private void setAdapterState(int adapterState) {
413 final boolean enabled = adapterState == BluetoothAdapter.STATE_ON;
414 if (mEnabled == enabled) return;
415 mEnabled = enabled;
416 fireStateChange();
417 }
418
419 private void setConnecting(boolean connecting) {
420 if (mConnecting == connecting) return;
421 mConnecting = connecting;
422 fireStateChange();
423 }
424
425 private void fireStateChange() {
426 for (Callback cb : mCallbacks) {
427 fireStateChange(cb);
428 }
429 }
430
431 private void fireStateChange(Callback cb) {
John Spurlockd1c86e22014-06-01 00:04:53 -0400432 cb.onBluetoothStateChange(mEnabled, mConnecting);
John Spurlockccb6b9a2014-05-17 15:54:40 -0400433 }
John Spurlock486b78e2014-07-07 08:37:56 -0400434
Jason Monk4ae97d32014-12-17 10:14:33 -0500435 private static int getProfileFromAction(String action) {
436 if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
437 return BluetoothProfile.A2DP;
438 } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
439 return BluetoothProfile.HEADSET;
440 } else if (BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
441 return BluetoothProfile.A2DP_SINK;
442 } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
443 return BluetoothProfile.HEADSET_CLIENT;
444 } else if (BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
445 return BluetoothProfile.INPUT_DEVICE;
446 } else if (BluetoothMap.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
447 return BluetoothProfile.MAP;
448 } else if (BluetoothPan.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
449 return BluetoothProfile.PAN;
450 }
451 if (DEBUG) Log.d(TAG, "Unknown action " + action);
452 return -1;
453 }
454
Jason Monk4630c892014-12-08 16:36:16 -0500455 private final ServiceListener mProfileListener = new ServiceListener() {
456 @Override
457 public void onServiceDisconnected(int profile) {
Jason Monk4ae97d32014-12-17 10:14:33 -0500458 if (DEBUG) Log.d(TAG, "Disconnected from " + BluetoothUtil.profileToString(profile));
459 // We lost a profile, don't do any updates until it gets removed.
460 mHandler.removeMessages(MSG_UPDATE_CONNECTION_STATES);
461 mHandler.removeMessages(MSG_UPDATE_SINGLE_CONNECTION_STATE);
462 mHandler.obtainMessage(MSG_REM_PROFILE, profile, 0).sendToTarget();
Jason Monk4630c892014-12-08 16:36:16 -0500463 }
464
465 @Override
466 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jason Monk4ae97d32014-12-17 10:14:33 -0500467 if (DEBUG) Log.d(TAG, "Connected to " + BluetoothUtil.profileToString(profile));
468 mHandler.obtainMessage(MSG_ADD_PROFILE, profile, 0, proxy).sendToTarget();
Jason Monk4630c892014-12-08 16:36:16 -0500469 }
470 };
471
John Spurlock486b78e2014-07-07 08:37:56 -0400472 private final class Receiver extends BroadcastReceiver {
473 public void register() {
474 final IntentFilter filter = new IntentFilter();
475 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
476 filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
477 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
478 filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
Jason Monk4630c892014-12-08 16:36:16 -0500479 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
480 filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
481 filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
482 filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
483 filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
484 filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
485 filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
John Spurlock486b78e2014-07-07 08:37:56 -0400486 mContext.registerReceiver(this, filter);
487 }
488
489 @Override
490 public void onReceive(Context context, Intent intent) {
491 final String action = intent.getAction();
492 final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Jason Monk4ae97d32014-12-17 10:14:33 -0500493
John Spurlock486b78e2014-07-07 08:37:56 -0400494 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
495 setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR));
Jason Monk4ae97d32014-12-17 10:14:33 -0500496 updateBondedDevices();
John Spurlock486b78e2014-07-07 08:37:56 -0400497 if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled);
498 } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
Jason Monk4630c892014-12-08 16:36:16 -0500499 updateInfo(device);
John Spurlock486b78e2014-07-07 08:37:56 -0400500 final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
501 ERROR);
John Spurlock486b78e2014-07-07 08:37:56 -0400502 mLastDevice = device;
503 if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED "
504 + connectionStateToString(state) + " " + deviceToString(device));
Jason Monk4630c892014-12-08 16:36:16 -0500505 setConnecting(state == BluetoothAdapter.STATE_CONNECTING);
John Spurlock486b78e2014-07-07 08:37:56 -0400506 } else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
507 updateInfo(device);
508 mLastDevice = device;
509 } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
510 if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device);
Jason Monk4ae97d32014-12-17 10:14:33 -0500511 updateBondedDevices();
512 } else {
513 int profile = getProfileFromAction(intent.getAction());
514 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
515 if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGE "
516 + BluetoothUtil.profileToString(profile)
517 + " " + BluetoothUtil.connectionStateToString(state));
518 if ((profile != -1) && (state != -1)) {
519 updateConnectionState(device, profile, state);
520 }
John Spurlock486b78e2014-07-07 08:37:56 -0400521 }
John Spurlock486b78e2014-07-07 08:37:56 -0400522 }
523 }
524
525 private DeviceInfo updateInfo(BluetoothDevice device) {
526 DeviceInfo info = mDeviceInfo.get(device);
527 info = info != null ? info : new DeviceInfo();
528 mDeviceInfo.put(device, info);
529 return info;
530 }
531
Jason Monk4ae97d32014-12-17 10:14:33 -0500532 private class H extends Handler {
533 public H(Looper l) {
534 super(l);
535 }
536
537 public void handleMessage(Message msg) {
538 switch (msg.what) {
539 case MSG_UPDATE_CONNECTION_STATES:
540 handleUpdateConnectionStates();
541 firePairedDevicesChanged();
542 break;
543 case MSG_UPDATE_SINGLE_CONNECTION_STATE:
544 handleUpdateConnectionState((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
545 handleConnectionChange();
546 firePairedDevicesChanged();
547 break;
548 case MSG_UPDATE_BONDED_DEVICES:
549 handleUpdateBondedDevices();
550 firePairedDevicesChanged();
551 break;
552 case MSG_ADD_PROFILE:
553 mProfiles.put(msg.arg1, (BluetoothProfile) msg.obj);
554 handleUpdateConnectionStates();
555 firePairedDevicesChanged();
556 break;
557 case MSG_REM_PROFILE:
558 mProfiles.remove(msg.arg1);
559 handleUpdateConnectionStates();
560 firePairedDevicesChanged();
561 break;
562 }
563 };
564 };
565
John Spurlock486b78e2014-07-07 08:37:56 -0400566 private static class DeviceInfo {
Jason Monk4ae97d32014-12-17 10:14:33 -0500567 int connectionStateIndex = 0;
John Spurlock486b78e2014-07-07 08:37:56 -0400568 boolean bonded; // per getBondedDevices
Jason Monk4630c892014-12-08 16:36:16 -0500569 SparseArray<Boolean> connectedProfiles = new SparseArray<>();
Jason Monk4ae97d32014-12-17 10:14:33 -0500570 SparseArray<Integer> profileStates = new SparseArray<>();
John Spurlock486b78e2014-07-07 08:37:56 -0400571 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400572}