Merge "HID profile."
This commit is contained in:
committed by
Android (Google) Code Review
commit
cf990885da
241
core/java/android/bluetooth/BluetoothInputDevice.java
Normal file
241
core/java/android/bluetooth/BluetoothInputDevice.java
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 android.bluetooth;
|
||||
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Currently the Bluetooth service runs in the system server and this
|
||||
* proxy object will be immediately bound to the service on construction.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class BluetoothInputDevice {
|
||||
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.
|
||||
*/
|
||||
public static final int PRIORITY_UNDEFINED = -1;
|
||||
|
||||
private final IBluetooth mService;
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* Create a BluetoothInputDevice proxy object for interacting with the local
|
||||
* Bluetooth Service which handle the HID profile.
|
||||
* @param c Context
|
||||
*/
|
||||
public BluetoothInputDevice(Context c) {
|
||||
mContext = c;
|
||||
|
||||
IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
|
||||
if (b != null) {
|
||||
mService = IBluetooth.Stub.asInterface(b);
|
||||
} else {
|
||||
Log.w(TAG, "Bluetooth Service not available!");
|
||||
|
||||
// Instead of throwing an exception which prevents people from going
|
||||
// into Wireless settings in the emulator. Let it crash later when it is actually used.
|
||||
mService = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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
|
||||
*/
|
||||
public boolean connectInputDevice(BluetoothDevice device) {
|
||||
if (DBG) log("connectInputDevice(" + device + ")");
|
||||
try {
|
||||
return mService.connectInputDevice(device);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
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
|
||||
*/
|
||||
public boolean disconnectInputDevice(BluetoothDevice device) {
|
||||
if (DBG) log("disconnectInputDevice(" + device + ")");
|
||||
try {
|
||||
return mService.disconnectInputDevice(device);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a specified InputDevice is connected.
|
||||
*
|
||||
* @param device Remote BT device.
|
||||
* @return True if connected , false otherwise and on error.
|
||||
* @hide
|
||||
*/
|
||||
public boolean isInputDeviceConnected(BluetoothDevice device) {
|
||||
if (DBG) log("isInputDeviceConnected(" + device + ")");
|
||||
int state = getInputDeviceState(device);
|
||||
if (state == STATE_CONNECTED) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if any Input Device is connected.
|
||||
*
|
||||
* @return a unmodifiable set of connected Input Devices, or null on error.
|
||||
* @hide
|
||||
*/
|
||||
public Set<BluetoothDevice> getConnectedInputDevices() {
|
||||
if (DBG) log("getConnectedInputDevices()");
|
||||
try {
|
||||
return Collections.unmodifiableSet(
|
||||
new HashSet<BluetoothDevice>(
|
||||
Arrays.asList(mService.getConnectedInputDevices())));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the state of an Input Device.
|
||||
*
|
||||
* @param device Remote BT device.
|
||||
* @return The current state of the Input Device
|
||||
* @hide
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the priority associated with an Input Device.
|
||||
*
|
||||
* @param device Input Device
|
||||
* @return non-negative priority, or negative error code on error.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private static void log(String msg) {
|
||||
Log.d(TAG, msg);
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ public final class BluetoothUuid {
|
||||
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
|
||||
public static final ParcelUuid ObexObjectPush =
|
||||
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
|
||||
public static final ParcelUuid Hid =
|
||||
ParcelUuid.fromString("00000011-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
public static final ParcelUuid[] RESERVED_UUIDS = {
|
||||
AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
|
||||
@@ -82,6 +84,10 @@ public final class BluetoothUuid {
|
||||
return uuid.equals(AvrcpTarget);
|
||||
}
|
||||
|
||||
public static boolean isInputDevice(ParcelUuid uuid) {
|
||||
return uuid.equals(Hid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if ParcelUuid is present in uuidArray
|
||||
*
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.bluetooth;
|
||||
|
||||
import android.bluetooth.IBluetoothCallback;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
/**
|
||||
@@ -72,4 +73,12 @@ interface IBluetooth
|
||||
boolean connectHeadset(String address);
|
||||
boolean disconnectHeadset(String address);
|
||||
boolean notifyIncomingConnection(String address);
|
||||
|
||||
// HID profile APIs
|
||||
boolean connectInputDevice(in BluetoothDevice device);
|
||||
boolean disconnectInputDevice(in BluetoothDevice device);
|
||||
BluetoothDevice[] getConnectedInputDevices(); // change to Set<> once AIDL supports
|
||||
int getInputDeviceState(in BluetoothDevice device);
|
||||
boolean setInputDevicePriority(in BluetoothDevice device, int priority);
|
||||
int getInputDevicePriority(in BluetoothDevice device);
|
||||
}
|
||||
|
||||
@@ -1001,7 +1001,7 @@ public final class Settings {
|
||||
public static boolean hasInterestingConfigurationChanges(int changes) {
|
||||
return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
|
||||
}
|
||||
|
||||
|
||||
public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
|
||||
return getInt(cr, SHOW_GTALK_SERVICE_STATUS, 0) != 0;
|
||||
}
|
||||
@@ -1208,7 +1208,7 @@ public final class Settings {
|
||||
public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
|
||||
|
||||
/**
|
||||
* @deprecated Use
|
||||
* @deprecated Use
|
||||
* {@link android.provider.Settings.Secure#LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED}
|
||||
* instead
|
||||
*/
|
||||
@@ -2281,6 +2281,14 @@ public final class Settings {
|
||||
return ("bluetooth_a2dp_sink_priority_" + address.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key that retrieves a bluetooth Input Device's priority.
|
||||
* @hide
|
||||
*/
|
||||
public static final String getBluetoothInputDevicePriorityKey(String address) {
|
||||
return ("bluetooth_input_device_priority_" + address.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not data roaming is enabled. (0 = false, 1 = true)
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothA2dp;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothInputDevice;
|
||||
import android.bluetooth.BluetoothUuid;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -427,6 +428,20 @@ class BluetoothEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
private void onInputDevicePropertyChanged(String path, String[] propValues) {
|
||||
String address = mBluetoothService.getAddressFromObjectPath(path);
|
||||
if (address == null) {
|
||||
Log.e(TAG, "onInputDevicePropertyChanged: Address of the remote device in null");
|
||||
return;
|
||||
}
|
||||
log(" Input Device : Name of Property is:" + propValues[0]);
|
||||
boolean state = false;
|
||||
if (propValues[1].equals("true")) {
|
||||
state = true;
|
||||
}
|
||||
mBluetoothService.handleInputDevicePropertyChange(address, state);
|
||||
}
|
||||
|
||||
private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
|
||||
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
||||
if (address == null) {
|
||||
@@ -573,6 +588,8 @@ class BluetoothEventLoop {
|
||||
}
|
||||
|
||||
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
|
||||
if (!mBluetoothService.isEnabled()) return false;
|
||||
|
||||
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
||||
if (address == null) {
|
||||
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
|
||||
@@ -581,15 +598,15 @@ class BluetoothEventLoop {
|
||||
|
||||
boolean authorized = false;
|
||||
ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
|
||||
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
||||
BluetoothDevice device = mAdapter.getRemoteDevice(address);
|
||||
|
||||
// Bluez sends the UUID of the local service being accessed, _not_ the
|
||||
// remote service
|
||||
if (mBluetoothService.isEnabled() &&
|
||||
(BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|
||||
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
|
||||
!isOtherSinkInNonDisconnectingState(address)) {
|
||||
BluetoothDevice device = mAdapter.getRemoteDevice(address);
|
||||
if ((BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|
||||
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
|
||||
!isOtherSinkInNonDisconnectingState(address)) {
|
||||
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
||||
|
||||
authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
|
||||
if (authorized) {
|
||||
Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
|
||||
@@ -597,6 +614,15 @@ 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;
|
||||
if (authorized) {
|
||||
Log.i(TAG, "Allowing incoming HID connection from " + address);
|
||||
} else {
|
||||
Log.i(TAG, "Rejecting incoming HID connection from " + address);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
|
||||
}
|
||||
@@ -604,7 +630,19 @@ class BluetoothEventLoop {
|
||||
return authorized;
|
||||
}
|
||||
|
||||
boolean isOtherSinkInNonDisconnectingState(String address) {
|
||||
private boolean isOtherInputDeviceConnected(String address) {
|
||||
Set<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 isOtherSinkInNonDisconnectingState(String address) {
|
||||
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
||||
Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
|
||||
if (devices.size() == 0) return false;
|
||||
|
||||
@@ -24,16 +24,19 @@
|
||||
|
||||
package android.server;
|
||||
|
||||
import android.bluetooth.BluetoothA2dp;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHeadset;
|
||||
import android.bluetooth.BluetoothDeviceProfileState;
|
||||
import android.bluetooth.BluetoothProfileState;
|
||||
import android.bluetooth.BluetoothInputDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.bluetooth.BluetoothUuid;
|
||||
import android.bluetooth.IBluetooth;
|
||||
import android.bluetooth.IBluetoothCallback;
|
||||
import android.bluetooth.IBluetoothHeadset;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -70,8 +73,10 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class BluetoothService extends IBluetooth.Stub {
|
||||
private static final String TAG = "BluetoothService";
|
||||
@@ -129,6 +134,8 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
private final BluetoothProfileState mHfpProfileState;
|
||||
|
||||
private BluetoothA2dpService mA2dpService;
|
||||
private final HashMap<BluetoothDevice, Integer> mInputDevices;
|
||||
|
||||
private static String mDockAddress;
|
||||
private String mDockPin;
|
||||
|
||||
@@ -198,6 +205,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
|
||||
filter.addAction(Intent.ACTION_DOCK_EVENT);
|
||||
mContext.registerReceiver(mReceiver, filter);
|
||||
mInputDevices = new HashMap<BluetoothDevice, Integer>();
|
||||
}
|
||||
|
||||
public static synchronized String readDockBluetoothAddress() {
|
||||
@@ -1220,6 +1228,127 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
|
||||
}
|
||||
|
||||
public synchronized boolean connectInputDevice(BluetoothDevice device) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||
"Need BLUETOOTH_ADMIN permission");
|
||||
|
||||
String objectPath = getObjectPathFromAddress(device.getAddress());
|
||||
if (objectPath == null || getConnectedInputDevices().length != 0 ||
|
||||
getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
|
||||
return false;
|
||||
}
|
||||
if(connectInputDeviceNative(objectPath)) {
|
||||
handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||
"Need BLUETOOTH_ADMIN permission");
|
||||
|
||||
String objectPath = getObjectPathFromAddress(device.getAddress());
|
||||
if (objectPath == null || getConnectedInputDevices().length == 0) {
|
||||
return false;
|
||||
}
|
||||
if(disconnectInputDeviceNative(objectPath)) {
|
||||
handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized int getInputDeviceState(BluetoothDevice device) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
||||
|
||||
if (mInputDevices.get(device) == null) {
|
||||
return BluetoothInputDevice.STATE_DISCONNECTED;
|
||||
}
|
||||
return mInputDevices.get(device.getAddress());
|
||||
}
|
||||
|
||||
public synchronized BluetoothDevice[] getConnectedInputDevices() {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
||||
Set<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
|
||||
new int[] {BluetoothInputDevice.STATE_CONNECTED});
|
||||
return devices.toArray(new BluetoothDevice[devices.size()]);
|
||||
}
|
||||
|
||||
public synchronized int getInputDevicePriority(BluetoothDevice device) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
|
||||
BluetoothInputDevice.PRIORITY_UNDEFINED);
|
||||
}
|
||||
|
||||
public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||
"Need BLUETOOTH_ADMIN permission");
|
||||
if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
|
||||
return false;
|
||||
}
|
||||
return Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
|
||||
priority);
|
||||
}
|
||||
|
||||
/*package*/synchronized Set<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
|
||||
Set<BluetoothDevice> inputDevices = new HashSet<BluetoothDevice>();
|
||||
if (mInputDevices.isEmpty()) {
|
||||
return inputDevices;
|
||||
}
|
||||
for (BluetoothDevice device: mInputDevices.keySet()) {
|
||||
int inputDeviceState = getInputDeviceState(device);
|
||||
for (int state : states) {
|
||||
if (state == inputDeviceState) {
|
||||
inputDevices.add(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputDevices;
|
||||
}
|
||||
|
||||
private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
|
||||
int prevState;
|
||||
if (mInputDevices.get(device) == null) {
|
||||
prevState = BluetoothInputDevice.STATE_DISCONNECTED;
|
||||
} else {
|
||||
prevState = mInputDevices.get(device);
|
||||
}
|
||||
if (prevState == state) return;
|
||||
|
||||
mInputDevices.put(device, state);
|
||||
|
||||
if (getInputDevicePriority(device) >
|
||||
BluetoothInputDevice.PRIORITY_OFF &&
|
||||
state == BluetoothInputDevice.STATE_CONNECTING ||
|
||||
state == BluetoothInputDevice.STATE_CONNECTED) {
|
||||
// We have connected or attempting to connect.
|
||||
// Bump priority
|
||||
setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_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);
|
||||
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
|
||||
|
||||
if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
|
||||
|
||||
}
|
||||
|
||||
/*package*/ void handleInputDevicePropertyChange(String path, boolean connected) {
|
||||
String address = getAddressFromObjectPath(path);
|
||||
if (address == null) return;
|
||||
int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
|
||||
BluetoothInputDevice.STATE_DISCONNECTED;
|
||||
BluetoothDevice device = mAdapter.getRemoteDevice(address);
|
||||
handleInputDeviceStateChange(device, state);
|
||||
}
|
||||
|
||||
/*package*/ boolean isRemoteDeviceInCache(String address) {
|
||||
return (mDeviceProperties.get(address) != null);
|
||||
}
|
||||
@@ -2103,4 +2232,6 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
short channel);
|
||||
private native boolean removeServiceRecordNative(int handle);
|
||||
private native boolean setLinkTimeoutNative(String path, int num_slots);
|
||||
private native boolean connectInputDeviceNative(String path);
|
||||
private native boolean disconnectInputDeviceNative(String path);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,10 @@ static Properties adapter_properties[] = {
|
||||
{"UUIDs", DBUS_TYPE_ARRAY},
|
||||
};
|
||||
|
||||
static Properties input_properties[] = {
|
||||
{"Connected", DBUS_TYPE_BOOLEAN},
|
||||
};
|
||||
|
||||
typedef union {
|
||||
char *str_val;
|
||||
int int_val;
|
||||
@@ -698,6 +702,11 @@ jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg)
|
||||
sizeof(remote_device_properties) / sizeof(Properties));
|
||||
}
|
||||
|
||||
jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg) {
|
||||
return parse_property_change(env, msg, (Properties *) &input_properties,
|
||||
sizeof(input_properties) / sizeof(Properties));
|
||||
}
|
||||
|
||||
jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
|
||||
return parse_properties(env, iter, (Properties *) &adapter_properties,
|
||||
sizeof(adapter_properties) / sizeof(Properties));
|
||||
@@ -708,6 +717,11 @@ jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter)
|
||||
sizeof(remote_device_properties) / sizeof(Properties));
|
||||
}
|
||||
|
||||
jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter) {
|
||||
return parse_properties(env, iter, (Properties *) &input_properties,
|
||||
sizeof(input_properties) / sizeof(Properties));
|
||||
}
|
||||
|
||||
int get_bdaddr(const char *str, bdaddr_t *ba) {
|
||||
char *d = ((char *)ba) + 5, *endp;
|
||||
int i;
|
||||
|
||||
@@ -162,6 +162,8 @@ jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter);
|
||||
jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter);
|
||||
jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
|
||||
jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
|
||||
jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter);
|
||||
jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg);
|
||||
void append_variant(DBusMessageIter *iter, int type, void *val);
|
||||
int get_bdaddr(const char *str, bdaddr_t *ba);
|
||||
void get_bdaddr_as_string(const bdaddr_t *ba, char *str);
|
||||
|
||||
@@ -64,6 +64,8 @@ static jmethodID method_onDisplayPasskey;
|
||||
static jmethodID method_onAgentAuthorize;
|
||||
static jmethodID method_onAgentCancel;
|
||||
|
||||
static jmethodID method_onInputDevicePropertyChanged;
|
||||
|
||||
typedef event_loop_native_data_t native_data_t;
|
||||
|
||||
#define EVENT_LOOP_REFS 10
|
||||
@@ -116,6 +118,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
||||
"(Ljava/lang/String;I)V");
|
||||
method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
|
||||
"(Ljava/lang/String;II)V");
|
||||
method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged",
|
||||
"(Ljava/lang/String;[Ljava/lang/String;)V");
|
||||
|
||||
|
||||
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
|
||||
#endif
|
||||
@@ -853,6 +858,22 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
|
||||
method_onDeviceDisconnectRequested,
|
||||
env->NewStringUTF(remote_device_path));
|
||||
goto success;
|
||||
} else if (dbus_message_is_signal(msg,
|
||||
"org.bluez.Input",
|
||||
"PropertyChanged")) {
|
||||
|
||||
jobjectArray str_array =
|
||||
parse_input_property_change(env, msg);
|
||||
if (str_array != NULL) {
|
||||
const char *c_path = dbus_message_get_path(msg);
|
||||
env->CallVoidMethod(nat->me,
|
||||
method_onInputDevicePropertyChanged,
|
||||
env->NewStringUTF(c_path),
|
||||
str_array);
|
||||
} else {
|
||||
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
|
||||
}
|
||||
goto success;
|
||||
}
|
||||
|
||||
ret = a2dp_event_filter(msg, env);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
|
||||
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
|
||||
#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input"
|
||||
|
||||
#define LOG_TAG "BluetoothService.cpp"
|
||||
|
||||
#include "android_bluetooth_common.h"
|
||||
@@ -881,6 +883,43 @@ static jboolean setLinkTimeoutNative(JNIEnv *env, jobject object, jstring object
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring path) {
|
||||
LOGV(__FUNCTION__);
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
const char *c_path = env->GetStringUTFChars(path, NULL);
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
|
||||
c_path, DBUS_INPUT_IFACE, "Connect",
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
env->ReleaseStringUTFChars(path, c_path);
|
||||
return ret ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object,
|
||||
jstring path) {
|
||||
LOGV(__FUNCTION__);
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
const char *c_path = env->GetStringUTFChars(path, NULL);
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
|
||||
c_path, DBUS_INPUT_IFACE, "Disconnect",
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
env->ReleaseStringUTFChars(path, c_path);
|
||||
return ret ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static JNINativeMethod sMethods[] = {
|
||||
/* name, signature, funcPtr */
|
||||
{"classInitNative", "()V", (void*)classInitNative},
|
||||
@@ -926,6 +965,9 @@ static JNINativeMethod sMethods[] = {
|
||||
{"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative},
|
||||
{"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative},
|
||||
{"setLinkTimeoutNative", "(Ljava/lang/String;I)Z", (void *)setLinkTimeoutNative},
|
||||
// HID functions
|
||||
{"connectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)connectInputDeviceNative},
|
||||
{"disconnectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectInputDeviceNative},
|
||||
};
|
||||
|
||||
int register_android_server_BluetoothService(JNIEnv *env) {
|
||||
|
||||
Reference in New Issue
Block a user