Skip to content

Commit b38fa2a

Browse files
Jaikumar GaneshAndroid (Google) Code Review
authored andcommitted
Merge "DO NOT MERGE Incoming Bluetooth Connection requests - dialog." into gingerbread
2 parents 17523ab + d762f06 commit b38fa2a

11 files changed

Lines changed: 493 additions & 43 deletions

core/java/android/bluetooth/BluetoothA2dp.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,22 @@ public int getSinkPriority(BluetoothDevice device) {
269269
}
270270
}
271271

272+
/**
273+
* Allow or disallow incoming connection
274+
* @param device Sink
275+
* @param value True / False
276+
* @return Success or Failure of the binder call.
277+
*/
278+
public boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
279+
if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")");
280+
try {
281+
return mService.allowIncomingConnect(device, value);
282+
} catch (RemoteException e) {
283+
Log.e(TAG, "", e);
284+
return false;
285+
}
286+
}
287+
272288
/** Helper for converting a state to a string.
273289
* For debug use only - strings are not internationalized.
274290
* @hide

core/java/android/bluetooth/BluetoothDevice.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,33 @@ public final class BluetoothDevice implements Parcelable {
276276
public static final String ACTION_PAIRING_CANCEL =
277277
"android.bluetooth.device.action.PAIRING_CANCEL";
278278

279+
/** @hide */
280+
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
281+
public static final String ACTION_CONNECTION_ACCESS_REQUEST =
282+
"android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
283+
284+
/** @hide */
285+
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
286+
public static final String ACTION_CONNECTION_ACCESS_REPLY =
287+
"android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
288+
289+
/** @hide */
290+
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
291+
public static final String ACTION_CONNECTION_ACCESS_CANCEL =
292+
"android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
293+
/**
294+
* Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent.
295+
* @hide
296+
*/
297+
public static final String EXTRA_CONNECTION_ACCESS_RESULT =
298+
"android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT";
299+
300+
/**@hide*/
301+
public static final int CONNECTION_ACCESS_YES = 1;
302+
303+
/**@hide*/
304+
public static final int CONNECTION_ACCESS_NO = 2;
305+
279306
/** A bond attempt succeeded
280307
* @hide */
281308
public static final int BOND_SUCCESS = 0;

core/java/android/bluetooth/BluetoothDeviceProfileState.java

Lines changed: 238 additions & 8 deletions
Large diffs are not rendered by default.

core/java/android/bluetooth/BluetoothHeadset.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,23 @@ public boolean createIncomingConnect(BluetoothDevice device) {
456456
return false;
457457
}
458458

