summaryrefslogtreecommitdiffstats
path: root/core/java/android/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/bluetooth')
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java19
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java64
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java275
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java17
-rw-r--r--core/java/android/bluetooth/BluetoothInputDevice.java22
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl1
-rw-r--r--core/java/android/bluetooth/IBluetoothA2dp.aidl2
-rw-r--r--core/java/android/bluetooth/IBluetoothHeadset.aidl1
8 files changed, 389 insertions, 12 deletions
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 61d3707..96f3290 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -423,7 +423,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
return false;
}
- /**
+ /**
+ * Allow or disallow incoming connection
+ * @param device Sink
+ * @param value True / False
+ * @return Success or Failure of the binder call.
+ * @hide
+ */
+ public boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
+ if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")");
+ try {
+ return mService.allowIncomingConnect(device, value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 254e2f8..d9525a3 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -276,6 +276,70 @@ public final class BluetoothDevice implements Parcelable {
public static final String ACTION_PAIRING_CANCEL =
"android.bluetooth.device.action.PAIRING_CANCEL";
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_ACCESS_REQUEST =
+ "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
+
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_ACCESS_REPLY =
+ "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
+
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_ACCESS_CANCEL =
+ "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
+
+ /**
+ * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent.
+ * @hide
+ */
+ public static final String EXTRA_ACCESS_REQUEST_TYPE =
+ "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE";
+
+ /**@hide*/
+ public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1;
+
+ /**@hide*/
+ public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2;
+
+ /**
+ * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
+ * Contains package name to return reply intent to.
+ * @hide
+ */
+ public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME";
+
+ /**
+ * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
+ * Contains class name to return reply intent to.
+ * @hide
+ */
+ public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME";
+
+ /**
+ * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent.
+ * @hide
+ */
+ public static final String EXTRA_CONNECTION_ACCESS_RESULT =
+ "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT";
+
+ /**@hide*/
+ public static final int CONNECTION_ACCESS_YES = 1;
+
+ /**@hide*/
+ public static final int CONNECTION_ACCESS_NO = 2;
+
+ /**
+ * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents,
+ * Contains boolean to indicate if the allowed response is once-for-all so that
+ * next request will be granted without asking user again.
+ * @hide
+ */
+ public static final String EXTRA_ALWAYS_ALLOWED =
+ "android.bluetooth.device.extra.ALWAYS_ALLOWED";
+
/**
* A bond attempt succeeded
* @hide
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 56f236d..ab3a426 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -22,9 +22,11 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Message;
import android.bluetooth.BluetoothAdapter;
+import android.os.PowerManager;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -81,8 +83,18 @@ public final class BluetoothDeviceProfileState extends StateMachine {
public static final int AUTO_CONNECT_PROFILES = 101;
public static final int TRANSITION_TO_STABLE = 102;
public static final int CONNECT_OTHER_PROFILES = 103;
+ private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104;
+ private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105;
private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs
+ private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs
+ private static final int CONNECTION_ACCESS_UNDEFINED = -1;
+ private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec
+ private static final long MAX_INCOMING_REJECT_TIMER = 3600 * 1000 * 4; // 4 hours
+
+ private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
+ private static final String ACCESS_AUTHORITY_CLASS =
+ "com.android.settings.bluetooth.BluetoothPermissionRequest";
private BondedDevice mBondedDevice = new BondedDevice();
private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
@@ -98,10 +110,16 @@ public final class BluetoothDeviceProfileState extends StateMachine {
private BluetoothHeadset mHeadsetService;
private BluetoothPbap mPbapService;
private boolean mPbapServiceConnected;
+ private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
private BluetoothDevice mDevice;
private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED;
+ private long mIncomingRejectTimer;
+ private boolean mConnectionAccessReplyReceived = false;
+ private Pair<Integer, String> mIncomingConnections;
+ private PowerManager.WakeLock mWakeLock;
+ private PowerManager mPowerManager;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -113,6 +131,10 @@ public final class BluetoothDeviceProfileState extends StateMachine {
if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+ // We trust this device now
+ if (newState == BluetoothHeadset.STATE_CONNECTED) {
+ setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
+ }
mA2dpState = newState;
if (oldState == BluetoothA2dp.STATE_CONNECTED &&
newState == BluetoothA2dp.STATE_DISCONNECTED) {
@@ -125,7 +147,10 @@ public final class BluetoothDeviceProfileState extends StateMachine {
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
-
+ // We trust this device now
+ if (newState == BluetoothHeadset.STATE_CONNECTED) {
+ setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
+ }
mHeadsetState = newState;
if (oldState == BluetoothHeadset.STATE_CONNECTED &&
newState == BluetoothHeadset.STATE_DISCONNECTED) {
@@ -139,7 +164,10 @@ public final class BluetoothDeviceProfileState extends StateMachine {
int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
int oldState =
intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
-
+ // We trust this device now
+ if (newState == BluetoothHeadset.STATE_CONNECTED) {
+ setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
+ }
if (oldState == BluetoothProfile.STATE_CONNECTED &&
newState == BluetoothProfile.STATE_DISCONNECTED) {
sendMessage(DISCONNECT_HID_INCOMING);
@@ -152,8 +180,15 @@ public final class BluetoothDeviceProfileState extends StateMachine {
// This is technically not needed, but we can get stuck sometimes.
// For example, if incoming A2DP fails, we are not informed by Bluez
sendMessage(TRANSITION_TO_STABLE);
+ } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
+ mWakeLock.release();
+ int val = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
+ BluetoothDevice.CONNECTION_ACCESS_NO);
+ Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY);
+ msg.arg1 = val;
+ sendMessage(msg);
}
- }
+ }
};
private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) {
@@ -195,6 +230,7 @@ public final class BluetoothDeviceProfileState extends StateMachine {
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
mContext.registerReceiver(mBroadcastReceiver, filter);
@@ -203,6 +239,14 @@ public final class BluetoothDeviceProfileState extends StateMachine {
BluetoothProfile.HEADSET);
// TODO(): Convert PBAP to the new Profile APIs.
PbapServiceListener p = new PbapServiceListener();
+
+ mIncomingConnections = mService.getIncomingState(address);
+ mIncomingRejectTimer = readTimerValue();
+ mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK |
+ PowerManager.ACQUIRE_CAUSES_WAKEUP |
+ PowerManager.ON_AFTER_RELEASE, TAG);
+ mWakeLock.setReferenceCounted(false);
}
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
@@ -497,6 +541,24 @@ public final class BluetoothDeviceProfileState extends StateMachine {
// Ignore
Log.e(TAG, "Error: Incoming connection with a pending incoming connection");
break;
+ case CONNECTION_ACCESS_REQUEST_REPLY:
+ int val = message.arg1;
+ mConnectionAccessReplyReceived = true;
+ boolean value = false;
+ if (val == BluetoothDevice.CONNECTION_ACCESS_YES) {
+ value = true;
+ }
+ setTrust(val);
+
+ handleIncomingConnection(CONNECT_HFP_INCOMING, value);
+ break;
+ case CONNECTION_ACCESS_REQUEST_EXPIRY:
+ if (!mConnectionAccessReplyReceived) {
+ handleIncomingConnection(CONNECT_HFP_INCOMING, false);
+ sendConnectionAccessRemovalIntent();
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ break;
case CONNECT_A2DP_INCOMING:
// Serialize the commands.
deferMessage(message);
@@ -690,6 +752,25 @@ public final class BluetoothDeviceProfileState extends StateMachine {
case CONNECT_A2DP_INCOMING:
// ignore
break;
+ case CONNECTION_ACCESS_REQUEST_REPLY:
+ int val = message.arg1;
+ mConnectionAccessReplyReceived = true;
+ boolean value = false;
+ if (val == BluetoothDevice.CONNECTION_ACCESS_YES) {
+ value = true;
+ }
+ setTrust(val);
+ handleIncomingConnection(CONNECT_A2DP_INCOMING, value);
+ break;
+ case CONNECTION_ACCESS_REQUEST_EXPIRY:
+ // The check protects the race condition between REQUEST_REPLY
+ // and the timer expiry.
+ if (!mConnectionAccessReplyReceived) {
+ handleIncomingConnection(CONNECT_A2DP_INCOMING, false);
+ sendConnectionAccessRemovalIntent();
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ break;
case CONNECT_A2DP_OUTGOING:
// Defer message and retry
deferMessage(message);
@@ -847,6 +928,20 @@ public final class BluetoothDeviceProfileState extends StateMachine {
case DISCONNECT_HID_OUTGOING:
deferMessage(message);
break;
+ case CONNECTION_ACCESS_REQUEST_REPLY:
+ mConnectionAccessReplyReceived = true;
+ int val = message.arg1;
+ setTrust(val);
+ handleIncomingConnection(CONNECT_HID_INCOMING,
+ val == BluetoothDevice.CONNECTION_ACCESS_YES);
+ break;
+ case CONNECTION_ACCESS_REQUEST_EXPIRY:
+ if (!mConnectionAccessReplyReceived) {
+ handleIncomingConnection(CONNECT_HID_INCOMING, false);
+ sendConnectionAccessRemovalIntent();
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ break;
case DISCONNECT_HFP_INCOMING:
// Shouldn't happen but if does, we can handle it.
// Depends if the headset can handle it.
@@ -891,8 +986,150 @@ public final class BluetoothDeviceProfileState extends StateMachine {
deferMessage(msg);
}
+ private void updateIncomingAllowedTimer() {
+ // Not doing a perfect exponential backoff because
+ // we want two different rates. For all practical
+ // purposes, this is good enough.
+ if (mIncomingRejectTimer == 0) mIncomingRejectTimer = INIT_INCOMING_REJECT_TIMER;
+
+ mIncomingRejectTimer *= 5;
+ if (mIncomingRejectTimer > MAX_INCOMING_REJECT_TIMER) {
+ mIncomingRejectTimer = MAX_INCOMING_REJECT_TIMER;
+ }
+ writeTimerValue(mIncomingRejectTimer);
+ }
+
+ private boolean handleIncomingConnection(int command, boolean accept) {
+ boolean ret = false;
+ Log.i(TAG, "handleIncomingConnection:" + command + ":" + accept);
+ switch (command) {
+ case CONNECT_HFP_INCOMING:
+ if (!accept) {
+ ret = mHeadsetService.rejectIncomingConnect(mDevice);
+ sendMessage(TRANSITION_TO_STABLE);
+ updateIncomingAllowedTimer();
+ } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
+ writeTimerValue(0);
+ ret = mHeadsetService.acceptIncomingConnect(mDevice);
+ } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
+ writeTimerValue(0);
+ handleConnectionOfOtherProfiles(command);
+ ret = mHeadsetService.createIncomingConnect(mDevice);
+ }
+ break;
+ case CONNECT_A2DP_INCOMING:
+ if (!accept) {
+ ret = mA2dpService.allowIncomingConnect(mDevice, false);
+ sendMessage(TRANSITION_TO_STABLE);
+ updateIncomingAllowedTimer();
+ } else {
+ writeTimerValue(0);
+ ret = mA2dpService.allowIncomingConnect(mDevice, true);
+ handleConnectionOfOtherProfiles(command);
+ }
+ break;
+ case CONNECT_HID_INCOMING:
+ if (!accept) {
+ ret = mService.allowIncomingHidConnect(mDevice, false);
+ sendMessage(TRANSITION_TO_STABLE);
+ updateIncomingAllowedTimer();
+ } else {
+ writeTimerValue(0);
+ ret = mService.allowIncomingHidConnect(mDevice, true);
+ }
+ break;
+ default:
+ Log.e(TAG, "Waiting for incoming connection but state changed to:" + command);
+ break;
+ }
+ return ret;
+ }
+
+ private void sendConnectionAccessIntent() {
+ mConnectionAccessReplyReceived = false;
+
+ if (!mPowerManager.isScreenOn()) mWakeLock.acquire();
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
+ intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
+ intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
+ BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ }
+
+ private void sendConnectionAccessRemovalIntent() {
+ mWakeLock.release();
+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ }
+
+ private int getTrust() {
+ String address = mDevice.getAddress();
+ if (mIncomingConnections != null) return mIncomingConnections.first;
+ return CONNECTION_ACCESS_UNDEFINED;
+ }
+
+
+ private String getStringValue(long value) {
+ StringBuilder sbr = new StringBuilder();
+ sbr.append(Long.toString(System.currentTimeMillis()));
+ sbr.append("-");
+ sbr.append(Long.toString(value));
+ return sbr.toString();
+ }
+
+ private void setTrust(int value) {
+ String second;
+ if (mIncomingConnections == null) {
+ second = getStringValue(INIT_INCOMING_REJECT_TIMER);
+ } else {
+ second = mIncomingConnections.second;
+ }
+
+ mIncomingConnections = new Pair(value, second);
+ mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections);
+ }
+
+ private void writeTimerValue(long value) {
+ Integer first;
+ if (mIncomingConnections == null) {
+ first = CONNECTION_ACCESS_UNDEFINED;
+ } else {
+ first = mIncomingConnections.first;
+ }
+ mIncomingConnections = new Pair(first, getStringValue(value));
+ mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections);
+ }
+
+ private long readTimerValue() {
+ if (mIncomingConnections == null)
+ return 0;
+ String value = mIncomingConnections.second;
+ String[] splits = value.split("-");
+ if (splits != null && splits.length == 2) {
+ return Long.parseLong(splits[1]);
+ }
+ return 0;
+ }
+
+ private boolean readIncomingAllowedValue() {
+ if (readTimerValue() == 0) return true;
+ String value = mIncomingConnections.second;
+ String[] splits = value.split("-");
+ if (splits != null && splits.length == 2) {
+ long val1 = Long.parseLong(splits[0]);
+ long val2 = Long.parseLong(splits[1]);
+ if (val1 + val2 <= System.currentTimeMillis()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
synchronized boolean processCommand(int command) {
- Log.i(TAG, "Processing command:" + command);
+ log("Processing command:" + command);
switch(command) {
case CONNECT_HFP_OUTGOING:
if (mHeadsetService == null) {
@@ -904,11 +1141,9 @@ public final class BluetoothDeviceProfileState extends StateMachine {
case CONNECT_HFP_INCOMING:
if (mHeadsetService == null) {
deferProfileServiceMessage(command);
- } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
- return mHeadsetService.acceptIncomingConnect(mDevice);
- } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
- handleConnectionOfOtherProfiles(command);
- return mHeadsetService.createIncomingConnect(mDevice);
+ } else {
+ processIncomingConnectCommand(command);
+ return true;
}
break;
case CONNECT_A2DP_OUTGOING:
@@ -917,12 +1152,12 @@ public final class BluetoothDeviceProfileState extends StateMachine {
}
break;
case CONNECT_A2DP_INCOMING:
- handleConnectionOfOtherProfiles(command);
- // ignore, Bluez takes care
+ processIncomingConnectCommand(command);
return true;
case CONNECT_HID_OUTGOING:
return mService.connectInputDeviceInternal(mDevice);
case CONNECT_HID_INCOMING:
+ processIncomingConnectCommand(command);
return true;
case DISCONNECT_HFP_OUTGOING:
if (mHeadsetService == null) {
@@ -972,6 +1207,8 @@ public final class BluetoothDeviceProfileState extends StateMachine {
}
break;
case UNPAIR:
+ writeTimerValue(INIT_INCOMING_REJECT_TIMER);
+ setTrust(CONNECTION_ACCESS_UNDEFINED);
return mService.removeBondInternal(mDevice.getAddress());
default:
Log.e(TAG, "Error: Unknown Command");
@@ -979,6 +1216,22 @@ public final class BluetoothDeviceProfileState extends StateMachine {
return false;
}
+ private void processIncomingConnectCommand(int command) {
+ // Check if device is already trusted
+ int access = getTrust();
+ if (access == BluetoothDevice.CONNECTION_ACCESS_YES) {
+ handleIncomingConnection(command, true);
+ } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO &&
+ !readIncomingAllowedValue()) {
+ handleIncomingConnection(command, false);
+ } else {
+ sendConnectionAccessIntent();
+ Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY);
+ sendMessageDelayed(msg,
+ CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT);
+ }
+ }
+
private void handleConnectionOfOtherProfiles(int command) {
// The white paper recommendations mentions that when there is a
// link loss, it is the responsibility of the remote device to connect.
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 23724f2..3284361 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -618,6 +618,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
/**
+ * Reject the incoming connection.
+ * @hide
+ */
+ public boolean rejectIncomingConnect(BluetoothDevice device) {
+ if (DBG) log("rejectIncomingConnect");
+ if (mService != null) {
+ try {
+ return mService.rejectIncomingConnect(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
* Connect to a Bluetooth Headset.
* Note: This is an internal function and shouldn't be exposed
*
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
index 282b70a..f6757d9 100644
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -308,6 +308,28 @@ public final class BluetoothInputDevice implements BluetoothProfile {
return BluetoothProfile.PRIORITY_OFF;
}
+ /**
+ * Allow or disallow incoming connection
+ * @param device Input device
+ * @param allow true / false
+ * @return Success or Failure of the operation
+ * @hide
+ */
+ public boolean allowIncomingConnect(BluetoothDevice device, boolean allow) {
+ if (DBG) log("allowIncomingConnect(" + device + ", " + allow + ")");
+
+ if (mService == null || !isEnabled() || !isValidDevice(device)) {
+ return false;
+ }
+ try {
+ mService.allowIncomingHidConnect(device, allow);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ return true;
+ }
+
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 28b09b6..6ca6c2e 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -91,6 +91,7 @@ interface IBluetooth
int getInputDeviceConnectionState(in BluetoothDevice device);
boolean setInputDevicePriority(in BluetoothDevice device, int priority);
int getInputDevicePriority(in BluetoothDevice device);
+ boolean allowIncomingHidConnect(in BluetoothDevice device, boolean value);
boolean isTetheringOn();
void setBluetoothTethering(boolean value);
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index b4fc366..444dd1e 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -39,4 +39,6 @@ interface IBluetoothA2dp {
boolean resumeSink(in BluetoothDevice device);
boolean connectSinkInternal(in BluetoothDevice device);
boolean disconnectSinkInternal(in BluetoothDevice device);
+ boolean allowIncomingConnect(in BluetoothDevice device, boolean value);
+
}
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 273cda7..ec00527 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -42,6 +42,7 @@ interface IBluetoothHeadset {
// Internal functions, not be made public
boolean createIncomingConnect(in BluetoothDevice device);
boolean acceptIncomingConnect(in BluetoothDevice device);
+ boolean rejectIncomingConnect(in BluetoothDevice device);
boolean cancelConnectThread();
boolean connectHeadsetInternal(in BluetoothDevice device);
boolean disconnectHeadsetInternal(in BluetoothDevice device);