am b38fa2a0: Merge "DO NOT MERGE Incoming Bluetooth Connection requests - dialog." into gingerbread
* commit 'b38fa2a0ac2bde56504929de6a3dd61ac723958e': DO NOT MERGE Incoming Bluetooth Connection requests - dialog.
This commit is contained in:
@@ -269,6 +269,22 @@ public final class BluetoothA2dp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow or disallow incoming connection
|
||||||
|
* @param device Sink
|
||||||
|
* @param value True / False
|
||||||
|
* @return Success or Failure of the binder call.
|
||||||
|
*/
|
||||||
|
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.
|
/** Helper for converting a state to a string.
|
||||||
* For debug use only - strings are not internationalized.
|
* For debug use only - strings are not internationalized.
|
||||||
* @hide
|
* @hide
|
||||||
|
|||||||
@@ -276,6 +276,33 @@ public final class BluetoothDevice implements Parcelable {
|
|||||||
public static final String ACTION_PAIRING_CANCEL =
|
public static final String ACTION_PAIRING_CANCEL =
|
||||||
"android.bluetooth.device.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_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;
|
||||||
|
|
||||||
/** A bond attempt succeeded
|
/** A bond attempt succeeded
|
||||||
* @hide */
|
* @hide */
|
||||||
public static final int BOND_SUCCESS = 0;
|
public static final int BOND_SUCCESS = 0;
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.PowerManager;
|
||||||
import android.server.BluetoothA2dpService;
|
import android.server.BluetoothA2dpService;
|
||||||
import android.server.BluetoothService;
|
import android.server.BluetoothService;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.android.internal.util.HierarchicalState;
|
import com.android.internal.util.HierarchicalState;
|
||||||
import com.android.internal.util.HierarchicalStateMachine;
|
import com.android.internal.util.HierarchicalStateMachine;
|
||||||
@@ -73,9 +75,17 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
public static final int AUTO_CONNECT_PROFILES = 101;
|
public static final int AUTO_CONNECT_PROFILES = 101;
|
||||||
public static final int TRANSITION_TO_STABLE = 102;
|
public static final int TRANSITION_TO_STABLE = 102;
|
||||||
public static final int CONNECT_OTHER_PROFILES = 103;
|
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 AUTO_CONNECT_DELAY = 6000; // 6 secs
|
private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs
|
||||||
private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs
|
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 PREFS_NAME = "ConnectionAccess";
|
||||||
|
|
||||||
private BondedDevice mBondedDevice = new BondedDevice();
|
private BondedDevice mBondedDevice = new BondedDevice();
|
||||||
private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
|
private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
|
||||||
@@ -90,10 +100,16 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
private BluetoothPbap mPbapService;
|
private BluetoothPbap mPbapService;
|
||||||
private boolean mHeadsetServiceConnected;
|
private boolean mHeadsetServiceConnected;
|
||||||
private boolean mPbapServiceConnected;
|
private boolean mPbapServiceConnected;
|
||||||
|
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
|
||||||
|
|
||||||
private BluetoothDevice mDevice;
|
private BluetoothDevice mDevice;
|
||||||
private int mHeadsetState;
|
private int mHeadsetState;
|
||||||
private int mA2dpState;
|
private int mA2dpState;
|
||||||
|
private long mIncomingRejectTimer;
|
||||||
|
private boolean mConnectionAccessReplyReceived = false;
|
||||||
|
private Pair<Integer, String> mIncomingConnections;
|
||||||
|
private PowerManager.WakeLock mWakeLock;
|
||||||
|
private PowerManager mPowerManager;
|
||||||
|
|
||||||
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@@ -108,6 +124,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
int initiator = intent.getIntExtra(
|
int initiator = intent.getIntExtra(
|
||||||
BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
|
BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
|
||||||
BluetoothHeadset.LOCAL_DISCONNECT);
|
BluetoothHeadset.LOCAL_DISCONNECT);
|
||||||
|
// We trust this device now
|
||||||
|
if (newState == BluetoothHeadset.STATE_CONNECTED) {
|
||||||
|
setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
|
||||||
|
}
|
||||||
mHeadsetState = newState;
|
mHeadsetState = newState;
|
||||||
if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
|
if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
|
||||||
initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
|
initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
|
||||||
@@ -121,6 +141,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
|
int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
|
||||||
int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
|
int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
|
||||||
mA2dpState = newState;
|
mA2dpState = newState;
|
||||||
|
// We trust this device now
|
||||||
|
if (newState == BluetoothA2dp.STATE_CONNECTED) {
|
||||||
|
setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
|
||||||
|
}
|
||||||
if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
|
if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
|
||||||
oldState == BluetoothA2dp.STATE_PLAYING) &&
|
oldState == BluetoothA2dp.STATE_PLAYING) &&
|
||||||
newState == BluetoothA2dp.STATE_DISCONNECTED) {
|
newState == BluetoothA2dp.STATE_DISCONNECTED) {
|
||||||
@@ -134,6 +158,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
// This is technically not needed, but we can get stuck sometimes.
|
// This is technically not needed, but we can get stuck sometimes.
|
||||||
// For example, if incoming A2DP fails, we are not informed by Bluez
|
// For example, if incoming A2DP fails, we are not informed by Bluez
|
||||||
sendMessage(TRANSITION_TO_STABLE);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -174,11 +205,20 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
|
filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
|
||||||
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
|
||||||
|
|
||||||
mContext.registerReceiver(mBroadcastReceiver, filter);
|
mContext.registerReceiver(mBroadcastReceiver, filter);
|
||||||
|
|
||||||
HeadsetServiceListener l = new HeadsetServiceListener();
|
HeadsetServiceListener l = new HeadsetServiceListener();
|
||||||
PbapServiceListener p = new PbapServiceListener();
|
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 class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
|
private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
|
||||||
@@ -438,6 +478,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
// Ignore
|
// Ignore
|
||||||
Log.e(TAG, "Error: Incoming connection with a pending incoming connection");
|
Log.e(TAG, "Error: Incoming connection with a pending incoming connection");
|
||||||
break;
|
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:
|
case CONNECT_A2DP_INCOMING:
|
||||||
// Serialize the commands.
|
// Serialize the commands.
|
||||||
deferMessage(message);
|
deferMessage(message);
|
||||||
@@ -607,6 +665,25 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
case CONNECT_A2DP_INCOMING:
|
case CONNECT_A2DP_INCOMING:
|
||||||
// ignore
|
// ignore
|
||||||
break;
|
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:
|
case CONNECT_A2DP_OUTGOING:
|
||||||
// Defer message and retry
|
// Defer message and retry
|
||||||
deferMessage(message);
|
deferMessage(message);
|
||||||
@@ -662,8 +739,138 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
deferMessage(msg);
|
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;
|
||||||
|
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.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) {
|
synchronized boolean processCommand(int command) {
|
||||||
Log.i(TAG, "Processing command:" + command);
|
Log.e(TAG, "Processing command:" + command);
|
||||||
|
Message msg;
|
||||||
switch(command) {
|
switch(command) {
|
||||||
case CONNECT_HFP_OUTGOING:
|
case CONNECT_HFP_OUTGOING:
|
||||||
if (mHeadsetService != null) {
|
if (mHeadsetService != null) {
|
||||||
@@ -673,11 +880,21 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
case CONNECT_HFP_INCOMING:
|
case CONNECT_HFP_INCOMING:
|
||||||
if (!mHeadsetServiceConnected) {
|
if (!mHeadsetServiceConnected) {
|
||||||
deferProfileServiceMessage(command);
|
deferProfileServiceMessage(command);
|
||||||
} else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
|
} else {
|
||||||
return mHeadsetService.acceptIncomingConnect(mDevice);
|
// Check if device is already trusted
|
||||||
} else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
|
int access = getTrust();
|
||||||
handleConnectionOfOtherProfiles(command);
|
if (access == BluetoothDevice.CONNECTION_ACCESS_YES) {
|
||||||
return mHeadsetService.createIncomingConnect(mDevice);
|
handleIncomingConnection(command, true);
|
||||||
|
} else if (access == BluetoothDevice.CONNECTION_ACCESS_NO &&
|
||||||
|
!readIncomingAllowedValue()) {
|
||||||
|
handleIncomingConnection(command, false);
|
||||||
|
} else {
|
||||||
|
sendConnectionAccessIntent();
|
||||||
|
msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY);
|
||||||
|
sendMessageDelayed(msg,
|
||||||
|
CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONNECT_A2DP_OUTGOING:
|
case CONNECT_A2DP_OUTGOING:
|
||||||
@@ -686,8 +903,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONNECT_A2DP_INCOMING:
|
case CONNECT_A2DP_INCOMING:
|
||||||
handleConnectionOfOtherProfiles(command);
|
// Check if device is already trusted
|
||||||
// ignore, Bluez takes care
|
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();
|
||||||
|
msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY);
|
||||||
|
sendMessageDelayed(msg,
|
||||||
|
CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
case DISCONNECT_HFP_OUTGOING:
|
case DISCONNECT_HFP_OUTGOING:
|
||||||
if (!mHeadsetServiceConnected) {
|
if (!mHeadsetServiceConnected) {
|
||||||
@@ -728,6 +956,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UNPAIR:
|
case UNPAIR:
|
||||||
|
writeTimerValue(INIT_INCOMING_REJECT_TIMER);
|
||||||
|
setTrust(CONNECTION_ACCESS_UNDEFINED);
|
||||||
return mService.removeBondInternal(mDevice.getAddress());
|
return mService.removeBondInternal(mDevice.getAddress());
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Error: Unknown Command");
|
Log.e(TAG, "Error: Unknown Command");
|
||||||
|
|||||||
@@ -493,6 +493,23 @@ public final class BluetoothHeadset {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Connect to a Bluetooth Headset.
|
||||||
* Note: This is an internal function and shouldn't be exposed
|
* Note: This is an internal function and shouldn't be exposed
|
||||||
|
|||||||
@@ -36,4 +36,6 @@ interface IBluetoothA2dp {
|
|||||||
|
|
||||||
boolean connectSinkInternal(in BluetoothDevice device);
|
boolean connectSinkInternal(in BluetoothDevice device);
|
||||||
boolean disconnectSinkInternal(in BluetoothDevice device);
|
boolean disconnectSinkInternal(in BluetoothDevice device);
|
||||||
|
boolean allowIncomingConnect(in BluetoothDevice device, boolean value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ interface IBluetoothHeadset {
|
|||||||
|
|
||||||
boolean createIncomingConnect(in BluetoothDevice device);
|
boolean createIncomingConnect(in BluetoothDevice device);
|
||||||
boolean acceptIncomingConnect(in BluetoothDevice device);
|
boolean acceptIncomingConnect(in BluetoothDevice device);
|
||||||
|
boolean rejectIncomingConnect(in BluetoothDevice device);
|
||||||
boolean cancelConnectThread();
|
boolean cancelConnectThread();
|
||||||
boolean connectHeadsetInternal(in BluetoothDevice device);
|
boolean connectHeadsetInternal(in BluetoothDevice device);
|
||||||
boolean disconnectHeadsetInternal(in BluetoothDevice device);
|
boolean disconnectHeadsetInternal(in BluetoothDevice device);
|
||||||
|
|||||||
@@ -457,6 +457,22 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
|
|||||||
Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
|
Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
|
||||||
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||||
|
"Need BLUETOOTH_ADMIN permission");
|
||||||
|
String address = device.getAddress();
|
||||||
|
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Integer data = mBluetoothService.getAuthorizationAgentRequestData(address);
|
||||||
|
if (data == null) {
|
||||||
|
Log.w(TAG, "allowIncomingConnect(" + device + ") called but no native data available");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log("allowIncomingConnect: A2DP: " + device + ":" + value);
|
||||||
|
return mBluetoothService.setAuthorizationNative(address, value, data.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
private synchronized void onSinkPropertyChanged(String path, String []propValues) {
|
private synchronized void onSinkPropertyChanged(String path, String []propValues) {
|
||||||
if (!mBluetoothService.isEnabled()) {
|
if (!mBluetoothService.isEnabled()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class BluetoothEventLoop {
|
|||||||
private boolean mInterrupted;
|
private boolean mInterrupted;
|
||||||
|
|
||||||
private final HashMap<String, Integer> mPasskeyAgentRequestData;
|
private final HashMap<String, Integer> mPasskeyAgentRequestData;
|
||||||
|
private final HashMap<String, Integer> mAuthorizationAgentRequestData;
|
||||||
private final BluetoothService mBluetoothService;
|
private final BluetoothService mBluetoothService;
|
||||||
private final BluetoothAdapter mAdapter;
|
private final BluetoothAdapter mAdapter;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
@@ -109,6 +110,7 @@ class BluetoothEventLoop {
|
|||||||
mBluetoothService = bluetoothService;
|
mBluetoothService = bluetoothService;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPasskeyAgentRequestData = new HashMap();
|
mPasskeyAgentRequestData = new HashMap();
|
||||||
|
mAuthorizationAgentRequestData = new HashMap<String, Integer>();
|
||||||
mAdapter = adapter;
|
mAdapter = adapter;
|
||||||
//WakeLock instantiation in BluetoothEventLoop class
|
//WakeLock instantiation in BluetoothEventLoop class
|
||||||
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
||||||
@@ -130,6 +132,10 @@ class BluetoothEventLoop {
|
|||||||
return mPasskeyAgentRequestData;
|
return mPasskeyAgentRequestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package */ HashMap<String, Integer> getAuthorizationAgentRequestData() {
|
||||||
|
return mAuthorizationAgentRequestData;
|
||||||
|
}
|
||||||
|
|
||||||
/* package */ void start() {
|
/* package */ void start() {
|
||||||
|
|
||||||
if (!isEventLoopRunningNative()) {
|
if (!isEventLoopRunningNative()) {
|
||||||
@@ -518,27 +524,29 @@ class BluetoothEventLoop {
|
|||||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
|
private void onAgentAuthorize(String objectPath, String deviceUuid, int nativeData) {
|
||||||
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
|
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean authorized = false;
|
boolean authorized = false;
|
||||||
ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
|
ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
|
||||||
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
||||||
|
|
||||||
|
BluetoothDevice device = mAdapter.getRemoteDevice(address);
|
||||||
|
mAuthorizationAgentRequestData.put(address, new Integer(nativeData));
|
||||||
|
|
||||||
// Bluez sends the UUID of the local service being accessed, _not_ the
|
// Bluez sends the UUID of the local service being accessed, _not_ the
|
||||||
// remote service
|
// remote service
|
||||||
if (mBluetoothService.isEnabled() &&
|
if (mBluetoothService.isEnabled() &&
|
||||||
(BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|
(BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|
||||||
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
|
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
|
||||||
!isOtherSinkInNonDisconnectingState(address)) {
|
!isOtherSinkInNonDisconnectingState(address)) {
|
||||||
BluetoothDevice device = mAdapter.getRemoteDevice(address);
|
|
||||||
authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
|
authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
|
||||||
if (authorized) {
|
if (authorized) {
|
||||||
Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
|
Log.i(TAG, "First check pass for incoming A2DP / AVRCP connection from " + address);
|
||||||
// Some headsets try to connect AVCTP before AVDTP - against the recommendation
|
// Some headsets try to connect AVCTP before AVDTP - against the recommendation
|
||||||
// If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
|
// If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
|
||||||
// machine. We don't handle AVCTP signals currently. We only send
|
// machine. We don't handle AVCTP signals currently. We only send
|
||||||
@@ -546,6 +554,8 @@ class BluetoothEventLoop {
|
|||||||
// some cases. For now, just don't move to incoming state in this case.
|
// some cases. For now, just don't move to incoming state in this case.
|
||||||
if (!BluetoothUuid.isAvrcpTarget(uuid)) {
|
if (!BluetoothUuid.isAvrcpTarget(uuid)) {
|
||||||
mBluetoothService.notifyIncomingA2dpConnection(address);
|
mBluetoothService.notifyIncomingA2dpConnection(address);
|
||||||
|
} else {
|
||||||
|
a2dp.allowIncomingConnect(device, authorized);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
|
Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
|
||||||
@@ -554,7 +564,7 @@ class BluetoothEventLoop {
|
|||||||
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
|
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
|
||||||
}
|
}
|
||||||
log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
|
log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
|
||||||
return authorized;
|
if (!authorized) a2dp.allowIncomingConnect(device, authorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onAgentOutOfBandDataAvailable(String objectPath) {
|
private boolean onAgentOutOfBandDataAvailable(String objectPath) {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ package android.server;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothClass;
|
import android.bluetooth.BluetoothClass;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothHeadset;
|
|
||||||
import android.bluetooth.BluetoothDeviceProfileState;
|
import android.bluetooth.BluetoothDeviceProfileState;
|
||||||
|
import android.bluetooth.BluetoothHeadset;
|
||||||
import android.bluetooth.BluetoothProfileState;
|
import android.bluetooth.BluetoothProfileState;
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
import android.bluetooth.BluetoothUuid;
|
import android.bluetooth.BluetoothUuid;
|
||||||
@@ -67,6 +67,7 @@ import java.io.FileWriter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -142,6 +143,11 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private static String mDockAddress;
|
private static String mDockAddress;
|
||||||
private String mDockPin;
|
private String mDockPin;
|
||||||
|
|
||||||
|
private static final String INCOMING_CONNECTION_FILE =
|
||||||
|
"/data/misc/bluetooth/incoming_connection.conf";
|
||||||
|
private HashMap<String, Pair<Integer, String>> mIncomingConnections;
|
||||||
|
|
||||||
|
|
||||||
private static class RemoteService {
|
private static class RemoteService {
|
||||||
public String address;
|
public String address;
|
||||||
public ParcelUuid uuid;
|
public ParcelUuid uuid;
|
||||||
@@ -209,6 +215,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
filter.addAction(Intent.ACTION_DOCK_EVENT);
|
filter.addAction(Intent.ACTION_DOCK_EVENT);
|
||||||
mContext.registerReceiver(mReceiver, filter);
|
mContext.registerReceiver(mReceiver, filter);
|
||||||
|
mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized String readDockBluetoothAddress() {
|
public static synchronized String readDockBluetoothAddress() {
|
||||||
@@ -733,8 +740,6 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
if (state == BluetoothDevice.BOND_BONDED) {
|
if (state == BluetoothDevice.BOND_BONDED) {
|
||||||
addProfileState(address);
|
addProfileState(address);
|
||||||
} else if (state == BluetoothDevice.BOND_NONE) {
|
|
||||||
removeProfileState(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
|
if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
|
||||||
@@ -1312,6 +1317,8 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean removeBondInternal(String address) {
|
public synchronized boolean removeBondInternal(String address) {
|
||||||
|
// Unset the trusted device state and then unpair
|
||||||
|
setTrust(address, false);
|
||||||
return removeDeviceNative(getObjectPathFromAddress(address));
|
return removeDeviceNative(getObjectPathFromAddress(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2161,10 +2168,6 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeProfileState(String address) {
|
|
||||||
mDeviceProfileState.remove(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initProfileState() {
|
private void initProfileState() {
|
||||||
String []bonds = null;
|
String []bonds = null;
|
||||||
String val = getPropertyInternal("Devices");
|
String val = getPropertyInternal("Devices");
|
||||||
@@ -2213,6 +2216,11 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
mA2dpService = a2dpService;
|
mA2dpService = a2dpService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ Integer getAuthorizationAgentRequestData(String address) {
|
||||||
|
Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
public void sendProfileStateMessage(int profile, int cmd) {
|
public void sendProfileStateMessage(int profile, int cmd) {
|
||||||
Message msg = new Message();
|
Message msg = new Message();
|
||||||
msg.what = cmd;
|
msg.what = cmd;
|
||||||
@@ -2223,6 +2231,116 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createIncomingConnectionStateFile() {
|
||||||
|
File f = new File(INCOMING_CONNECTION_FILE);
|
||||||
|
if (!f.exists()) {
|
||||||
|
try {
|
||||||
|
f.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "IOException: cannot create file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public Pair<Integer, String> getIncomingState(String address) {
|
||||||
|
if (mIncomingConnections.isEmpty()) {
|
||||||
|
createIncomingConnectionStateFile();
|
||||||
|
readIncomingConnectionState();
|
||||||
|
}
|
||||||
|
return mIncomingConnections.get(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readIncomingConnectionState() {
|
||||||
|
synchronized(mIncomingConnections) {
|
||||||
|
FileInputStream fstream = null;
|
||||||
|
try {
|
||||||
|
fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
|
||||||
|
DataInputStream in = new DataInputStream(fstream);
|
||||||
|
BufferedReader file = new BufferedReader(new InputStreamReader(in));
|
||||||
|
String line;
|
||||||
|
while((line = file.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.length() == 0) continue;
|
||||||
|
String[] value = line.split(",");
|
||||||
|
if (value != null && value.length == 3) {
|
||||||
|
Integer val1 = Integer.parseInt(value[1]);
|
||||||
|
Pair<Integer, String> val = new Pair(val1, value[2]);
|
||||||
|
mIncomingConnections.put(value[0], val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log("FileNotFoundException: readIncomingConnectionState" + e.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log("IOException: readIncomingConnectionState" + e.toString());
|
||||||
|
} finally {
|
||||||
|
if (fstream != null) {
|
||||||
|
try {
|
||||||
|
fstream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void truncateIncomingConnectionFile() {
|
||||||
|
RandomAccessFile r = null;
|
||||||
|
try {
|
||||||
|
r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
|
||||||
|
r.setLength(0);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log("IOException: truncateIncomingConnectionState" + e.toString());
|
||||||
|
} finally {
|
||||||
|
if (r != null) {
|
||||||
|
try {
|
||||||
|
r.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
|
||||||
|
synchronized(mIncomingConnections) {
|
||||||
|
mIncomingConnections.put(address, data);
|
||||||
|
|
||||||
|
truncateIncomingConnectionFile();
|
||||||
|
BufferedWriter out = null;
|
||||||
|
StringBuilder value = new StringBuilder();
|
||||||
|
try {
|
||||||
|
out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
|
||||||
|
for (String devAddress: mIncomingConnections.keySet()) {
|
||||||
|
Pair<Integer, String> val = mIncomingConnections.get(devAddress);
|
||||||
|
value.append(devAddress);
|
||||||
|
value.append(",");
|
||||||
|
value.append(val.first.toString());
|
||||||
|
value.append(",");
|
||||||
|
value.append(val.second);
|
||||||
|
value.append("\n");
|
||||||
|
}
|
||||||
|
out.write(value.toString());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log("IOException: writeIncomingConnectionState" + e.toString());
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void log(String msg) {
|
private static void log(String msg) {
|
||||||
Log.d(TAG, msg);
|
Log.d(TAG, msg);
|
||||||
}
|
}
|
||||||
@@ -2273,4 +2391,5 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
short channel);
|
short channel);
|
||||||
private native boolean removeServiceRecordNative(int handle);
|
private native boolean removeServiceRecordNative(int handle);
|
||||||
private native boolean setLinkTimeoutNative(String path, int num_slots);
|
private native boolean setLinkTimeoutNative(String path, int num_slots);
|
||||||
|
native boolean setAuthorizationNative(String address, boolean value, int data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
|||||||
"(Ljava/lang/String;Z)V");
|
"(Ljava/lang/String;Z)V");
|
||||||
|
|
||||||
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
|
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
"(Ljava/lang/String;Ljava/lang/String;I)V");
|
||||||
method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
|
method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
|
||||||
"(Ljava/lang/String;)Z");
|
"(Ljava/lang/String;)Z");
|
||||||
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
|
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
|
||||||
@@ -917,29 +917,11 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
|
|||||||
LOGV("... object_path = %s", object_path);
|
LOGV("... object_path = %s", object_path);
|
||||||
LOGV("... uuid = %s", uuid);
|
LOGV("... uuid = %s", uuid);
|
||||||
|
|
||||||
bool auth_granted =
|
dbus_message_ref(msg); // increment refcount because we pass to java
|
||||||
env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
|
env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
|
||||||
env->NewStringUTF(object_path), env->NewStringUTF(uuid));
|
env->NewStringUTF(object_path), env->NewStringUTF(uuid),
|
||||||
|
int(msg));
|
||||||
|
|
||||||
// reply
|
|
||||||
if (auth_granted) {
|
|
||||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
|
||||||
if (!reply) {
|
|
||||||
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
dbus_connection_send(nat->conn, reply, NULL);
|
|
||||||
dbus_message_unref(reply);
|
|
||||||
} else {
|
|
||||||
DBusMessage *reply = dbus_message_new_error(msg,
|
|
||||||
"org.bluez.Error.Rejected", "Authorization rejected");
|
|
||||||
if (!reply) {
|
|
||||||
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
dbus_connection_send(nat->conn, reply, NULL);
|
|
||||||
dbus_message_unref(reply);
|
|
||||||
}
|
|
||||||
goto success;
|
goto success;
|
||||||
} else if (dbus_message_is_method_call(msg,
|
} else if (dbus_message_is_method_call(msg,
|
||||||
"org.bluez.Agent", "OutOfBandAvailable")) {
|
"org.bluez.Agent", "OutOfBandAvailable")) {
|
||||||
|
|||||||
@@ -599,6 +599,35 @@ static jboolean setRemoteOutOfBandDataNative(JNIEnv *env, jobject object, jstrin
|
|||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jboolean setAuthorizationNative(JNIEnv *env, jobject object, jstring address,
|
||||||
|
jboolean val, int nativeData) {
|
||||||
|
#ifdef HAVE_BLUETOOTH
|
||||||
|
LOGV(__FUNCTION__);
|
||||||
|
native_data_t *nat = get_native_data(env, object);
|
||||||
|
if (nat) {
|
||||||
|
DBusMessage *msg = (DBusMessage *)nativeData;
|
||||||
|
DBusMessage *reply;
|
||||||
|
if (val) {
|
||||||
|
reply = dbus_message_new_method_return(msg);
|
||||||
|
} else {
|
||||||
|
reply = dbus_message_new_error(msg,
|
||||||
|
"org.bluez.Error.Rejected", "Authorization rejected");
|
||||||
|
}
|
||||||
|
if (!reply) {
|
||||||
|
LOGE("%s: Cannot create message reply D-Bus\n", __FUNCTION__);
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_connection_send(nat->conn, reply, NULL);
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
return JNI_TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
|
static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
|
||||||
jstring pin, int nativeData) {
|
jstring pin, int nativeData) {
|
||||||
#ifdef HAVE_BLUETOOTH
|
#ifdef HAVE_BLUETOOTH
|
||||||
@@ -1029,6 +1058,7 @@ static JNINativeMethod sMethods[] = {
|
|||||||
(void *)setPairingConfirmationNative},
|
(void *)setPairingConfirmationNative},
|
||||||
{"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
|
{"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
|
||||||
{"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
|
{"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
|
||||||
|
{"setAuthorizationNative", "(Ljava/lang/String;ZI)Z", (void *)setAuthorizationNative},
|
||||||
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
|
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
|
||||||
{"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
|
{"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
|
||||||
(void *)cancelPairingUserInputNative},
|
(void *)cancelPairingUserInputNative},
|
||||||
|
|||||||
Reference in New Issue
Block a user