459+
/**
460+
* Reject the incoming connection.
461+
* @hide
462+
*/
463+
public boolean rejectIncomingConnect(BluetoothDevice device) {
464+
if (DBG) log("rejectIncomingConnect");
465+
if (mService != null) {
466+
try {
467+
return mService.rejectIncomingConnect(device);
468+
} catch (RemoteException e) {Log.e(TAG, e.toString());}
469+
} else {
470+
Log.w(TAG, "Proxy not attached to service");
471+
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
472+
}
473+
return false;
474+
}
475+
459476
/**
460477
* Connect to a Bluetooth Headset.
461478
* Note: This is an internal function and shouldn't be exposed

core/java/android/bluetooth/IBluetoothA2dp.aidl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ interface IBluetoothA2dp {
3636

3737
boolean connectSinkInternal(in BluetoothDevice device);
3838
boolean disconnectSinkInternal(in BluetoothDevice device);
39+
boolean allowIncomingConnect(in BluetoothDevice device, boolean value);
40+
3941
}

core/java/android/bluetooth/IBluetoothHeadset.aidl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface IBluetoothHeadset {
3737

3838
boolean createIncomingConnect(in BluetoothDevice device);
3939
boolean acceptIncomingConnect(in BluetoothDevice device);
40+
boolean rejectIncomingConnect(in BluetoothDevice device);
4041
boolean cancelConnectThread();
4142
boolean connectHeadsetInternal(in BluetoothDevice device);
4243
boolean disconnectHeadsetInternal(in BluetoothDevice device);

core/java/android/server/BluetoothA2dpService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,22 @@ public synchronized boolean setSinkPriority(BluetoothDevice device, int priority
457457
Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
458458
}
459459

460+
public synchronized boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
461+
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
462+
"Need BLUETOOTH_ADMIN permission");
463+
String address = device.getAddress();
464+
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
465+
return false;
466+
}
467+
Integer data = mBluetoothService.getAuthorizationAgentRequestData(address);
468+
if (data == null) {
469+
Log.w(TAG, "allowIncomingConnect(" + device + ") called but no native data available");
470+
return false;
471+
}
472+
log("allowIncomingConnect: A2DP: " + device + ":" + value);
473+
return mBluetoothService.setAuthorizationNative(address, value, data.intValue());
474+
}
475+
460476
private synchronized void onSinkPropertyChanged(String path, String []propValues) {
461477
if (!mBluetoothService.isEnabled()) {
462478
return;

core/java/android/server/BluetoothEventLoop.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class BluetoothEventLoop {
4848
private boolean mInterrupted;
4949

5050
private final HashMap<String, Integer> mPasskeyAgentRequestData;
51+
private final HashMap<String, Integer> mAuthorizationAgentRequestData;
5152
private final BluetoothService mBluetoothService;
5253
private final BluetoothAdapter mAdapter;
5354
private final Context mContext;
@@ -104,6 +105,7 @@ public void handleMessage(Message msg) {
104105
mBluetoothService = bluetoothService;
105106
mContext = context;
106107
mPasskeyAgentRequestData = new HashMap();
108+
mAuthorizationAgentRequestData = new HashMap<String, Integer>();
107109
mAdapter = adapter;
108110
initializeNativeDataNative();
109111
}
@@ -120,6 +122,10 @@ protected void finalize() throws Throwable {
120122
return mPasskeyAgentRequestData;
121123
}
122124

125+
/* package */ HashMap<String, Integer> getAuthorizationAgentRequestData() {
126+
return mAuthorizationAgentRequestData;
127+
}
128+
123129
/* package */ void start() {
124130

125131
if (!isEventLoopRunningNative()) {
@@ -491,34 +497,38 @@ private void onRequestOobData(String objectPath , int nativeData) {
491497
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
492498
}
493499

494-
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
500+
private void onAgentAuthorize(String objectPath, String deviceUuid, int nativeData) {
495501
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
496502
if (address == null) {
497503
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
498-
return false;
504+
return;
499505
}
500506

501507
boolean authorized = false;
502508
ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
503509
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
504510

511+
BluetoothDevice device = mAdapter.getRemoteDevice(address);
512+
mAuthorizationAgentRequestData.put(address, new Integer(nativeData));
513+
505514
// Bluez sends the UUID of the local service being accessed, _not_ the
506515
// remote service
507516
if (mBluetoothService.isEnabled() &&
508517
(BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
509518
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
510519
!isOtherSinkInNonDisconnectingState(address)) {
511-
BluetoothDevice device = mAdapter.getRemoteDevice(address);
512520
authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
513521
if (authorized) {
514-
Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
522+
Log.i(TAG, "First check pass for incoming A2DP / AVRCP connection from " + address);
515523
// Some headsets try to connect AVCTP before AVDTP - against the recommendation
516524
// If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
517525
// machine. We don't handle AVCTP signals currently. We only send
518526
// intents for AVDTP state changes. We need to handle both of them in
519527
// some cases. For now, just don't move to incoming state in this case.
520528
if (!BluetoothUuid.isAvrcpTarget(uuid)) {
521529
mBluetoothService.notifyIncomingA2dpConnection(address);
530+
} else {
531+
a2dp.allowIncomingConnect(device, authorized);
522532
}
523533
} else {
524534
Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
@@ -527,7 +537,7 @@ private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
527537
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
528538
}
529539
log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
530-
return authorized;
540+
if (!authorized) a2dp.allowIncomingConnect(device, authorized);
531541
}
532542

533543
private boolean onAgentOutOfBandDataAvailable(String objectPath) {

core/java/android/server/BluetoothService.java

Lines changed: 126 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import android.bluetooth.BluetoothAdapter;
2828
import android.bluetooth.BluetoothClass;
2929
import android.bluetooth.BluetoothDevice;
30-
import android.bluetooth.BluetoothHeadset;
3130
import android.bluetooth.BluetoothDeviceProfileState;
31+
import android.bluetooth.BluetoothHeadset;
3232
import android.bluetooth.BluetoothProfileState;
3333
import android.bluetooth.BluetoothSocket;
3434
import android.bluetooth.BluetoothUuid;
@@ -67,6 +67,7 @@
6767
import java.io.IOException;
6868
import java.io.InputStreamReader;
6969
import java.io.PrintWriter;
70+
import java.io.RandomAccessFile;
7071
import java.io.UnsupportedEncodingException;
7172
import java.util.ArrayList;
7273
import java.util.Arrays;
@@ -142,6 +143,11 @@ public class BluetoothService extends IBluetooth.Stub {
142143
private static String mDockAddress;
143144
private String mDockPin;
144145

146+
private static final String INCOMING_CONNECTION_FILE =
147+
"/data/misc/bluetooth/incoming_connection.conf";
148+
private HashMap<String, Pair<Integer, String>> mIncomingConnections;
149+
150+
145151
private static class RemoteService {
146152
public String address;
147153
public ParcelUuid uuid;
@@ -209,6 +215,7 @@ public BluetoothService(Context context) {
209215

210216
filter.addAction(Intent.ACTION_DOCK_EVENT);
211217
mContext.registerReceiver(mReceiver, filter);
218+
mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
212219
}
213220

214221
public static synchronized String readDockBluetoothAddress() {
@@ -733,8 +740,6 @@ public synchronized void setBondState(String address, int state, int reason) {
733740

734741
if (state == BluetoothDevice.BOND_BONDED) {
735742
addProfileState(address);
736-
} else if (state == BluetoothDevice.BOND_NONE) {
737-
removeProfileState(address);
738743
}
739744

740745
if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
@@ -1312,6 +1317,8 @@ public synchronized boolean removeBond(String address) {
13121317
}
13131318

13141319
public synchronized boolean removeBondInternal(String address) {
1320+
// Unset the trusted device state and then unpair
1321+
setTrust(address, false);
13151322
return removeDeviceNative(getObjectPathFromAddress(address));
13161323
}
13171324

@@ -2161,10 +2168,6 @@ private BluetoothDeviceProfileState addProfileState(String address) {
21612168
return state;
21622169
}
21632170

2164-
private void removeProfileState(String address) {
2165-
mDeviceProfileState.remove(address);
2166-
}
2167-
21682171
private void initProfileState() {
21692172
String []bonds = null;
21702173
String val = getPropertyInternal("Devices");
@@ -2213,6 +2216,11 @@ public boolean notifyIncomingConnection(String address) {
22132216
mA2dpService = a2dpService;
22142217
}
22152218

2219+
/*package*/ Integer getAuthorizationAgentRequestData(String address) {
2220+
Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
2221+
return data;
2222+
}
2223+
22162224
public void sendProfileStateMessage(int profile, int cmd) {
22172225
Message msg = new Message();
22182226
msg.what = cmd;
@@ -2223,6 +2231,116 @@ public void sendProfileStateMessage(int profile, int cmd) {
22232231
}
22242232
}
22252233

2234+
private void createIncomingConnectionStateFile() {
2235+
File f = new File(INCOMING_CONNECTION_FILE);
2236+
if (!f.exists()) {
2237+
try {
2238+
f.createNewFile();
2239+
} catch (IOException e) {
2240+
Log.e(TAG, "IOException: cannot create file");
2241+
}
2242+
}
2243+
}
2244+
2245+
/** @hide */
2246+
public Pair<Integer, String> getIncomingState(String address) {
2247+
if (mIncomingConnections.isEmpty()) {
2248+
createIncomingConnectionStateFile();
2249+
readIncomingConnectionState();
2250+
}
2251+
return mIncomingConnections.get(address);
2252+
}
2253+
2254+
private void readIncomingConnectionState() {
2255+
synchronized(mIncomingConnections) {
2256+
FileInputStream fstream = null;
2257+
try {
2258+
fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
2259+
DataInputStream in = new DataInputStream(fstream);
2260+
BufferedReader file = new BufferedReader(new InputStreamReader(in));
2261+
String line;
2262+
while((line = file.readLine()) != null) {
2263+
line = line.trim();
2264+
if (line.length() == 0) continue;
2265+
String[] value = line.split(",");
2266+
if (value != null && value.length == 3) {
2267+
Integer val1 = Integer.parseInt(value[1]);
2268+
Pair<Integer, String> val = new Pair(val1, value[2]);
2269+
mIncomingConnections.put(value[0], val);
2270+
}
2271+
}
2272+
} catch (FileNotFoundException e) {
2273+
log("FileNotFoundException: readIncomingConnectionState" + e.toString());
2274+
} catch (IOException e) {
2275+
log("IOException: readIncomingConnectionState" + e.toString());
2276+
} finally {
2277+
if (fstream != null) {
2278+
try {
2279+
fstream.close();
2280+
} catch (IOException e) {
2281+
// Ignore
2282+
}
2283+
}
2284+
}
2285+
}
2286+
}
2287+
2288+
private void truncateIncomingConnectionFile() {
2289+
RandomAccessFile r = null;
2290+
try {
2291+
r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
2292+
r.setLength(0);
2293+
} catch (FileNotFoundException e) {
2294+
log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
2295+
} catch (IOException e) {
2296+
log("IOException: truncateIncomingConnectionState" + e.toString());
2297+
} finally {
2298+
if (r != null) {
2299+
try {
2300+
r.close();
2301+
} catch (IOException e) {
2302+
// ignore
2303+
}
2304+
}
2305+
}
2306+
}
2307+
2308+
/** @hide */
2309+
public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
2310+
synchronized(mIncomingConnections) {
2311+
mIncomingConnections.put(address, data);
2312+
2313+
truncateIncomingConnectionFile();
2314+
BufferedWriter out = null;
2315+
StringBuilder value = new StringBuilder();
2316+
try {
2317+
out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
2318+
for (String devAddress: mIncomingConnections.keySet()) {
2319+
Pair<Integer, String> val = mIncomingConnections.get(devAddress);
2320+
value.append(devAddress);
2321+
value.append(",");
2322+
value.append(val.first.toString());
2323+
value.append(",");
2324+
value.append(val.second);
2325+
value.append("\n");
2326+
}
2327+
out.write(value.toString());
2328+
} catch (FileNotFoundException e) {
2329+
log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
2330+
} catch (IOException e) {
2331+
log("IOException: writeIncomingConnectionState" + e.toString());
2332+
} finally {
2333+
if (out != null) {
2334+
try {
2335+
out.close();
2336+
} catch (IOException e) {
2337+
// Ignore
2338+
}
2339+
}
2340+
}
2341+
}
2342+
}
2343+
22262344
private static void log(String msg) {
22272345
Log.d(TAG, msg);
22282346
}
@@ -2273,4 +2391,5 @@ private native int addRfcommServiceRecordNative(String name, long uuidMsb, long
22732391
short channel);
22742392
private native boolean removeServiceRecordNative(int handle);
22752393
private native boolean setLinkTimeoutNative(String path, int num_slots);
2394+
native boolean setAuthorizationNative(String address, boolean value, int data);
22762395
}

0 commit comments

Comments
 (0)