diff options
11 files changed, 311 insertions, 395 deletions
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 4656e15..1f4fe80 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1049,6 +1049,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.A2DP) { BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); return true; + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); + return true; } else { return false; } diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index 6ec347f..116a068 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -136,17 +136,17 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); int oldState = - intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0); + intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - if (oldState == BluetoothInputDevice.STATE_CONNECTED && - newState == BluetoothInputDevice.STATE_DISCONNECTED) { + if (oldState == BluetoothProfile.STATE_CONNECTED && + newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_INCOMING); } - if (newState == BluetoothInputDevice.STATE_CONNECTED || - newState == BluetoothInputDevice.STATE_DISCONNECTED) { + if (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { @@ -194,7 +194,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -286,7 +286,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(DISCONNECT_A2DP_OUTGOING); deferMessage(message); break; - } else if (mService.getInputDeviceState(mDevice) != + } else if (mService.getInputDeviceConnectionState(mDevice) != BluetoothInputDevice.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_OUTGOING); deferMessage(message); diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index a70de59..df212a8 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 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. @@ -27,91 +27,88 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; + /** - * Public API for controlling the Bluetooth HID (Input Device) Profile - * - * BluetoothInputDevice is a proxy object used to make calls to Bluetooth Service - * which handles the HID profile. + * This class provides the public APIs to control the Bluetooth Input + * Device Profile. * - * Creating a BluetoothInputDevice object will initiate a binding with the - * Bluetooth service. Users of this object should call close() when they - * are finished, so that this proxy object can unbind from the service. + *<p>BluetoothInputDevice is a proxy object for controlling the Bluetooth + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothInputDevice proxy object. * - * Currently the Bluetooth service runs in the system server and this - * proxy object will be immediately bound to the service on construction. - * - * @hide + *<p>Each method is protected with its appropriate permission. + *@hide */ -public final class BluetoothInputDevice { +public final class BluetoothInputDevice implements BluetoothProfile { private static final String TAG = "BluetoothInputDevice"; private static final boolean DBG = false; - /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */ - public static final String EXTRA_INPUT_DEVICE_STATE = - "android.bluetooth.inputdevice.extra.INPUT_DEVICE_STATE"; - /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */ - public static final String EXTRA_PREVIOUS_INPUT_DEVICE_STATE = - "android.bluetooth.inputdevice.extra.PREVIOUS_INPUT_DEVICE_STATE"; - - /** Indicates the state of an input device has changed. - * This intent will always contain EXTRA_INPUT_DEVICE_STATE, - * EXTRA_PREVIOUS_INPUT_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE - * extras. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_INPUT_DEVICE_STATE_CHANGED = - "android.bluetooth.inputdevice.action.INPUT_DEVICE_STATE_CHANGED"; - - public static final int STATE_DISCONNECTED = 0; - public static final int STATE_CONNECTING = 1; - public static final int STATE_CONNECTED = 2; - public static final int STATE_DISCONNECTING = 3; - - /** - * Auto connection, incoming and outgoing connection are allowed at this - * priority level. - */ - public static final int PRIORITY_AUTO_CONNECT = 1000; - /** - * Incoming and outgoing connection are allowed at this priority level - */ - public static final int PRIORITY_ON = 100; - /** - * Connections to the device are not allowed at this priority level. - */ - public static final int PRIORITY_OFF = 0; /** - * Default priority level when the device is unpaired. + * Intent used to broadcast the change in connection state of the Input + * Device profile. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ - public static final int PRIORITY_UNDEFINED = -1; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. + * @hide */ public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; + /** + * @hide + */ public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001; + /** + * @hide + */ public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002; + /** + * @hide + */ public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003; + /** + * @hide + */ public static final int INPUT_OPERATION_SUCCESS = 5004; - private final IBluetooth mService; - private final Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetooth mService; /** * Create a BluetoothInputDevice proxy object for interacting with the local - * Bluetooth Service which handle the HID profile. - * @param c Context + * Bluetooth Service which handles the InputDevice profile + * */ - public BluetoothInputDevice(Context c) { - mContext = c; - + /*package*/ BluetoothInputDevice(Context mContext, ServiceListener l) { IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); if (b != null) { mService = IBluetooth.Stub.asInterface(b); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, this); + } } else { Log.w(TAG, "Bluetooth Service not available!"); @@ -121,130 +118,151 @@ public final class BluetoothInputDevice { } } - /** Initiate a connection to an Input device. - * - * This function returns false on error and true if the connection - * attempt is being made. - * - * Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when the - * connection is completed. - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} + * @hide */ - public boolean connectInputDevice(BluetoothDevice device) { - if (DBG) log("connectInputDevice(" + device + ")"); - try { - return mService.connectInputDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connectInputDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Initiate disconnect from an Input Device. - * This function return false on error and true if the disconnection - * attempt is being made. - * - * Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when - * disconnect is completed. - * - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} + * @hide */ - public boolean disconnectInputDevice(BluetoothDevice device) { - if (DBG) log("disconnectInputDevice(" + device + ")"); - try { - return mService.disconnectInputDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnectInputDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Check if a specified InputDevice is connected. - * - * @param device Remote BT device. - * @return True if connected , false otherwise and on error. - * @hide + /** + * {@inheritDoc} */ - public boolean isInputDeviceConnected(BluetoothDevice device) { - if (DBG) log("isInputDeviceConnected(" + device + ")"); - int state = getInputDeviceState(device); - if (state == STATE_CONNECTED) return true; - return false; + public List<BluetoothDevice> getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedInputDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); } - /** Check if any Input Device is connected. - * - * @return List of devices, empty List on error. - * @hide + /** + * {@inheritDoc} */ - public List<BluetoothDevice> getConnectedInputDevices() { - if (DBG) log("getConnectedInputDevices()"); - try { - return mService.getConnectedInputDevices(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return new ArrayList<BluetoothDevice>(); + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getInputDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); } - /** Get the state of an Input Device. - * - * @param device Remote BT device. - * @return The current state of the Input Device - * @hide + /** + * {@inheritDoc} */ - public int getInputDeviceState(BluetoothDevice device) { - if (DBG) log("getInputDeviceState(" + device + ")"); - try { - return mService.getInputDeviceState(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return STATE_DISCONNECTED; + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getInputDeviceConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; } /** - * Set priority of an input device. - * - * Priority is a non-negative integer. Priority can take the following - * values: - * {@link PRIORITY_ON}, {@link PRIORITY_OFF}, {@link PRIORITY_AUTO_CONNECT} - * - * @param device Paired device. - * @param priority Integer priority - * @return true if priority is set, false on error + * {@inheritDoc} + * @hide */ - public boolean setInputDevicePriority(BluetoothDevice device, int priority) { - if (DBG) log("setInputDevicePriority(" + device + ", " + priority + ")"); - try { - return mService.setInputDevicePriority(device, priority); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return mService.setInputDevicePriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Get the priority associated with an Input Device. - * - * @param device Input Device - * @return non-negative priority, or negative error code on error. + * {@inheritDoc} + * @hide */ - public int getInputDevicePriority(BluetoothDevice device) { - if (DBG) log("getInputDevicePriority(" + device + ")"); - try { - return mService.getInputDevicePriority(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return PRIORITY_OFF; + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getInputDevicePriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index ef80195..1ffee72 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -62,6 +62,11 @@ public interface BluetoothProfile { * A2DP profile. */ public static final int A2DP = 2; + /** + * Input Device Profile + * @hide + */ + public static final int INPUT_DEVICE = 3; /** * Default priority for devices that we try to auto-connect to and diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java index 3f36926..18060a0 100644 --- a/core/java/android/bluetooth/BluetoothProfileState.java +++ b/core/java/android/bluetooth/BluetoothProfileState.java @@ -72,10 +72,10 @@ public class BluetoothProfileState extends HierarchicalStateMachine { newState == BluetoothProfile.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); - if (mProfile == HID && (newState == BluetoothInputDevice.STATE_CONNECTED || - newState == BluetoothInputDevice.STATE_DISCONNECTED)) { + } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); + if (mProfile == HID && (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { @@ -96,7 +96,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); context.registerReceiver(mBroadcastReceiver, filter); } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index f3e73cf..69fb06a 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -85,7 +85,8 @@ interface IBluetooth boolean connectInputDevice(in BluetoothDevice device); boolean disconnectInputDevice(in BluetoothDevice device); List<BluetoothDevice> getConnectedInputDevices(); - int getInputDeviceState(in BluetoothDevice device); + List<BluetoothDevice> getInputDevicesMatchingConnectionStates(in int[] states); + int getInputDeviceConnectionState(in BluetoothDevice device); boolean setInputDevicePriority(in BluetoothDevice device, int priority); int getInputDevicePriority(in BluetoothDevice device); diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 3316ea5..c0b3cc8 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -57,6 +57,7 @@ class BluetoothEventLoop { private final BluetoothService mBluetoothService; private final BluetoothAdapter mAdapter; private BluetoothA2dp mA2dp; + private BluetoothInputDevice mInputDevice; private final Context mContext; // The WakeLock is used for bringing up the LCD during a pairing request // from remote device when Android is in Suspend state. @@ -125,15 +126,24 @@ class BluetoothEventLoop { /*package*/ void getProfileProxy() { mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.INPUT_DEVICE); } private BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { - mA2dp = (BluetoothA2dp) proxy; + if (profile == BluetoothProfile.A2DP) { + mA2dp = (BluetoothA2dp) proxy; + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + mInputDevice = (BluetoothInputDevice) proxy; + } } public void onServiceDisconnected(int profile) { - mA2dp = null; + if (profile == BluetoothProfile.A2DP) { + mA2dp = null; + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + mInputDevice = null; + } } }; @@ -651,10 +661,9 @@ class BluetoothEventLoop { } else { Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address); } - } else if (BluetoothUuid.isInputDevice(uuid) && !isOtherInputDeviceConnected(address)) { - BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext); - authorized = inputDevice.getInputDevicePriority(device) > - BluetoothInputDevice.PRIORITY_OFF; + } else if (mInputDevice != null && BluetoothUuid.isInputDevice(uuid)) { + // We can have more than 1 input device connected. + authorized = mInputDevice.getPriority(device) > BluetoothInputDevice.PRIORITY_OFF; if (authorized) { Log.i(TAG, "Allowing incoming HID connection from " + address); } else { @@ -669,18 +678,6 @@ class BluetoothEventLoop { return authorized; } - private boolean isOtherInputDeviceConnected(String address) { - List<BluetoothDevice> devices = - mBluetoothService.lookupInputDevicesMatchingStates(new int[] { - BluetoothInputDevice.STATE_CONNECTING, - BluetoothInputDevice.STATE_CONNECTED}); - - for (BluetoothDevice device : devices) { - if (!device.getAddress().equals(address)) return true; - } - return false; - } - private boolean onAgentOutOfBandDataAvailable(String objectPath) { if (!mBluetoothService.isEnabled()) return false; @@ -758,7 +755,7 @@ class BluetoothEventLoop { boolean connected = false; BluetoothDevice device = mAdapter.getRemoteDevice(address); - int state = mBluetoothService.getInputDeviceState(device); + int state = mBluetoothService.getInputDeviceConnectionState(device); if (state == BluetoothInputDevice.STATE_CONNECTING) { if (result == BluetoothInputDevice.INPUT_CONNECT_FAILED_ALREADY_CONNECTED) { connected = true; diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java index 7ffa5ae..cdc0f2d 100644 --- a/core/java/android/server/BluetoothInputProfileHandler.java +++ b/core/java/android/server/BluetoothInputProfileHandler.java @@ -64,7 +64,7 @@ final class BluetoothInputProfileHandler { BluetoothDeviceProfileState state) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (objectPath == null || - getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED || + getInputDeviceConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED || getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { return false; } @@ -92,7 +92,7 @@ final class BluetoothInputProfileHandler { BluetoothDeviceProfileState state) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (objectPath == null || - getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { + getInputDeviceConnectionState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { return false; } if (state != null) { @@ -115,7 +115,7 @@ final class BluetoothInputProfileHandler { return true; } - synchronized int getInputDeviceState(BluetoothDevice device) { + synchronized int getInputDeviceConnectionState(BluetoothDevice device) { if (mInputDevices.get(device) == null) { return BluetoothInputDevice.STATE_DISCONNECTED; } @@ -128,6 +128,11 @@ final class BluetoothInputProfileHandler { return devices; } + synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) { + List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(states); + return devices; + } + synchronized int getInputDevicePriority(BluetoothDevice device) { return Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), @@ -147,7 +152,7 @@ final class BluetoothInputProfileHandler { List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); for (BluetoothDevice device: mInputDevices.keySet()) { - int inputDeviceState = getInputDeviceState(device); + int inputDeviceState = getInputDeviceConnectionState(device); for (int state : states) { if (state == inputDeviceState) { inputDevices.add(device); @@ -178,10 +183,10 @@ final class BluetoothInputProfileHandler { setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT); } - Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState); - intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state); + intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothInputDevice.EXTRA_STATE, state); mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index a295de5..70aaf0a 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -31,6 +31,7 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDeviceProfileState; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfileState; @@ -83,6 +84,7 @@ public class BluetoothService extends IBluetooth.Stub { private int mNativeData; private BluetoothEventLoop mEventLoop; private BluetoothHeadset mBluetoothHeadset; + private BluetoothInputDevice mInputDevice; private boolean mIsAirplaneSensitive; private boolean mIsAirplaneToggleable; private int mBluetoothState; @@ -2078,6 +2080,8 @@ public class BluetoothService extends IBluetooth.Stub { mAdapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); + mAdapter.getProfileProxy(mContext, + mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE); pw.println("\n--Known devices--"); for (String address : mDeviceProperties.keySet()) { @@ -2119,8 +2123,17 @@ public class BluetoothService extends IBluetooth.Stub { } } - // Rather not do this from here, but no-where else and I need this - // dump + dumpHeadsetProfile(pw); + dumpInputDeviceProfile(pw); + + pw.println("\n--Application Service Records--"); + for (Integer handle : mServiceRecordToPid.keySet()) { + Integer pid = mServiceRecordToPid.get(handle); + pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); + } + } + + private void dumpHeadsetProfile(PrintWriter pw) { pw.println("\n--Headset Service--"); if (mBluetoothHeadset != null) { List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); @@ -2158,24 +2171,60 @@ public class BluetoothService extends IBluetooth.Stub { pw.println("SCO audio connected to device:" + device); } } - - mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); } + mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); + } - pw.println("\n--Application Service Records--"); - for (Integer handle : mServiceRecordToPid.keySet()) { - Integer pid = mServiceRecordToPid.get(handle); - pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); + private void dumpInputDeviceProfile(PrintWriter pw) { + pw.println("\n--Bluetooth Service- Input Device Profile"); + if (mInputDevice != null) { + List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices(); + if (deviceList.size() == 0) { + pw.println("\nNo input devices connected--"); + } else { + pw.println("\nNumber of connected devices:" + deviceList.size()); + BluetoothDevice device = deviceList.get(0); + pw.println("getConnectedDevices[0] = " + device); + pw.println("Priority of Connected device = " + mInputDevice.getPriority(device)); + + switch (mInputDevice.getConnectionState(device)) { + case BluetoothInputDevice.STATE_CONNECTING: + pw.println("getConnectionState() = STATE_CONNECTING"); + break; + case BluetoothInputDevice.STATE_CONNECTED: + pw.println("getConnectionState() = STATE_CONNECTED"); + break; + case BluetoothInputDevice.STATE_DISCONNECTING: + pw.println("getConnectionState() = STATE_DISCONNECTING"); + break; + } + } + deviceList.clear(); + deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] { + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); + pw.println("--Connected and Disconnected input devices"); + for (BluetoothDevice device: deviceList) { + pw.println(device); + } } + mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset); } private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { - mBluetoothHeadset = (BluetoothHeadset) proxy; - } + if (profile == BluetoothProfile.HEADSET) { + mBluetoothHeadset = (BluetoothHeadset) proxy; + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + mInputDevice = (BluetoothInputDevice) proxy; + } + } public void onServiceDisconnected(int profile) { - mBluetoothHeadset = null; + if (profile == BluetoothProfile.HEADSET) { + mBluetoothHeadset = null; + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + mInputDevice = null; + } } }; @@ -2311,9 +2360,9 @@ public class BluetoothService extends IBluetooth.Stub { return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device); } - public synchronized int getInputDeviceState(BluetoothDevice device) { + public synchronized int getInputDeviceConnectionState(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothInputProfileHandler.getInputDeviceState(device); + return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device); } @@ -2322,6 +2371,13 @@ public class BluetoothService extends IBluetooth.Stub { return mBluetoothInputProfileHandler.getConnectedInputDevices(); } + public synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates( + int[] states) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states); + } + + public synchronized int getInputDevicePriority(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mBluetoothInputProfileHandler.getInputDevicePriority(device); diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java index 96b028a..672f252 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java @@ -250,8 +250,8 @@ public class BluetoothStressTest extends InstrumentationTestCase { for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectInput(adapter, device); - mTestUtils.disconnectInput(adapter, device); + mTestUtils.connectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE); } mTestUtils.unpair(adapter, device); diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java index effed76..35210e5 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java @@ -238,6 +238,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; break; + case BluetoothProfile.INPUT_DEVICE: + mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED; + break; default: mConnectionAction = null; } @@ -270,47 +273,6 @@ public class BluetoothTestUtils extends Assert { } } - private class ConnectInputReceiver extends FlagReceiver { - private static final int STATE_DISCONNECTED_FLAG = 1; - private static final int STATE_CONNECTING_FLAG = 1 << 1; - private static final int STATE_CONNECTED_FLAG = 1 << 2; - private static final int STATE_DISCONNECTING_FLAG = 1 << 3; - - private BluetoothDevice mDevice; - - public ConnectInputReceiver(BluetoothDevice device, int expectedFlags) { - super(expectedFlags); - - mDevice = device; - } - - @Override - public void onReceive(Context context, Intent intent) { - if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) { - return; - } - - if (BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED.equals(intent.getAction())) { - int state = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, -1); - assertNotSame(-1, state); - switch (state) { - case BluetoothInputDevice.STATE_DISCONNECTED: - setFiredFlag(STATE_DISCONNECTED_FLAG); - break; - case BluetoothInputDevice.STATE_CONNECTING: - setFiredFlag(STATE_CONNECTING_FLAG); - break; - case BluetoothInputDevice.STATE_CONNECTED: - setFiredFlag(STATE_CONNECTED_FLAG); - break; - case BluetoothInputDevice.STATE_DISCONNECTING: - setFiredFlag(STATE_DISCONNECTING_FLAG); - break; - } - } - } - } - private class ConnectPanReceiver extends FlagReceiver { private static final int STATE_DISCONNECTED_FLAG = 1; private static final int STATE_CONNECTING_FLAG = 1 << 1; @@ -366,6 +328,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = (BluetoothHeadset) proxy; break; + case BluetoothProfile.INPUT_DEVICE: + mInput = (BluetoothInputDevice) proxy; + break; } } } @@ -379,6 +344,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = null; break; + case BluetoothProfile.INPUT_DEVICE: + mInput = null; + break; } } } @@ -393,6 +361,7 @@ public class BluetoothTestUtils extends Assert { private Context mContext; private BluetoothA2dp mA2dp; private BluetoothHeadset mHeadset; + private BluetoothInputDevice mInput; /** * Creates a utility instance for testing Bluetooth. @@ -1078,142 +1047,6 @@ public class BluetoothTestUtils extends Assert { } /** - * Connects the local device with a remote HID device and checks to make sure that the profile - * is connected and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void connectInput(BluetoothAdapter adapter, BluetoothDevice device) { - int mask = (ConnectInputReceiver.STATE_CONNECTING_FLAG - | ConnectInputReceiver.STATE_CONNECTED_FLAG); - long start = -1; - - if (!adapter.isEnabled()) { - fail(String.format("connectInput() bluetooth not enabled: device=%s", device)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("connectInput() device not paired: device=%s", device)); - } - - BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext); - assertNotNull(inputDevice); - ConnectInputReceiver receiver = getConnectInputReceiver(device, mask); - - int state = inputDevice.getInputDeviceState(device); - switch (state) { - case BluetoothInputDevice.STATE_CONNECTED: - removeReceiver(receiver); - return; - case BluetoothInputDevice.STATE_CONNECTING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - case BluetoothInputDevice.STATE_DISCONNECTED: - case BluetoothInputDevice.STATE_DISCONNECTING: - start = System.currentTimeMillis(); - assertTrue(inputDevice.connectInputDevice(device)); - break; - default: - removeReceiver(receiver); - fail(String.format("connectInput() invalid state: device=%s, state=%d", device, - state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = inputDevice.getInputDeviceState(device); - if (state == BluetoothInputDevice.STATE_CONNECTED - && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("connectInput() completed in %d ms: device=%s", - (finish - start), device)); - } else { - writeOutput(String.format("connectInput() completed: device=%s", device)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("connectInput() timeout: device=%s, state=%d (expected %d), " - + "flags=0x%x (expected 0x%s)", device, state, BluetoothInputDevice.STATE_CONNECTED, - firedFlags, mask)); - } - - /** - * Disconnects the local device with a remote HID device and checks to make sure that the - * profile is connected and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void disconnectInput(BluetoothAdapter adapter, BluetoothDevice device) { - int mask = (ConnectInputReceiver.STATE_DISCONNECTING_FLAG - | ConnectInputReceiver.STATE_DISCONNECTED_FLAG); - long start = -1; - - if (!adapter.isEnabled()) { - fail(String.format("disconnectInput() bluetooth not enabled: device=%s", device)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("disconnectInput() device not paired: device=%s", device)); - } - - BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext); - assertNotNull(inputDevice); - ConnectInputReceiver receiver = getConnectInputReceiver(device, mask); - - int state = inputDevice.getInputDeviceState(device); - switch (state) { - case BluetoothInputDevice.STATE_CONNECTED: - case BluetoothInputDevice.STATE_CONNECTING: - start = System.currentTimeMillis(); - assertTrue(inputDevice.disconnectInputDevice(device)); - break; - case BluetoothInputDevice.STATE_DISCONNECTED: - removeReceiver(receiver); - return; - case BluetoothInputDevice.STATE_DISCONNECTING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - default: - removeReceiver(receiver); - fail(String.format("disconnectInput() invalid state: device=%s, state=%d", device, - state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = inputDevice.getInputDeviceState(device); - if (state == BluetoothInputDevice.STATE_DISCONNECTED - && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("disconnectInput() completed in %d ms: device=%s", - (finish - start), device)); - } else { - writeOutput(String.format("disconnectInput() completed: device=%s", device)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("disconnectInput() timeout: device=%s, state=%d (expected %d), " - + "flags=0x%x (expected 0x%s)", device, state, - BluetoothInputDevice.STATE_DISCONNECTED, firedFlags, mask)); - } - - /** * Connects the PANU to a remote NAP and checks to make sure that the PANU is connected and that * the correct actions were broadcast. * @@ -1478,21 +1311,14 @@ public class BluetoothTestUtils extends Assert { int expectedFlags) { String[] actions = { BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, - BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED}; + BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, + BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED}; ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, expectedFlags); addReceiver(receiver, actions); return receiver; } - private ConnectInputReceiver getConnectInputReceiver(BluetoothDevice device, - int expectedFlags) { - String[] actions = {BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED}; - ConnectInputReceiver receiver = new ConnectInputReceiver(device, expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - private ConnectPanReceiver getConnectPanReceiver(BluetoothDevice device, int role, int expectedFlags) { String[] actions = {BluetoothPan.ACTION_PAN_STATE_CHANGED}; @@ -1511,15 +1337,20 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); switch (profile) { case BluetoothProfile.A2DP: - while (mA2dp != null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + while (mA2dp == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } return mA2dp; case BluetoothProfile.HEADSET: - while (mHeadset != null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + while (mHeadset == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } return mHeadset; + case BluetoothProfile.INPUT_DEVICE: + while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mInput; default: return null; } |