diff options
Diffstat (limited to 'core/java/android/server')
| -rw-r--r-- | core/java/android/server/BluetoothA2dpService.java | 16 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothBondState.java | 12 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 34 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothService.java | 159 |
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); } |
