summaryrefslogtreecommitdiffstats
path: root/core/java/android/server
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/server')
-rw-r--r--core/java/android/server/BluetoothA2dpService.java16
-rw-r--r--core/java/android/server/BluetoothBondState.java12
-rw-r--r--core/java/android/server/BluetoothEventLoop.java34
-rw-r--r--core/java/android/server/BluetoothService.java159
4 files changed, 207 insertions, 14 deletions
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 8a6fdb4..fd277d0 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -449,6 +449,22 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
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());
+ }
+
/**
* Called by native code on a PropertyChanged signal from
* org.bluez.AudioSink.
diff --git a/core/java/android/server/BluetoothBondState.java b/core/java/android/server/BluetoothBondState.java
index 76e7885..75f38f9 100644
--- a/core/java/android/server/BluetoothBondState.java
+++ b/core/java/android/server/BluetoothBondState.java
@@ -121,6 +121,8 @@ class BluetoothBondState {
/** reason is ignored unless state == BOND_NOT_BONDED */
public synchronized void setBondState(String address, int state, int reason) {
+ if (DBG) Log.d(TAG, "setBondState " + "address" + " " + state + "reason: " + reason);
+
int oldState = getBondState(address);
if (oldState == state) {
return;
@@ -136,8 +138,10 @@ class BluetoothBondState {
if (state == BluetoothDevice.BOND_BONDED) {
mService.addProfileState(address);
- } else if (state == BluetoothDevice.BOND_NONE) {
- mService.removeProfileState(address);
+ } else if (state == BluetoothDevice.BOND_BONDING) {
+ if (mA2dpProxy == null || mHeadsetProxy == null) {
+ getProfileProxy();
+ }
}
setProfilePriorities(address, state);
@@ -240,6 +244,8 @@ class BluetoothBondState {
}
public synchronized void clearPinAttempts(String address) {
+ if (DBG) Log.d(TAG, "clearPinAttempts: " + address);
+
mPinAttempt.remove(address);
}
@@ -265,6 +271,8 @@ class BluetoothBondState {
} else {
newAttempt = attempt.intValue() + 1;
}
+ if (DBG) Log.d(TAG, "attemp newAttempt: " + newAttempt);
+
mPinAttempt.put(address, new Integer(newAttempt));
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index a220007..63c420a 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -49,6 +49,7 @@ class BluetoothEventLoop {
private boolean mInterrupted;
private final HashMap<String, Integer> mPasskeyAgentRequestData;
+ private final HashMap<String, Integer> mAuthorizationAgentRequestData;
private final BluetoothService mBluetoothService;
private final BluetoothAdapter mAdapter;
private BluetoothA2dp mA2dp;
@@ -110,6 +111,7 @@ class BluetoothEventLoop {
mBluetoothService = bluetoothService;
mContext = context;
mPasskeyAgentRequestData = new HashMap<String, Integer>();
+ mAuthorizationAgentRequestData = new HashMap<String, Integer>();
mAdapter = adapter;
//WakeLock instantiation in BluetoothEventLoop class
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -155,6 +157,10 @@ class BluetoothEventLoop {
return mPasskeyAgentRequestData;
}
+ /* package */ HashMap<String, Integer> getAuthorizationAgentRequestData() {
+ return mAuthorizationAgentRequestData;
+ }
+
/* package */ void start() {
if (!isEventLoopRunningNative()) {
@@ -747,20 +753,22 @@ class BluetoothEventLoop {
*
* @param objectPath the path of the device requesting to be authorized
* @param deviceUuid the UUID of the requesting device
- * @return true if the authorization is allowed; false if not allowed
+ * @param nativeData reference for native data
*/
- private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
- if (!mBluetoothService.isEnabled()) return false;
+ private void onAgentAuthorize(String objectPath, String deviceUuid, int nativeData) {
+ if (!mBluetoothService.isEnabled()) return;
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
- return false;
+ return;
}
boolean authorized = false;
ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
+
BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ mAuthorizationAgentRequestData.put(address, new Integer(nativeData));
// Bluez sends the UUID of the local service being accessed, _not_ the
// remote service
@@ -769,26 +777,29 @@ class BluetoothEventLoop {
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
!isOtherSinkInNonDisconnectedState(address)) {
authorized = mA2dp.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
- if (authorized) {
- Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
+ if (authorized && !BluetoothUuid.isAvrcpTarget(uuid)) {
+ Log.i(TAG, "First check pass for incoming A2DP / AVRCP connection from " + address);
// Some headsets try to connect AVCTP before AVDTP - against the recommendation
// If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
// machine. We don't handle AVCTP signals currently. We only send
// intents for AVDTP state changes. We need to handle both of them in
// some cases. For now, just don't move to incoming state in this case.
- if (!BluetoothUuid.isAvrcpTarget(uuid)) {
- mBluetoothService.notifyIncomingA2dpConnection(address);
- }
+ mBluetoothService.notifyIncomingA2dpConnection(address);
} else {
- Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
+ Log.i(TAG, "" + authorized +
+ "Incoming A2DP / AVRCP connection from " + address);
+ mA2dp.allowIncomingConnect(device, authorized);
}
} else if (mInputDevice != null && BluetoothUuid.isInputDevice(uuid)) {
// We can have more than 1 input device connected.
authorized = mInputDevice.getPriority(device) > BluetoothInputDevice.PRIORITY_OFF;
if (authorized) {
- Log.i(TAG, "Allowing incoming HID connection from " + address);
+ Log.i(TAG, "First check pass for incoming HID connection from " + address);
+ // notify profile state change
+ mBluetoothService.notifyIncomingHidConnection(address);
} else {
Log.i(TAG, "Rejecting incoming HID connection from " + address);
+ mBluetoothService.allowIncomingHidConnect(device, authorized);
}
} else if (BluetoothUuid.isBnep(uuid) && mBluetoothService.allowIncomingTethering()){
authorized = true;
@@ -796,7 +807,6 @@ class BluetoothEventLoop {
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
}
log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
- return authorized;
}
private boolean onAgentOutOfBandDataAvailable(String objectPath) {
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index b5ae298..9839f76 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -59,13 +59,18 @@ import android.util.Log;
import android.util.Pair;
import java.io.BufferedInputStream;
+import java.io.BufferedReader;
import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
+import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -151,6 +156,9 @@ public class BluetoothService extends IBluetooth.Stub {
private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler;
+ private static final String INCOMING_CONNECTION_FILE =
+ "/data/misc/bluetooth/incoming_connection.conf";
+ private HashMap<String, Pair<Integer, String>> mIncomingConnections;
private static class RemoteService {
public String address;
@@ -224,6 +232,7 @@ public class BluetoothService extends IBluetooth.Stub {
mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
+ mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
}
public static synchronized String readDockBluetoothAddress() {
@@ -2069,6 +2078,24 @@ public class BluetoothService extends IBluetooth.Stub {
}
}
+ public boolean allowIncomingHidConnect(BluetoothDevice device, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ String address = device.getAddress();
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ return false;
+ }
+
+ Integer data = getAuthorizationAgentRequestData(address);
+ if (data == null) {
+ Log.w(TAG, "allowIncomingHidConnect(" + device +
+ ") called but no native data available");
+ return false;
+ }
+ if (DBG) log("allowIncomingHidConnect: " + device + " : " + allow + " : " + data);
+ return setAuthorizationNative(address, allow, data.intValue());
+ }
+
/*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
synchronized (mBluetoothInputProfileHandler) {
return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
@@ -2181,6 +2208,17 @@ public class BluetoothService extends IBluetooth.Stub {
}
}
+ /*package*/boolean notifyIncomingHidConnection(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state == null) {
+ return false;
+ }
+ Message msg = new Message();
+ msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING;
+ state.sendMessage(msg);
+ return true;
+ }
+
public boolean connectHeadset(String address) {
if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
@@ -2319,6 +2357,11 @@ public class BluetoothService extends IBluetooth.Stub {
mA2dpService = a2dpService;
}
+ /*package*/ Integer getAuthorizationAgentRequestData(String address) {
+ Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
+ return data;
+ }
+
public void sendProfileStateMessage(int profile, int cmd) {
Message msg = new Message();
msg.what = cmd;
@@ -2422,6 +2465,120 @@ 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) {
+ Log.d(TAG, msg);
+ }
+
private native static void classInitNative();
private native void initializeNativeDataNative();
private native boolean setupNativeDataNative();
@@ -2468,6 +2625,7 @@ public class BluetoothService extends IBluetooth.Stub {
short channel);
private native boolean removeServiceRecordNative(int handle);
private native boolean setLinkTimeoutNative(String path, int num_slots);
+
native boolean connectInputDeviceNative(String path);
native boolean disconnectInputDeviceNative(String path);
@@ -2491,4 +2649,5 @@ public class BluetoothService extends IBluetooth.Stub {
native String getChannelApplicationNative(String channelPath);
native ParcelFileDescriptor getChannelFdNative(String channelPath);
native boolean releaseChannelFdNative(String channelPath);
+ native boolean setAuthorizationNative(String address, boolean value, int data);
}