QS: Introduce bluetooth control panel.
- Factor out common detail item panel view, share with Wifi.
- Add an empty state (large icon + text)
- Implement connect / disconnect for supported BT profiles.
- Wire up "scanning" state, but still waiting on asset.
- Add BT controller info to dump.
Bug:16235253
Change-Id: Icf854cafba962fe4b63767d7206e309d80b7b87b
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 379b509..f021623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -16,49 +16,91 @@
package com.android.systemui.statusbar.policy;
+import static android.bluetooth.BluetoothAdapter.ERROR;
+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.profileStateToString;
+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.uuidToString;
+import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.ParcelUuid;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import com.android.systemui.statusbar.policy.BluetoothUtil.Profile;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Set;
-public class BluetoothControllerImpl extends BroadcastReceiver implements BluetoothController {
- private static final String TAG = "StatusBar.BluetoothController";
+public class BluetoothControllerImpl implements BluetoothController {
+ private static final String TAG = "BluetoothController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final Context mContext;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
- private final Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
private final BluetoothAdapter mAdapter;
+ private final Receiver mReceiver = new Receiver();
+ private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>();
private boolean mEnabled;
private boolean mConnecting;
private BluetoothDevice mLastDevice;
public BluetoothControllerImpl(Context context) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
- filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
- filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
- context.registerReceiver(this, filter);
-
- final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- handleAdapterStateChange(adapter.getState());
+ mContext = context;
+ final BluetoothManager bluetoothManager =
+ (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
+ mAdapter = bluetoothManager.getAdapter();
+ if (mAdapter == null) {
+ Log.w(TAG, "Default BT adapter not found");
+ return;
}
- fireCallbacks();
+
+ mReceiver.register();
+ setAdapterState(mAdapter.getState());
updateBondedBluetoothDevices();
}
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("BluetoothController state:");
+ pw.print(" mAdapter="); pw.println(mAdapter);
+ pw.print(" mEnabled="); pw.println(mEnabled);
+ pw.print(" mConnecting="); pw.println(mConnecting);
+ pw.print(" mLastDevice="); pw.println(mLastDevice);
+ pw.print(" mCallbacks.size="); pw.println(mCallbacks.size());
+ pw.print(" mDeviceInfo.size="); pw.println(mDeviceInfo.size());
+ for (int i = 0; i < mDeviceInfo.size(); i++) {
+ final BluetoothDevice device = mDeviceInfo.keyAt(i);
+ final DeviceInfo info = mDeviceInfo.valueAt(i);
+ pw.print(" "); pw.print(deviceToString(device));
+ pw.print('('); pw.print(uuidsToString(device)); pw.print(')');
+ pw.print(" "); pw.println(infoToString(info));
+ }
+ }
+
+ private static String infoToString(DeviceInfo info) {
+ return info == null ? null : ("connectionState=" +
+ connectionStateToString(info.connectionState) + ",bonded=" + info.bonded);
+ }
+
public void addStateChangedCallback(Callback cb) {
mCallbacks.add(cb);
- fireCallback(cb);
+ fireStateChange(cb);
}
@Override
@@ -99,64 +141,191 @@
return mAdapter != null;
}
- public Set<BluetoothDevice> getBondedBluetoothDevices() {
- return mBondedDevices;
+ @Override
+ public ArraySet<PairedDevice> getPairedDevices() {
+ final ArraySet<PairedDevice> rt = new ArraySet<>();
+ for (int i = 0; i < mDeviceInfo.size(); i++) {
+ final BluetoothDevice device = mDeviceInfo.keyAt(i);
+ final DeviceInfo info = mDeviceInfo.valueAt(i);
+ if (!info.bonded) continue;
+ final PairedDevice paired = new PairedDevice();
+ paired.id = device.getAddress();
+ paired.tag = device;
+ paired.name = device.getAliasName();
+ paired.state = connectionStateToPairedDeviceState(info.connectionState);
+ rt.add(paired);
+ }
+ return rt;
+ }
+
+ private static int connectionStateToPairedDeviceState(int state) {
+ if (state == BluetoothAdapter.STATE_CONNECTED) return PairedDevice.STATE_CONNECTED;
+ if (state == BluetoothAdapter.STATE_CONNECTING) return PairedDevice.STATE_CONNECTING;
+ if (state == BluetoothAdapter.STATE_DISCONNECTING) return PairedDevice.STATE_DISCONNECTING;
+ return PairedDevice.STATE_DISCONNECTED;
+ }
+
+ @Override
+ public void connect(final PairedDevice pd) {
+ connect(pd, true);
+ }
+
+ @Override
+ public void disconnect(PairedDevice pd) {
+ connect(pd, false);
+ }
+
+ private void connect(PairedDevice pd, final boolean connect) {
+ if (mAdapter == null || pd == null || pd.tag == null) return;
+ final BluetoothDevice device = (BluetoothDevice) pd.tag;
+ final String action = connect ? "connect" : "disconnect";
+ if (DEBUG) Log.d(TAG, action + " " + deviceToString(device));
+ final SparseBooleanArray profiles = new SparseBooleanArray();
+ for (ParcelUuid uuid : device.getUuids()) {
+ final int profile = uuidToProfile(uuid);
+ if (profile == 0) {
+ Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: "
+ + uuidToString(uuid));
+ continue;
+ }
+ final int profileState = mAdapter.getProfileConnectionState(profile);
+ if (DEBUG && !profiles.get(profile)) Log.d(TAG, "Profile " + profileToString(profile)
+ + " state = " + profileStateToString(profileState));
+ final boolean connected = profileState == BluetoothProfile.STATE_CONNECTED;
+ if (connect != connected) {
+ profiles.put(profile, true);
+ }
+ }
+ for (int i = 0; i < profiles.size(); i++) {
+ final int profile = profiles.keyAt(i);
+ mAdapter.getProfileProxy(mContext, new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (DEBUG) Log.d(TAG, "onServiceConnected " + profileToString(profile));
+ final Profile p = BluetoothUtil.getProfile(proxy);
+ if (p == null) {
+ 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);
+ }
}
@Override
public String getLastDeviceName() {
- return mLastDevice != null ? mLastDevice.getAliasName()
- : mBondedDevices.size() == 1 ? mBondedDevices.iterator().next().getAliasName()
- : null;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
-
- if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- handleAdapterStateChange(
- intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR));
- }
- if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
- mConnecting = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, -1)
- == BluetoothAdapter.STATE_CONNECTING;
- mLastDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- }
- if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
- mLastDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- }
- fireCallbacks();
- updateBondedBluetoothDevices();
+ return mLastDevice != null ? mLastDevice.getAliasName() : null;
}
private void updateBondedBluetoothDevices() {
- mBondedDevices.clear();
-
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- Set<BluetoothDevice> devices = adapter.getBondedDevices();
- if (devices != null) {
- for (BluetoothDevice device : devices) {
- if (device.getBondState() != BluetoothDevice.BOND_NONE) {
- mBondedDevices.add(device);
- }
+ if (mAdapter == null) return;
+ final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
+ for (DeviceInfo info : mDeviceInfo.values()) {
+ info.bonded = false;
+ }
+ int bondedCount = 0;
+ BluetoothDevice lastBonded = null;
+ if (bondedDevices != null) {
+ for (BluetoothDevice bondedDevice : bondedDevices) {
+ final boolean bonded = bondedDevice.getBondState() != BluetoothDevice.BOND_NONE;
+ updateInfo(bondedDevice).bonded = bonded;
+ if (bonded) {
+ bondedCount++;
+ lastBonded = bondedDevice;
}
}
}
+ if (mLastDevice == null && bondedCount == 1) {
+ mLastDevice = lastBonded;
+ }
+ firePairedDevicesChanged();
}
- private void handleAdapterStateChange(int adapterState) {
- mEnabled = (adapterState == BluetoothAdapter.STATE_ON);
- }
-
- private void fireCallbacks() {
+ private void firePairedDevicesChanged() {
for (Callback cb : mCallbacks) {
- fireCallback(cb);
+ cb.onBluetoothPairedDevicesChanged();
}
}
- private void fireCallback(Callback cb) {
+ private void setAdapterState(int adapterState) {
+ final boolean enabled = adapterState == BluetoothAdapter.STATE_ON;
+ if (mEnabled == enabled) return;
+ mEnabled = enabled;
+ fireStateChange();
+ }
+
+ private void setConnecting(boolean connecting) {
+ if (mConnecting == connecting) return;
+ mConnecting = connecting;
+ fireStateChange();
+ }
+
+ private void fireStateChange() {
+ for (Callback cb : mCallbacks) {
+ fireStateChange(cb);
+ }
+ }
+
+ private void fireStateChange(Callback cb) {
cb.onBluetoothStateChange(mEnabled, mConnecting);
}
+
+ private final class Receiver extends BroadcastReceiver {
+ public void register() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
+ mContext.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR));
+ if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled);
+ } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+ final DeviceInfo info = updateInfo(device);
+ final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
+ ERROR);
+ if (state != ERROR) {
+ info.connectionState = state;
+ }
+ mLastDevice = device;
+ if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED "
+ + connectionStateToString(state) + " " + deviceToString(device));
+ setConnecting(info.connectionState == BluetoothAdapter.STATE_CONNECTING);
+ } else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
+ updateInfo(device);
+ mLastDevice = device;
+ } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
+ if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device);
+ // we'll update all bonded devices below
+ }
+ updateBondedBluetoothDevices();
+ }
+ }
+
+ private DeviceInfo updateInfo(BluetoothDevice device) {
+ DeviceInfo info = mDeviceInfo.get(device);
+ info = info != null ? info : new DeviceInfo();
+ mDeviceInfo.put(device, info);
+ return info;
+ }
+
+ private static class DeviceInfo {
+ int connectionState = BluetoothAdapter.STATE_DISCONNECTED;
+ boolean bonded; // per getBondedDevices
+ }
}