blob: 076cfe20c00c0aa29c726b56c020c6e1ba096949 [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;
John Spurlock486b78e2014-07-07 08:37:56 -040043import android.os.ParcelUuid;
44import android.util.ArrayMap;
45import android.util.ArraySet;
46import android.util.Log;
Jason Monk4630c892014-12-08 16:36:16 -050047import android.util.SparseArray;
John Spurlockaf8d6c42014-05-07 17:49:08 -040048
John Spurlock486b78e2014-07-07 08:37:56 -040049import com.android.systemui.statusbar.policy.BluetoothUtil.Profile;
50
51import java.io.FileDescriptor;
52import java.io.PrintWriter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040053import java.util.ArrayList;
Jason Monk4630c892014-12-08 16:36:16 -050054import java.util.List;
John Spurlockaf8d6c42014-05-07 17:49:08 -040055import java.util.Set;
56
John Spurlock486b78e2014-07-07 08:37:56 -040057public class BluetoothControllerImpl implements BluetoothController {
58 private static final String TAG = "BluetoothController";
59 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jason Monk4630c892014-12-08 16:36:16 -050060 // This controls the order in which we check the states. Since a device can only have
61 // one state on screen, but can have multiple profiles, the later states override the
62 // value of earlier states. So if a device has a profile in CONNECTING and one in
63 // CONNECTED, it will show as CONNECTED, theoretically this shouldn't really happen often,
64 // but seemed worth noting.
65 private static final int[] CONNECTION_STATES = {
66 BluetoothProfile.STATE_DISCONNECTED,
67 BluetoothProfile.STATE_DISCONNECTING,
68 BluetoothProfile.STATE_CONNECTING,
69 BluetoothProfile.STATE_CONNECTED,
70 };
John Spurlockaf8d6c42014-05-07 17:49:08 -040071
John Spurlock486b78e2014-07-07 08:37:56 -040072 private final Context mContext;
John Spurlockd1c86e22014-06-01 00:04:53 -040073 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
John Spurlockaf8d6c42014-05-07 17:49:08 -040074 private final BluetoothAdapter mAdapter;
John Spurlock486b78e2014-07-07 08:37:56 -040075 private final Receiver mReceiver = new Receiver();
76 private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>();
Jason Monk4630c892014-12-08 16:36:16 -050077 private final SparseArray<BluetoothProfile> mProfiles = new SparseArray<>();
John Spurlockaf8d6c42014-05-07 17:49:08 -040078
John Spurlockd1c86e22014-06-01 00:04:53 -040079 private boolean mEnabled;
80 private boolean mConnecting;
81 private BluetoothDevice mLastDevice;
John Spurlockaf8d6c42014-05-07 17:49:08 -040082
83 public BluetoothControllerImpl(Context context) {
John Spurlock486b78e2014-07-07 08:37:56 -040084 mContext = context;
85 final BluetoothManager bluetoothManager =
86 (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
87 mAdapter = bluetoothManager.getAdapter();
88 if (mAdapter == null) {
89 Log.w(TAG, "Default BT adapter not found");
90 return;
John Spurlockaf8d6c42014-05-07 17:49:08 -040091 }
John Spurlock486b78e2014-07-07 08:37:56 -040092
93 mReceiver.register();
94 setAdapterState(mAdapter.getState());
Jason Monk4630c892014-12-08 16:36:16 -050095 updateBluetoothDevices();
96 bindAllProfiles();
John Spurlockaf8d6c42014-05-07 17:49:08 -040097 }
98
John Spurlock486b78e2014-07-07 08:37:56 -040099 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
100 pw.println("BluetoothController state:");
101 pw.print(" mAdapter="); pw.println(mAdapter);
102 pw.print(" mEnabled="); pw.println(mEnabled);
103 pw.print(" mConnecting="); pw.println(mConnecting);
104 pw.print(" mLastDevice="); pw.println(mLastDevice);
105 pw.print(" mCallbacks.size="); pw.println(mCallbacks.size());
Jason Monk4630c892014-12-08 16:36:16 -0500106 pw.print(" mProfiles="); pw.println(profilesToString(mProfiles));
John Spurlock486b78e2014-07-07 08:37:56 -0400107 pw.print(" mDeviceInfo.size="); pw.println(mDeviceInfo.size());
108 for (int i = 0; i < mDeviceInfo.size(); i++) {
109 final BluetoothDevice device = mDeviceInfo.keyAt(i);
110 final DeviceInfo info = mDeviceInfo.valueAt(i);
111 pw.print(" "); pw.print(deviceToString(device));
112 pw.print('('); pw.print(uuidsToString(device)); pw.print(')');
113 pw.print(" "); pw.println(infoToString(info));
114 }
115 }
116
117 private static String infoToString(DeviceInfo info) {
118 return info == null ? null : ("connectionState=" +
Jason Monk4630c892014-12-08 16:36:16 -0500119 connectionStateToString(info.connectionState) + ",bonded=" + info.bonded
120 + ",profiles=" + profilesToString(info.connectedProfiles));
121 }
122
123 private static String profilesToString(SparseArray<?> profiles) {
124 final int N = profiles.size();
125 final StringBuffer buffer = new StringBuffer();
126 buffer.append('[');
127 for (int i = 0; i < N; i++) {
128 if (i != 0) {
129 buffer.append(',');
130 }
131 buffer.append(BluetoothUtil.profileToString(profiles.keyAt(i)));
132 }
133 buffer.append(']');
134 return buffer.toString();
John Spurlock486b78e2014-07-07 08:37:56 -0400135 }
136
John Spurlockd1c86e22014-06-01 00:04:53 -0400137 public void addStateChangedCallback(Callback cb) {
138 mCallbacks.add(cb);
John Spurlock486b78e2014-07-07 08:37:56 -0400139 fireStateChange(cb);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400140 }
141
142 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400143 public void removeStateChangedCallback(Callback cb) {
144 mCallbacks.remove(cb);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400145 }
146
147 @Override
148 public boolean isBluetoothEnabled() {
149 return mAdapter != null && mAdapter.isEnabled();
150 }
151
152 @Override
153 public boolean isBluetoothConnected() {
154 return mAdapter != null
155 && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
156 }
157
158 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400159 public boolean isBluetoothConnecting() {
160 return mAdapter != null
161 && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTING;
162 }
163
164 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400165 public void setBluetoothEnabled(boolean enabled) {
166 if (mAdapter != null) {
167 if (enabled) {
168 mAdapter.enable();
169 } else {
170 mAdapter.disable();
171 }
172 }
173 }
174
175 @Override
176 public boolean isBluetoothSupported() {
177 return mAdapter != null;
178 }
179
John Spurlock486b78e2014-07-07 08:37:56 -0400180 @Override
181 public ArraySet<PairedDevice> getPairedDevices() {
182 final ArraySet<PairedDevice> rt = new ArraySet<>();
183 for (int i = 0; i < mDeviceInfo.size(); i++) {
184 final BluetoothDevice device = mDeviceInfo.keyAt(i);
185 final DeviceInfo info = mDeviceInfo.valueAt(i);
186 if (!info.bonded) continue;
187 final PairedDevice paired = new PairedDevice();
188 paired.id = device.getAddress();
189 paired.tag = device;
190 paired.name = device.getAliasName();
191 paired.state = connectionStateToPairedDeviceState(info.connectionState);
192 rt.add(paired);
193 }
194 return rt;
195 }
196
197 private static int connectionStateToPairedDeviceState(int state) {
198 if (state == BluetoothAdapter.STATE_CONNECTED) return PairedDevice.STATE_CONNECTED;
199 if (state == BluetoothAdapter.STATE_CONNECTING) return PairedDevice.STATE_CONNECTING;
200 if (state == BluetoothAdapter.STATE_DISCONNECTING) return PairedDevice.STATE_DISCONNECTING;
201 return PairedDevice.STATE_DISCONNECTED;
202 }
203
204 @Override
205 public void connect(final PairedDevice pd) {
206 connect(pd, true);
207 }
208
209 @Override
210 public void disconnect(PairedDevice pd) {
211 connect(pd, false);
212 }
213
214 private void connect(PairedDevice pd, final boolean connect) {
215 if (mAdapter == null || pd == null || pd.tag == null) return;
216 final BluetoothDevice device = (BluetoothDevice) pd.tag;
Jason Monk4630c892014-12-08 16:36:16 -0500217 final DeviceInfo info = mDeviceInfo.get(device);
John Spurlock486b78e2014-07-07 08:37:56 -0400218 final String action = connect ? "connect" : "disconnect";
219 if (DEBUG) Log.d(TAG, action + " " + deviceToString(device));
John Spurlock27bdff72014-07-16 21:14:50 -0400220 final ParcelUuid[] uuids = device.getUuids();
221 if (uuids == null) {
222 Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device));
223 return;
224 }
Jason Monk4630c892014-12-08 16:36:16 -0500225 SparseArray<Boolean> profiles = new SparseArray<>();
226 if (connect) {
227 // When connecting add every profile we can recognize by uuid.
228 for (ParcelUuid uuid : uuids) {
229 final int profile = uuidToProfile(uuid);
230 if (profile == 0) {
231 Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: "
232 + uuidToString(uuid));
233 continue;
234 }
235 final boolean connected = info.connectedProfiles.get(profile, false);
236 if (!connected) {
237 profiles.put(profile, true);
238 }
John Spurlock486b78e2014-07-07 08:37:56 -0400239 }
Jason Monk4630c892014-12-08 16:36:16 -0500240 } else {
241 // When disconnecting, just add every profile we know they are connected to.
242 profiles = info.connectedProfiles;
John Spurlock486b78e2014-07-07 08:37:56 -0400243 }
244 for (int i = 0; i < profiles.size(); i++) {
245 final int profile = profiles.keyAt(i);
Jason Monk4630c892014-12-08 16:36:16 -0500246 if (mProfiles.indexOfKey(profile) >= 0) {
247 final Profile p = BluetoothUtil.getProfile(mProfiles.get(profile));
248 final boolean ok = connect ? p.connect(device) : p.disconnect(device);
249 if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " "
250 + (ok ? "succeeded" : "failed"));
251 } else {
252 Log.w(TAG, "Unable get get Profile for " + profileToString(profile));
253 }
John Spurlock486b78e2014-07-07 08:37:56 -0400254 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400255 }
256
257 @Override
John Spurlockd1c86e22014-06-01 00:04:53 -0400258 public String getLastDeviceName() {
John Spurlock486b78e2014-07-07 08:37:56 -0400259 return mLastDevice != null ? mLastDevice.getAliasName() : null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400260 }
261
Jason Monk4630c892014-12-08 16:36:16 -0500262 private void updateBluetoothDevices() {
John Spurlock486b78e2014-07-07 08:37:56 -0400263 if (mAdapter == null) return;
264 final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
265 for (DeviceInfo info : mDeviceInfo.values()) {
266 info.bonded = false;
Jason Monk4630c892014-12-08 16:36:16 -0500267 info.connectionState = ERROR;
268 info.connectedProfiles.clear();
John Spurlock486b78e2014-07-07 08:37:56 -0400269 }
270 int bondedCount = 0;
271 BluetoothDevice lastBonded = null;
272 if (bondedDevices != null) {
273 for (BluetoothDevice bondedDevice : bondedDevices) {
274 final boolean bonded = bondedDevice.getBondState() != BluetoothDevice.BOND_NONE;
275 updateInfo(bondedDevice).bonded = bonded;
276 if (bonded) {
277 bondedCount++;
278 lastBonded = bondedDevice;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400279 }
280 }
281 }
Jason Monk4630c892014-12-08 16:36:16 -0500282 final int N = mProfiles.size();
283 final int[] connectionType = new int[1];
284 for (int i = 0; i < CONNECTION_STATES.length; i++) {
285 connectionType[0] = CONNECTION_STATES[i];
286 for (int j = 0; j < N; j++) {
287 int profile = mProfiles.keyAt(j);
288 List<BluetoothDevice> devices = mProfiles.get(profile)
289 .getDevicesMatchingConnectionStates(connectionType);
290 for (int k = 0; k < devices.size(); k++) {
291 DeviceInfo info = mDeviceInfo.get(devices.get(k));
292 info.connectionState = CONNECTION_STATES[i];
293 if (CONNECTION_STATES[i] == BluetoothProfile.STATE_CONNECTED) {
294 info.connectedProfiles.put(profile, true);
295 }
296 }
297 }
298 }
John Spurlock486b78e2014-07-07 08:37:56 -0400299 if (mLastDevice == null && bondedCount == 1) {
300 mLastDevice = lastBonded;
301 }
Jason Monk4630c892014-12-08 16:36:16 -0500302 // If we are no longer connected to the current device, see if we are connected to
303 // something else, so we don't display a name we aren't connected to.
304 if (mLastDevice != null &&
305 mDeviceInfo.get(mLastDevice).connectionState != BluetoothProfile.STATE_CONNECTED) {
306 // Make sure we don't keep this device while it isn't connected.
307 mLastDevice = null;
308 // Look for anything else connected.
309 final int size = mDeviceInfo.size();
310 for (int i = 0; i < size; i++) {
311 BluetoothDevice device = mDeviceInfo.keyAt(i);
312 DeviceInfo info = mDeviceInfo.valueAt(i);
313 if (info.connectionState == BluetoothProfile.STATE_CONNECTED) {
314 mLastDevice = device;
315 break;
316 }
317 }
318 }
John Spurlock486b78e2014-07-07 08:37:56 -0400319 firePairedDevicesChanged();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400320 }
321
Jason Monk4630c892014-12-08 16:36:16 -0500322 private void bindAllProfiles() {
323 // Note: This needs to contain all of the types that can be returned by BluetoothUtil
324 // otherwise we can't find the profiles we need when we connect/disconnect.
325 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
326 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP_SINK);
327 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.AVRCP_CONTROLLER);
328 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
329 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET_CLIENT);
330 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.INPUT_DEVICE);
331 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.MAP);
332 mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.PAN);
333 // Note Health is not in this list because health devices aren't 'connected'.
334 // If profiles are expanded to use more than just connection state and connect/disconnect
335 // then it should be added.
336 }
337
John Spurlock486b78e2014-07-07 08:37:56 -0400338 private void firePairedDevicesChanged() {
John Spurlockd1c86e22014-06-01 00:04:53 -0400339 for (Callback cb : mCallbacks) {
John Spurlock486b78e2014-07-07 08:37:56 -0400340 cb.onBluetoothPairedDevicesChanged();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400341 }
342 }
John Spurlockccb6b9a2014-05-17 15:54:40 -0400343
John Spurlock486b78e2014-07-07 08:37:56 -0400344 private void setAdapterState(int adapterState) {
345 final boolean enabled = adapterState == BluetoothAdapter.STATE_ON;
346 if (mEnabled == enabled) return;
347 mEnabled = enabled;
348 fireStateChange();
349 }
350
351 private void setConnecting(boolean connecting) {
352 if (mConnecting == connecting) return;
353 mConnecting = connecting;
354 fireStateChange();
355 }
356
357 private void fireStateChange() {
358 for (Callback cb : mCallbacks) {
359 fireStateChange(cb);
360 }
361 }
362
363 private void fireStateChange(Callback cb) {
John Spurlockd1c86e22014-06-01 00:04:53 -0400364 cb.onBluetoothStateChange(mEnabled, mConnecting);
John Spurlockccb6b9a2014-05-17 15:54:40 -0400365 }
John Spurlock486b78e2014-07-07 08:37:56 -0400366
Jason Monk4630c892014-12-08 16:36:16 -0500367 private final ServiceListener mProfileListener = new ServiceListener() {
368 @Override
369 public void onServiceDisconnected(int profile) {
370 mProfiles.remove(profile);
371 updateBluetoothDevices();
372 }
373
374 @Override
375 public void onServiceConnected(int profile, BluetoothProfile proxy) {
376 mProfiles.put(profile, proxy);
377 updateBluetoothDevices();
378 }
379 };
380
John Spurlock486b78e2014-07-07 08:37:56 -0400381 private final class Receiver extends BroadcastReceiver {
382 public void register() {
383 final IntentFilter filter = new IntentFilter();
384 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
385 filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
386 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
387 filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
Jason Monk4630c892014-12-08 16:36:16 -0500388 filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED);
389 filter.addAction(BluetoothDevice.ACTION_UUID);
390 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
391 filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
392 filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
393 filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
394 filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
395 filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
396 filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
John Spurlock486b78e2014-07-07 08:37:56 -0400397 mContext.registerReceiver(this, filter);
398 }
399
400 @Override
401 public void onReceive(Context context, Intent intent) {
402 final String action = intent.getAction();
403 final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
404 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
405 setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR));
406 if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled);
407 } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
Jason Monk4630c892014-12-08 16:36:16 -0500408 updateInfo(device);
John Spurlock486b78e2014-07-07 08:37:56 -0400409 final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
410 ERROR);
John Spurlock486b78e2014-07-07 08:37:56 -0400411 mLastDevice = device;
412 if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED "
413 + connectionStateToString(state) + " " + deviceToString(device));
Jason Monk4630c892014-12-08 16:36:16 -0500414 setConnecting(state == BluetoothAdapter.STATE_CONNECTING);
John Spurlock486b78e2014-07-07 08:37:56 -0400415 } else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
416 updateInfo(device);
417 mLastDevice = device;
418 } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
419 if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device);
420 // we'll update all bonded devices below
421 }
Jason Monk4630c892014-12-08 16:36:16 -0500422 // Always update bluetooth devices state.
423 updateBluetoothDevices();
John Spurlock486b78e2014-07-07 08:37:56 -0400424 }
425 }
426
427 private DeviceInfo updateInfo(BluetoothDevice device) {
428 DeviceInfo info = mDeviceInfo.get(device);
429 info = info != null ? info : new DeviceInfo();
430 mDeviceInfo.put(device, info);
431 return info;
432 }
433
434 private static class DeviceInfo {
435 int connectionState = BluetoothAdapter.STATE_DISCONNECTED;
436 boolean bonded; // per getBondedDevices
Jason Monk4630c892014-12-08 16:36:16 -0500437 SparseArray<Boolean> connectedProfiles = new SparseArray<>();
John Spurlock486b78e2014-07-07 08:37:56 -0400438 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400439}