• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.statusbar.policy;
18 
19 import static android.bluetooth.BluetoothAdapter.ERROR;
20 import static com.android.systemui.statusbar.policy.BluetoothUtil.connectionStateToString;
21 import static com.android.systemui.statusbar.policy.BluetoothUtil.deviceToString;
22 import static com.android.systemui.statusbar.policy.BluetoothUtil.profileToString;
23 import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToProfile;
24 import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToString;
25 import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString;
26 
27 import android.bluetooth.BluetoothA2dp;
28 import android.bluetooth.BluetoothA2dpSink;
29 import android.bluetooth.BluetoothAdapter;
30 import android.bluetooth.BluetoothDevice;
31 import android.bluetooth.BluetoothHeadset;
32 import android.bluetooth.BluetoothHeadsetClient;
33 import android.bluetooth.BluetoothInputDevice;
34 import android.bluetooth.BluetoothManager;
35 import android.bluetooth.BluetoothMap;
36 import android.bluetooth.BluetoothPan;
37 import android.bluetooth.BluetoothProfile;
38 import android.bluetooth.BluetoothProfile.ServiceListener;
39 import android.content.BroadcastReceiver;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.os.Handler;
44 import android.os.Looper;
45 import android.os.Message;
46 import android.os.ParcelUuid;
47 import android.util.ArrayMap;
48 import android.util.ArraySet;
49 import android.util.Log;
50 import android.util.SparseArray;
51 
52 import com.android.systemui.statusbar.policy.BluetoothUtil.Profile;
53 
54 import java.io.FileDescriptor;
55 import java.io.PrintWriter;
56 import java.util.ArrayList;
57 import java.util.Set;
58 
59 public class BluetoothControllerImpl implements BluetoothController {
60     private static final String TAG = "BluetoothController";
61     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
62     // 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     };
73     // 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;
82 
83     private final Context mContext;
84     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
85     private final BluetoothAdapter mAdapter;
86     private final Receiver mReceiver = new Receiver();
87     private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>();
88     private final SparseArray<BluetoothProfile> mProfiles = new SparseArray<>();
89 
90     private final H mHandler;
91 
92     private boolean mEnabled;
93     private boolean mConnecting;
94     private BluetoothDevice mLastDevice;
95 
BluetoothControllerImpl(Context context, Looper bgLooper)96     public BluetoothControllerImpl(Context context, Looper bgLooper) {
97         mContext = context;
98         mHandler = new H(bgLooper);
99 
100         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;
106         }
107 
108         mReceiver.register();
109         setAdapterState(mAdapter.getState());
110         updateBondedDevices();
111         bindAllProfiles();
112     }
113 
dump(FileDescriptor fd, PrintWriter pw, String[] args)114     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());
121         pw.print("  mProfiles="); pw.println(profilesToString(mProfiles));
122         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 
infoToString(DeviceInfo info)132     private static String infoToString(DeviceInfo info) {
133         return info == null ? null : ("connectionState=" +
134                 connectionStateToString(CONNECTION_STATES[info.connectionStateIndex])
135                 + ",bonded=" + info.bonded + ",profiles="
136                 + profilesToString(info.connectedProfiles));
137     }
138 
profilesToString(SparseArray<?> profiles)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();
151     }
152 
addStateChangedCallback(Callback cb)153     public void addStateChangedCallback(Callback cb) {
154         mCallbacks.add(cb);
155         fireStateChange(cb);
156     }
157 
158     @Override
removeStateChangedCallback(Callback cb)159     public void removeStateChangedCallback(Callback cb) {
160         mCallbacks.remove(cb);
161     }
162 
163     @Override
isBluetoothEnabled()164     public boolean isBluetoothEnabled() {
165         return mAdapter != null && mAdapter.isEnabled();
166     }
167 
168     @Override
isBluetoothConnected()169     public boolean isBluetoothConnected() {
170         return mAdapter != null
171                 && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
172     }
173 
174     @Override
isBluetoothConnecting()175     public boolean isBluetoothConnecting() {
176         return mAdapter != null
177                 && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTING;
178     }
179 
180     @Override
setBluetoothEnabled(boolean enabled)181     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
isBluetoothSupported()192     public boolean isBluetoothSupported() {
193         return mAdapter != null;
194     }
195 
196     @Override
getPairedDevices()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();
207             paired.state = connectionStateToPairedDeviceState(info.connectionStateIndex);
208             rt.add(paired);
209         }
210         return rt;
211     }
212 
connectionStateToPairedDeviceState(int index)213     private static int connectionStateToPairedDeviceState(int index) {
214         int state = CONNECTION_STATES[index];
215         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
connect(final PairedDevice pd)222     public void connect(final PairedDevice pd) {
223         connect(pd, true);
224     }
225 
226     @Override
disconnect(PairedDevice pd)227     public void disconnect(PairedDevice pd) {
228         connect(pd, false);
229     }
230 
connect(PairedDevice pd, final boolean connect)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;
234         final DeviceInfo info = mDeviceInfo.get(device);
235         final String action = connect ? "connect" : "disconnect";
236         if (DEBUG) Log.d(TAG, action + " " + deviceToString(device));
237         final ParcelUuid[] uuids = device.getUuids();
238         if (uuids == null) {
239             Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device));
240             return;
241         }
242         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                 }
256             }
257         } else {
258             // When disconnecting, just add every profile we know they are connected to.
259             profiles = info.connectedProfiles;
260         }
261         for (int i = 0; i < profiles.size(); i++) {
262             final int profile = profiles.keyAt(i);
263             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             }
271         }
272     }
273 
274     @Override
getLastDeviceName()275     public String getLastDeviceName() {
276         return mLastDevice != null ? mLastDevice.getAliasName() : null;
277     }
278 
updateBondedDevices()279     private void updateBondedDevices() {
280         mHandler.removeMessages(MSG_UPDATE_BONDED_DEVICES);
281         mHandler.sendEmptyMessage(MSG_UPDATE_BONDED_DEVICES);
282     }
283 
updateConnectionStates()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 
updateConnectionState(BluetoothDevice device, int profile, int state)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 
handleUpdateBondedDevices()299     private void handleUpdateBondedDevices() {
300         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;
314                 }
315             }
316         }
317         if (mLastDevice == null && bondedCount == 1) {
318             mLastDevice = lastBonded;
319         }
320         updateConnectionStates();
321         firePairedDevicesChanged();
322     }
323 
handleUpdateConnectionStates()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 
handleUpdateConnectionState(BluetoothDevice device, int profile, int state)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 
handleConnectionChange()368     private void handleConnectionChange() {
369         // 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 &&
372                 CONNECTION_STATES[mDeviceInfo.get(mLastDevice).connectionStateIndex]
373                         != BluetoothProfile.STATE_CONNECTED) {
374             // 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);
381                 if (CONNECTION_STATES[info.connectionStateIndex]
382                         == BluetoothProfile.STATE_CONNECTED) {
383                     mLastDevice = device;
384                     break;
385                 }
386             }
387         }
388     }
389 
bindAllProfiles()390     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 
firePairedDevicesChanged()406     private void firePairedDevicesChanged() {
407         for (Callback cb : mCallbacks) {
408             cb.onBluetoothPairedDevicesChanged();
409         }
410     }
411 
setAdapterState(int adapterState)412     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 
setConnecting(boolean connecting)419     private void setConnecting(boolean connecting) {
420         if (mConnecting == connecting) return;
421         mConnecting = connecting;
422         fireStateChange();
423     }
424 
fireStateChange()425     private void fireStateChange() {
426         for (Callback cb : mCallbacks) {
427             fireStateChange(cb);
428         }
429     }
430 
fireStateChange(Callback cb)431     private void fireStateChange(Callback cb) {
432         cb.onBluetoothStateChange(mEnabled, mConnecting);
433     }
434 
getProfileFromAction(String action)435     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 
455     private final ServiceListener mProfileListener = new ServiceListener() {
456         @Override
457         public void onServiceDisconnected(int profile) {
458             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();
463         }
464 
465         @Override
466         public void onServiceConnected(int profile, BluetoothProfile proxy) {
467             if (DEBUG) Log.d(TAG, "Connected to " + BluetoothUtil.profileToString(profile));
468             mHandler.obtainMessage(MSG_ADD_PROFILE, profile, 0, proxy).sendToTarget();
469         }
470     };
471 
472     private final class Receiver extends BroadcastReceiver {
register()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);
479             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);
486             mContext.registerReceiver(this, filter);
487         }
488 
489         @Override
onReceive(Context context, Intent intent)490         public void onReceive(Context context, Intent intent) {
491             final String action = intent.getAction();
492             final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
493 
494             if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
495                 setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR));
496                 updateBondedDevices();
497                 if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled);
498             } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
499                 updateInfo(device);
500                 final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
501                         ERROR);
502                 mLastDevice = device;
503                 if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED "
504                         + connectionStateToString(state) + " " + deviceToString(device));
505                 setConnecting(state == BluetoothAdapter.STATE_CONNECTING);
506             } 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);
511                 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                 }
521             }
522         }
523     }
524 
updateInfo(BluetoothDevice device)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 
532     private class H extends Handler {
H(Looper l)533         public H(Looper l) {
534             super(l);
535         }
536 
handleMessage(Message msg)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 
566     private static class DeviceInfo {
567         int connectionStateIndex = 0;
568         boolean bonded;  // per getBondedDevices
569         SparseArray<Boolean> connectedProfiles = new SparseArray<>();
570         SparseArray<Integer> profileStates = new SparseArray<>();
571     }
572 }
573