blob: cd17cfcd0e9fb965e78c3ea9c9bc7443c20a1464 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.policy;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.Dependency;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.WeakHashMap;
public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
CachedBluetoothDevice.Callback {
private static final String TAG = "BluetoothController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final LocalBluetoothManager mLocalBluetoothManager;
private final UserManager mUserManager;
private final int mCurrentUser;
private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState =
new WeakHashMap<>();
private final Handler mBgHandler;
private boolean mEnabled;
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
private CachedBluetoothDevice mLastDevice;
private final H mHandler = new H(Looper.getMainLooper());
private int mState;
public BluetoothControllerImpl(Context context, Looper bgLooper) {
mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class);
mBgHandler = new Handler(bgLooper);
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler);
mLocalBluetoothManager.getEventManager().registerCallback(this);
onBluetoothStateChanged(
mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
}
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mCurrentUser = ActivityManager.getCurrentUser();
}
@Override
public boolean canConfigBluetooth() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH,
UserHandle.of(mCurrentUser))
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_BLUETOOTH,
UserHandle.of(mCurrentUser));
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BluetoothController state:");
pw.print(" mLocalBluetoothManager="); pw.println(mLocalBluetoothManager);
if (mLocalBluetoothManager == null) {
return;
}
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState));
pw.print(" mLastDevice="); pw.println(mLastDevice);
pw.print(" mCallbacks.size="); pw.println(mHandler.mCallbacks.size());
pw.println(" Bluetooth Devices:");
for (CachedBluetoothDevice device :
mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
pw.println(" " + getDeviceString(device));
}
}
private static String stateToString(int state) {
switch (state) {
case BluetoothAdapter.STATE_CONNECTED:
return "CONNECTED";
case BluetoothAdapter.STATE_CONNECTING:
return "CONNECTING";
case BluetoothAdapter.STATE_DISCONNECTED:
return "DISCONNECTED";
case BluetoothAdapter.STATE_DISCONNECTING:
return "DISCONNECTING";
}
return "UNKNOWN(" + state + ")";
}
private String getDeviceString(CachedBluetoothDevice device) {
return device.getName() + " " + device.getBondState() + " " + device.isConnected();
}
@Override
public int getBondState(CachedBluetoothDevice device) {
return getCachedState(device).mBondState;
}
@Override
public CachedBluetoothDevice getLastDevice() {
return mLastDevice;
}
@Override
public int getMaxConnectionState(CachedBluetoothDevice device) {
return getCachedState(device).mMaxConnectionState;
}
@Override
public void addCallback(Callback cb) {
mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
public void removeCallback(Callback cb) {
mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget();
}
@Override
public boolean isBluetoothEnabled() {
return mEnabled;
}
@Override
public int getBluetoothState() {
return mState;
}
@Override
public boolean isBluetoothConnected() {
return mConnectionState == BluetoothAdapter.STATE_CONNECTED;
}
@Override
public boolean isBluetoothConnecting() {
return mConnectionState == BluetoothAdapter.STATE_CONNECTING;
}
@Override
public void setBluetoothEnabled(boolean enabled) {
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getBluetoothAdapter().setBluetoothEnabled(enabled);
}
}
@Override
public boolean isBluetoothSupported() {
return mLocalBluetoothManager != null;
}
@Override
public void connect(final CachedBluetoothDevice device) {
if (mLocalBluetoothManager == null || device == null) return;
device.connect(true);
}
@Override
public void disconnect(CachedBluetoothDevice device) {
if (mLocalBluetoothManager == null || device == null) return;
device.disconnect();
}
@Override
public String getLastDeviceName() {
return mLastDevice != null ? mLastDevice.getName() : null;
}
@Override
public Collection<CachedBluetoothDevice> getDevices() {
return mLocalBluetoothManager != null
? mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
: null;
}
private void updateConnected() {
// Make sure our connection state is up to date.
int state = mLocalBluetoothManager.getBluetoothAdapter().getConnectionState();
if (mLastDevice != null && !mLastDevice.isConnected()) {
// Clear out last device if no longer connected.
mLastDevice = null;
}
// If any of the devices are in a higher state than the adapter, move the adapter into
// that state.
for (CachedBluetoothDevice device : getDevices()) {
int maxDeviceState = device.getMaxConnectionState();
if (maxDeviceState > state) {
state = maxDeviceState;
}
if (mLastDevice == null && device.isConnected()) {
// Set as last connected device only if we don't have one.
mLastDevice = device;
}
}
if (mLastDevice == null && state == BluetoothAdapter.STATE_CONNECTED) {
// If somehow we think we are connected, but have no connected devices, we aren't
// connected.
state = BluetoothAdapter.STATE_DISCONNECTED;
}
if (state != mConnectionState) {
mConnectionState = state;
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
}
@Override
public void onBluetoothStateChanged(int bluetoothState) {
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
|| bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
mState = bluetoothState;
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
public void onScanningStateChanged(boolean started) {
// Don't care.
}
@Override
public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
cachedDevice.registerCallback(this);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceAttributesChanged() {
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
mCachedState.remove(cachedDevice);
mLastDevice = cachedDevice;
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
@Override
public void onAudioModeChanged() {}
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
if (state == null) {
state = new ActuallyCachedState(device, mHandler);
mBgHandler.post(state);
mCachedState.put(device, state);
return state;
}
return state;
}
private static class ActuallyCachedState implements Runnable {
private final WeakReference<CachedBluetoothDevice> mDevice;
private final Handler mUiHandler;
private int mBondState = BluetoothDevice.BOND_NONE;
private int mMaxConnectionState = BluetoothProfile.STATE_DISCONNECTED;
private ActuallyCachedState(CachedBluetoothDevice device, Handler uiHandler) {
mDevice = new WeakReference<>(device);
mUiHandler = uiHandler;
}
@Override
public void run() {
CachedBluetoothDevice device = mDevice.get();
if (device != null) {
mBondState = device.getBondState();
mMaxConnectionState = device.getMaxConnectionState();
mUiHandler.removeMessages(H.MSG_PAIRED_DEVICES_CHANGED);
mUiHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
}
}
private final class H extends Handler {
private final ArrayList<BluetoothController.Callback> mCallbacks = new ArrayList<>();
private static final int MSG_PAIRED_DEVICES_CHANGED = 1;
private static final int MSG_STATE_CHANGED = 2;
private static final int MSG_ADD_CALLBACK = 3;
private static final int MSG_REMOVE_CALLBACK = 4;
public H(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PAIRED_DEVICES_CHANGED:
firePairedDevicesChanged();
break;
case MSG_STATE_CHANGED:
fireStateChange();
break;
case MSG_ADD_CALLBACK:
mCallbacks.add((BluetoothController.Callback) msg.obj);
break;
case MSG_REMOVE_CALLBACK:
mCallbacks.remove((BluetoothController.Callback) msg.obj);
break;
}
}
private void firePairedDevicesChanged() {
for (BluetoothController.Callback cb : mCallbacks) {
cb.onBluetoothDevicesChanged();
}
}
private void fireStateChange() {
for (BluetoothController.Callback cb : mCallbacks) {
fireStateChange(cb);
}
}
private void fireStateChange(BluetoothController.Callback cb) {
cb.onBluetoothStateChange(mEnabled);
}
}
}