am 35850627: Merge "BluetoothControllerImpl: track bluetooth better" into lmp-mr1-dev

* commit '3585062760990b8ec3148b266155ae9ec033934d':
  BluetoothControllerImpl: track bluetooth better
This commit is contained in:
Dan Sandler
2014-12-10 15:53:59 +00:00
committed by Android Git Automerger
2 changed files with 156 additions and 45 deletions

View File

@@ -19,15 +19,21 @@ package com.android.systemui.statusbar.policy;
import static android.bluetooth.BluetoothAdapter.ERROR; import static android.bluetooth.BluetoothAdapter.ERROR;
import static com.android.systemui.statusbar.policy.BluetoothUtil.connectionStateToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.connectionStateToString;
import static com.android.systemui.statusbar.policy.BluetoothUtil.deviceToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.deviceToString;
import static com.android.systemui.statusbar.policy.BluetoothUtil.profileStateToString;
import static com.android.systemui.statusbar.policy.BluetoothUtil.profileToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.profileToString;
import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToProfile; import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToProfile;
import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToString;
import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener; import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
@@ -38,24 +44,37 @@ import android.os.ParcelUuid;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import android.util.SparseBooleanArray; import android.util.SparseArray;
import com.android.systemui.statusbar.policy.BluetoothUtil.Profile; import com.android.systemui.statusbar.policy.BluetoothUtil.Profile;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Set; import java.util.Set;
public class BluetoothControllerImpl implements BluetoothController { public class BluetoothControllerImpl implements BluetoothController {
private static final String TAG = "BluetoothController"; private static final String TAG = "BluetoothController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// This controls the order in which we check the states. Since a device can only have
// one state on screen, but can have multiple profiles, the later states override the
// value of earlier states. So if a device has a profile in CONNECTING and one in
// CONNECTED, it will show as CONNECTED, theoretically this shouldn't really happen often,
// but seemed worth noting.
private static final int[] CONNECTION_STATES = {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_CONNECTED,
};
private final Context mContext; private final Context mContext;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final BluetoothAdapter mAdapter; private final BluetoothAdapter mAdapter;
private final Receiver mReceiver = new Receiver(); private final Receiver mReceiver = new Receiver();
private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>(); private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>();
private final SparseArray<BluetoothProfile> mProfiles = new SparseArray<>();
private boolean mEnabled; private boolean mEnabled;
private boolean mConnecting; private boolean mConnecting;
@@ -73,7 +92,8 @@ public class BluetoothControllerImpl implements BluetoothController {
mReceiver.register(); mReceiver.register();
setAdapterState(mAdapter.getState()); setAdapterState(mAdapter.getState());
updateBondedBluetoothDevices(); updateBluetoothDevices();
bindAllProfiles();
} }
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -83,6 +103,7 @@ public class BluetoothControllerImpl implements BluetoothController {
pw.print(" mConnecting="); pw.println(mConnecting); pw.print(" mConnecting="); pw.println(mConnecting);
pw.print(" mLastDevice="); pw.println(mLastDevice); pw.print(" mLastDevice="); pw.println(mLastDevice);
pw.print(" mCallbacks.size="); pw.println(mCallbacks.size()); pw.print(" mCallbacks.size="); pw.println(mCallbacks.size());
pw.print(" mProfiles="); pw.println(profilesToString(mProfiles));
pw.print(" mDeviceInfo.size="); pw.println(mDeviceInfo.size()); pw.print(" mDeviceInfo.size="); pw.println(mDeviceInfo.size());
for (int i = 0; i < mDeviceInfo.size(); i++) { for (int i = 0; i < mDeviceInfo.size(); i++) {
final BluetoothDevice device = mDeviceInfo.keyAt(i); final BluetoothDevice device = mDeviceInfo.keyAt(i);
@@ -95,7 +116,22 @@ public class BluetoothControllerImpl implements BluetoothController {
private static String infoToString(DeviceInfo info) { private static String infoToString(DeviceInfo info) {
return info == null ? null : ("connectionState=" + return info == null ? null : ("connectionState=" +
connectionStateToString(info.connectionState) + ",bonded=" + info.bonded); connectionStateToString(info.connectionState) + ",bonded=" + info.bonded
+ ",profiles=" + profilesToString(info.connectedProfiles));
}
private static String profilesToString(SparseArray<?> profiles) {
final int N = profiles.size();
final StringBuffer buffer = new StringBuffer();
buffer.append('[');
for (int i = 0; i < N; i++) {
if (i != 0) {
buffer.append(',');
}
buffer.append(BluetoothUtil.profileToString(profiles.keyAt(i)));
}
buffer.append(']');
return buffer.toString();
} }
public void addStateChangedCallback(Callback cb) { public void addStateChangedCallback(Callback cb) {
@@ -178,6 +214,7 @@ public class BluetoothControllerImpl implements BluetoothController {
private void connect(PairedDevice pd, final boolean connect) { private void connect(PairedDevice pd, final boolean connect) {
if (mAdapter == null || pd == null || pd.tag == null) return; if (mAdapter == null || pd == null || pd.tag == null) return;
final BluetoothDevice device = (BluetoothDevice) pd.tag; final BluetoothDevice device = (BluetoothDevice) pd.tag;
final DeviceInfo info = mDeviceInfo.get(device);
final String action = connect ? "connect" : "disconnect"; final String action = connect ? "connect" : "disconnect";
if (DEBUG) Log.d(TAG, action + " " + deviceToString(device)); if (DEBUG) Log.d(TAG, action + " " + deviceToString(device));
final ParcelUuid[] uuids = device.getUuids(); final ParcelUuid[] uuids = device.getUuids();
@@ -185,43 +222,35 @@ public class BluetoothControllerImpl implements BluetoothController {
Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device)); Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device));
return; return;
} }
final SparseBooleanArray profiles = new SparseBooleanArray(); SparseArray<Boolean> profiles = new SparseArray<>();
for (ParcelUuid uuid : uuids) { if (connect) {
final int profile = uuidToProfile(uuid); // When connecting add every profile we can recognize by uuid.
if (profile == 0) { for (ParcelUuid uuid : uuids) {
Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: " final int profile = uuidToProfile(uuid);
+ uuidToString(uuid)); if (profile == 0) {
continue; Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: "
} + uuidToString(uuid));
final int profileState = mAdapter.getProfileConnectionState(profile); continue;
if (DEBUG && !profiles.get(profile)) Log.d(TAG, "Profile " + profileToString(profile) }
+ " state = " + profileStateToString(profileState)); final boolean connected = info.connectedProfiles.get(profile, false);
final boolean connected = profileState == BluetoothProfile.STATE_CONNECTED; if (!connected) {
if (connect != connected) { profiles.put(profile, true);
profiles.put(profile, true); }
} }
} else {
// When disconnecting, just add every profile we know they are connected to.
profiles = info.connectedProfiles;
} }
for (int i = 0; i < profiles.size(); i++) { for (int i = 0; i < profiles.size(); i++) {
final int profile = profiles.keyAt(i); final int profile = profiles.keyAt(i);
mAdapter.getProfileProxy(mContext, new ServiceListener() { if (mProfiles.indexOfKey(profile) >= 0) {
@Override final Profile p = BluetoothUtil.getProfile(mProfiles.get(profile));
public void onServiceConnected(int profile, BluetoothProfile proxy) { final boolean ok = connect ? p.connect(device) : p.disconnect(device);
if (DEBUG) Log.d(TAG, "onServiceConnected " + profileToString(profile)); if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " "
final Profile p = BluetoothUtil.getProfile(proxy); + (ok ? "succeeded" : "failed"));
if (p == null) { } else {
Log.w(TAG, "Unable get get Profile for " + profileToString(profile)); Log.w(TAG, "Unable get get Profile for " + profileToString(profile));
} else { }
final boolean ok = connect ? p.connect(device) : p.disconnect(device);
if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " "
+ (ok ? "succeeded" : "failed"));
}
}
@Override
public void onServiceDisconnected(int profile) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected " + profileToString(profile));
}
}, profile);
} }
} }
@@ -230,11 +259,13 @@ public class BluetoothControllerImpl implements BluetoothController {
return mLastDevice != null ? mLastDevice.getAliasName() : null; return mLastDevice != null ? mLastDevice.getAliasName() : null;
} }
private void updateBondedBluetoothDevices() { private void updateBluetoothDevices() {
if (mAdapter == null) return; if (mAdapter == null) return;
final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
for (DeviceInfo info : mDeviceInfo.values()) { for (DeviceInfo info : mDeviceInfo.values()) {
info.bonded = false; info.bonded = false;
info.connectionState = ERROR;
info.connectedProfiles.clear();
} }
int bondedCount = 0; int bondedCount = 0;
BluetoothDevice lastBonded = null; BluetoothDevice lastBonded = null;
@@ -248,12 +279,62 @@ public class BluetoothControllerImpl implements BluetoothController {
} }
} }
} }
final int N = mProfiles.size();
final int[] connectionType = new int[1];
for (int i = 0; i < CONNECTION_STATES.length; i++) {
connectionType[0] = CONNECTION_STATES[i];
for (int j = 0; j < N; j++) {
int profile = mProfiles.keyAt(j);
List<BluetoothDevice> devices = mProfiles.get(profile)
.getDevicesMatchingConnectionStates(connectionType);
for (int k = 0; k < devices.size(); k++) {
DeviceInfo info = mDeviceInfo.get(devices.get(k));
info.connectionState = CONNECTION_STATES[i];
if (CONNECTION_STATES[i] == BluetoothProfile.STATE_CONNECTED) {
info.connectedProfiles.put(profile, true);
}
}
}
}
if (mLastDevice == null && bondedCount == 1) { if (mLastDevice == null && bondedCount == 1) {
mLastDevice = lastBonded; mLastDevice = lastBonded;
} }
// If we are no longer connected to the current device, see if we are connected to
// something else, so we don't display a name we aren't connected to.
if (mLastDevice != null &&
mDeviceInfo.get(mLastDevice).connectionState != BluetoothProfile.STATE_CONNECTED) {
// Make sure we don't keep this device while it isn't connected.
mLastDevice = null;
// Look for anything else connected.
final int size = mDeviceInfo.size();
for (int i = 0; i < size; i++) {
BluetoothDevice device = mDeviceInfo.keyAt(i);
DeviceInfo info = mDeviceInfo.valueAt(i);
if (info.connectionState == BluetoothProfile.STATE_CONNECTED) {
mLastDevice = device;
break;
}
}
}
firePairedDevicesChanged(); firePairedDevicesChanged();
} }
private void bindAllProfiles() {
// Note: This needs to contain all of the types that can be returned by BluetoothUtil
// otherwise we can't find the profiles we need when we connect/disconnect.
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP_SINK);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.AVRCP_CONTROLLER);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET_CLIENT);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.INPUT_DEVICE);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.MAP);
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.PAN);
// Note Health is not in this list because health devices aren't 'connected'.
// If profiles are expanded to use more than just connection state and connect/disconnect
// then it should be added.
}
private void firePairedDevicesChanged() { private void firePairedDevicesChanged() {
for (Callback cb : mCallbacks) { for (Callback cb : mCallbacks) {
cb.onBluetoothPairedDevicesChanged(); cb.onBluetoothPairedDevicesChanged();
@@ -283,6 +364,20 @@ public class BluetoothControllerImpl implements BluetoothController {
cb.onBluetoothStateChange(mEnabled, mConnecting); cb.onBluetoothStateChange(mEnabled, mConnecting);
} }
private final ServiceListener mProfileListener = new ServiceListener() {
@Override
public void onServiceDisconnected(int profile) {
mProfiles.remove(profile);
updateBluetoothDevices();
}
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
mProfiles.put(profile, proxy);
updateBluetoothDevices();
}
};
private final class Receiver extends BroadcastReceiver { private final class Receiver extends BroadcastReceiver {
public void register() { public void register() {
final IntentFilter filter = new IntentFilter(); final IntentFilter filter = new IntentFilter();
@@ -290,6 +385,15 @@ public class BluetoothControllerImpl implements BluetoothController {
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED); filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED);
filter.addAction(BluetoothDevice.ACTION_UUID);
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
mContext.registerReceiver(this, filter); mContext.registerReceiver(this, filter);
} }
@@ -301,16 +405,13 @@ public class BluetoothControllerImpl implements BluetoothController {
setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR)); setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR));
if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled); if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled);
} else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
final DeviceInfo info = updateInfo(device); updateInfo(device);
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
ERROR); ERROR);
if (state != ERROR) {
info.connectionState = state;
}
mLastDevice = device; mLastDevice = device;
if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED " if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED "
+ connectionStateToString(state) + " " + deviceToString(device)); + connectionStateToString(state) + " " + deviceToString(device));
setConnecting(info.connectionState == BluetoothAdapter.STATE_CONNECTING); setConnecting(state == BluetoothAdapter.STATE_CONNECTING);
} else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) { } else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
updateInfo(device); updateInfo(device);
mLastDevice = device; mLastDevice = device;
@@ -318,7 +419,8 @@ public class BluetoothControllerImpl implements BluetoothController {
if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device); if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device);
// we'll update all bonded devices below // we'll update all bonded devices below
} }
updateBondedBluetoothDevices(); // Always update bluetooth devices state.
updateBluetoothDevices();
} }
} }
@@ -332,5 +434,6 @@ public class BluetoothControllerImpl implements BluetoothController {
private static class DeviceInfo { private static class DeviceInfo {
int connectionState = BluetoothAdapter.STATE_DISCONNECTED; int connectionState = BluetoothAdapter.STATE_DISCONNECTED;
boolean bonded; // per getBondedDevices boolean bonded; // per getBondedDevices
SparseArray<Boolean> connectedProfiles = new SparseArray<>();
} }
} }

View File

@@ -36,7 +36,10 @@ public class BluetoothUtil {
if (profile == BluetoothProfile.HEADSET) return "HEADSET"; if (profile == BluetoothProfile.HEADSET) return "HEADSET";
if (profile == BluetoothProfile.A2DP) return "A2DP"; if (profile == BluetoothProfile.A2DP) return "A2DP";
if (profile == BluetoothProfile.AVRCP_CONTROLLER) return "AVRCP_CONTROLLER"; if (profile == BluetoothProfile.AVRCP_CONTROLLER) return "AVRCP_CONTROLLER";
return "UNKNOWN"; if (profile == BluetoothProfile.PAN) return "PAN";
if (profile == BluetoothProfile.INPUT_DEVICE) return "INPUT_DEVICE";
if (profile == BluetoothProfile.MAP) return "MAP";
return "UNKNOWN(" + profile + ")";
} }
public static String profileStateToString(int state) { public static String profileStateToString(int state) {
@@ -106,6 +109,11 @@ public class BluetoothUtil {
if (BluetoothUuid.AvrcpController.equals(uuid)) return BluetoothProfile.AVRCP_CONTROLLER; if (BluetoothUuid.AvrcpController.equals(uuid)) return BluetoothProfile.AVRCP_CONTROLLER;
if (BluetoothUuid.Hid.equals(uuid)) return BluetoothProfile.INPUT_DEVICE;
if (BluetoothUuid.Hogp.equals(uuid)) return BluetoothProfile.INPUT_DEVICE;
if (BluetoothUuid.NAP.equals(uuid)) return BluetoothProfile.PAN;
return 0; return 0;
} }