blob: ba0a0d44a519ee7e7f647cf154adcde660dca71f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.server;
18
19import android.bluetooth.BluetoothA2dp;
Nick Pellybd022f42009-08-14 18:33:38 -070020import android.bluetooth.BluetoothAdapter;
Jaikumar Ganesh32d85712009-09-10 22:00:05 -070021import android.bluetooth.BluetoothClass;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.bluetooth.BluetoothDevice;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070023import android.bluetooth.BluetoothUuid;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -070024import android.bluetooth.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
27import android.os.Handler;
28import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.util.Log;
30
31import java.util.HashMap;
32
33/**
34 * TODO: Move this to
35 * java/services/com/android/server/BluetoothEventLoop.java
36 * and make the contructor package private again.
37 *
38 * @hide
39 */
40class BluetoothEventLoop {
41 private static final String TAG = "BluetoothEventLoop";
42 private static final boolean DBG = false;
43
44 private int mNativeData;
45 private Thread mThread;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -070046 private boolean mStarted;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 private boolean mInterrupted;
Nick Pellybd022f42009-08-14 18:33:38 -070048
The Android Open Source Project10592532009-03-18 17:39:46 -070049 private final HashMap<String, Integer> mPasskeyAgentRequestData;
Nick Pellybd022f42009-08-14 18:33:38 -070050 private final BluetoothService mBluetoothService;
51 private final BluetoothAdapter mAdapter;
The Android Open Source Project10592532009-03-18 17:39:46 -070052 private final Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54 private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070055 private static final int EVENT_RESTART_BLUETOOTH = 2;
Jaikumar Ganesh32d85712009-09-10 22:00:05 -070056 private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
58 // The time (in millisecs) to delay the pairing attempt after the first
59 // auto pairing attempt fails. We use an exponential delay with
60 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
61 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
62 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
63 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
64
65 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
66 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
67
68 private final Handler mHandler = new Handler() {
69 @Override
70 public void handleMessage(Message msg) {
Jaikumar Ganesh32d85712009-09-10 22:00:05 -070071 String address = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 switch (msg.what) {
73 case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
Jaikumar Ganesh32d85712009-09-10 22:00:05 -070074 address = (String)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 if (address != null) {
76 mBluetoothService.createBond(address);
77 return;
78 }
79 break;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070080 case EVENT_RESTART_BLUETOOTH:
Nick Pelly37b5a102009-03-24 20:46:18 -070081 mBluetoothService.restart();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 break;
Jaikumar Ganesh32d85712009-09-10 22:00:05 -070083 case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT:
84 address = (String)msg.obj;
85 if (address != null) {
86 mBluetoothService.setPairingConfirmation(address, true);
87 }
88 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 }
90 }
91 };
92
93 static { classInitNative(); }
94 private static native void classInitNative();
95
Nick Pellybd022f42009-08-14 18:33:38 -070096 /* pacakge */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
97 BluetoothService bluetoothService) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 mBluetoothService = bluetoothService;
99 mContext = context;
100 mPasskeyAgentRequestData = new HashMap();
Nick Pellybd022f42009-08-14 18:33:38 -0700101 mAdapter = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 initializeNativeDataNative();
103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104
105 protected void finalize() throws Throwable {
106 try {
107 cleanupNativeDataNative();
108 } finally {
109 super.finalize();
110 }
111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700113 /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 return mPasskeyAgentRequestData;
115 }
116
Robert Greenwalt28d139f2009-04-02 22:41:08 -0700117 /* package */ void start() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118
Robert Greenwalt28d139f2009-04-02 22:41:08 -0700119 if (!isEventLoopRunningNative()) {
120 if (DBG) log("Starting Event Loop thread");
121 startEventLoopNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 }
123 }
124
Robert Greenwalt28d139f2009-04-02 22:41:08 -0700125 public void stop() {
126 if (isEventLoopRunningNative()) {
127 if (DBG) log("Stopping Event Loop thread");
128 stopEventLoopNative();
129 }
130 }
131
132 public boolean isEventLoopRunning() {
133 return isEventLoopRunningNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 }
135
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700136 private void addDevice(String address, String[] properties) {
137 mBluetoothService.addRemoteDeviceProperties(address, properties);
138 String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI");
139 String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class");
140 String name = mBluetoothService.getRemoteDeviceProperty(address, "Name");
141 short rssiValue;
142 // For incoming connections, we don't get the RSSI value. Use a default of MIN_VALUE.
143 // If we accept the pairing, we will automatically show it at the top of the list.
144 if (rssi != null) {
145 rssiValue = (short)Integer.valueOf(rssi).intValue();
146 } else {
147 rssiValue = Short.MIN_VALUE;
148 }
149 if (classValue != null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700150 Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
151 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
152 intent.putExtra(BluetoothDevice.EXTRA_CLASS,
153 new BluetoothClass(Integer.valueOf(classValue)));
154 intent.putExtra(BluetoothDevice.EXTRA_RSSI, rssiValue);
155 intent.putExtra(BluetoothDevice.EXTRA_NAME, name);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700156
157 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
158 } else {
159 log ("ClassValue: " + classValue + " for remote device: " + address + " is null");
160 }
161 }
162
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700163 private void onDeviceFound(String address, String[] properties) {
164 if (properties == null) {
165 Log.e(TAG, "ERROR: Remote device properties are null");
166 return;
167 }
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700168 addDevice(address, properties);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 }
170
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700171 private void onDeviceDisappeared(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -0700172 Intent intent = new Intent(BluetoothDevice.ACTION_DISAPPEARED);
173 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176
Jaikumar Ganesh5e59ca82009-09-11 12:16:19 -0700177 private void onDeviceDisconnectRequested(String deviceObjectPath) {
178 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
179 if (address == null) {
180 Log.e(TAG, "onDeviceDisconnectRequested: Address of the remote device in null");
181 return;
182 }
183 Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
184 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
185 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
186 }
187
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700188 private void onCreatePairedDeviceResult(String address, int result) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 address = address.toUpperCase();
Nick Pellyb24e11b2009-09-08 17:40:43 -0700190 if (result == BluetoothDevice.BOND_SUCCESS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
192 if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
193 mBluetoothService.getBondState().clearPinAttempts(address);
194 }
195 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
196 mBluetoothService.getBondState().getAttempt(address) == 1) {
197 mBluetoothService.getBondState().addAutoPairingFailure(address);
198 pairingAttempt(address, result);
199 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
200 mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
201 pairingAttempt(address, result);
202 } else {
203 mBluetoothService.getBondState().setBondState(address,
Nick Pelly005b2282009-09-10 10:21:56 -0700204 BluetoothDevice.BOND_NONE, result);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
206 mBluetoothService.getBondState().clearPinAttempts(address);
207 }
208 }
209 }
210
211 private void pairingAttempt(String address, int result) {
212 // This happens when our initial guess of "0000" as the pass key
213 // fails. Try to create the bond again and display the pin dialog
214 // to the user. Use back-off while posting the delayed
215 // message. The initial value is
216 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
217 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
218 // reached, display an error to the user.
219 int attempt = mBluetoothService.getBondState().getAttempt(address);
220 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
221 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
222 mBluetoothService.getBondState().clearPinAttempts(address);
223 mBluetoothService.getBondState().setBondState(address,
Nick Pelly005b2282009-09-10 10:21:56 -0700224 BluetoothDevice.BOND_NONE, result);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 return;
226 }
227
228 Message message = mHandler.obtainMessage(EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
229 message.obj = address;
230 boolean postResult = mHandler.sendMessageDelayed(message,
231 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
232 if (!postResult) {
233 mBluetoothService.getBondState().clearPinAttempts(address);
234 mBluetoothService.getBondState().setBondState(address,
Nick Pelly005b2282009-09-10 10:21:56 -0700235 BluetoothDevice.BOND_NONE, result);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 return;
237 }
238 mBluetoothService.getBondState().attempt(address);
239 }
240
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700241 private void onDeviceCreated(String deviceObjectPath) {
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700242 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
243 if (!mBluetoothService.isRemoteDeviceInCache(address)) {
244 // Incoming connection, we haven't seen this device, add to cache.
245 String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
246 if (properties != null) {
247 addDevice(address, properties);
248 }
249 }
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700250 mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700251 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 }
253
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700254 private void onDeviceRemoved(String deviceObjectPath) {
255 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
256 if (address != null)
257 mBluetoothService.getBondState().setBondState(address.toUpperCase(),
Nick Pelly005b2282009-09-10 10:21:56 -0700258 BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 }
260
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700261 /*package*/ void onPropertyChanged(String[] propValues) {
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -0700262 if (mBluetoothService.isAdapterPropertiesEmpty()) {
263 // We have got a property change before
264 // we filled up our cache.
265 mBluetoothService.getAllProperties();
266 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700267 String name = propValues[0];
268 if (name.equals("Name")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700269 Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
270 intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700271 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
272 mBluetoothService.setProperty(name, propValues[1]);
273 } else if (name.equals("Pairable") || name.equals("Discoverable")) {
274 String pairable = name.equals("Pairable") ? propValues[1] :
275 mBluetoothService.getProperty("Pairable");
276 String discoverable = name.equals("Discoverable") ? propValues[1] :
277 mBluetoothService.getProperty("Discoverable");
278
279 // This shouldn't happen, unless Adapter Properties are null.
280 if (pairable == null || discoverable == null)
281 return;
282
Nick Pellybd022f42009-08-14 18:33:38 -0700283 int mode = BluetoothService.bluezStringToScanMode(
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700284 pairable.equals("true"),
285 discoverable.equals("true"));
286 if (mode >= 0) {
Nick Pellyde893f52009-09-08 13:15:33 -0700287 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
288 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700289 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
290 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
291 }
292 mBluetoothService.setProperty(name, propValues[1]);
293 } else if (name.equals("Discovering")) {
294 Intent intent;
295 if (propValues[1].equals("true")) {
296 mBluetoothService.setIsDiscovering(true);
Nick Pelly005b2282009-09-10 10:21:56 -0700297 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700298 } else {
299 // Stop the discovery.
300 mBluetoothService.cancelDiscovery();
301 mBluetoothService.setIsDiscovering(false);
Nick Pelly005b2282009-09-10 10:21:56 -0700302 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700303 }
304 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
305 mBluetoothService.setProperty(name, propValues[1]);
306 } else if (name.equals("Devices")) {
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700307 String value = null;
308 int len = Integer.valueOf(propValues[1]);
309 if (len > 0) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700310 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700311 for (int i = 2; i < propValues.length; i++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700312 str.append(propValues[i]);
313 str.append(",");
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700314 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700315 value = str.toString();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700316 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700317 mBluetoothService.setProperty(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700318 } else if (name.equals("Powered")) {
319 // bluetoothd has restarted, re-read all our properties.
320 // Note: bluez only sends this property change when it restarts.
321 if (propValues[1].equals("true"))
322 onRestartRequired();
323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 }
325
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700326 private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
327 String name = propValues[0];
328 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
329 if (address == null) {
330 Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
331 return;
332 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700333 if (DBG) {
334 log("Device property changed:" + address + "property:" + name);
335 }
Nick Pellybd022f42009-08-14 18:33:38 -0700336 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700337 if (name.equals("Name")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700338 Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
339 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
340 intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700341 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
342 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
343 } else if (name.equals("Class")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700344 Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
345 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
346 intent.putExtra(BluetoothDevice.EXTRA_CLASS,
347 new BluetoothClass(Integer.valueOf(propValues[1])));
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700348 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
349 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
350 } else if (name.equals("Connected")) {
351 Intent intent = null;
352 if (propValues[1].equals("true")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700353 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700354 } else {
Nick Pelly005b2282009-09-10 10:21:56 -0700355 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700356 }
Nick Pelly005b2282009-09-10 10:21:56 -0700357 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700358 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
359 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
360 } else if (name.equals("UUIDs")) {
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700361 String uuid = null;
362 int len = Integer.valueOf(propValues[1]);
363 if (len > 0) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700364 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700365 for (int i = 2; i < propValues.length; i++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700366 str.append(propValues[i]);
367 str.append(",");
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700368 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700369 uuid = str.toString();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700370 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700371 mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700372 mBluetoothService.sendUuidIntent(address);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700373 } else if (name.equals("Paired")) {
374 if (propValues[1].equals("true")) {
375 mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
376 } else {
377 mBluetoothService.getBondState().setBondState(address,
Nick Pelly005b2282009-09-10 10:21:56 -0700378 BluetoothDevice.BOND_NONE);
Lixin Yueefa1dd72009-08-31 15:55:13 +0800379 mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700380 }
Lixin Yueefa1dd72009-08-31 15:55:13 +0800381 } else if (name.equals("Trusted")) {
382 if (DBG)
383 log("set trust state succeded, value is " + propValues[1]);
384 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700385 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700386 }
387
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700388 private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700389 String address = mBluetoothService.getAddressFromObjectPath(objectPath);
390 if (address == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700391 Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
392 "returning null");
393 return null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700394 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 address = address.toUpperCase();
396 mPasskeyAgentRequestData.put(address, new Integer(nativeData));
397
Nick Pellyde893f52009-09-08 13:15:33 -0700398 if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700399 // shutdown path
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700400 mBluetoothService.cancelPairingUserInput(address);
401 return null;
The Android Open Source Project10592532009-03-18 17:39:46 -0700402 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700403 return address;
404 }
405
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700406 private void onRequestPairingConsent(String objectPath, int nativeData) {
407 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
408 if (address == null) return;
409
410 /* The link key will not be stored if the incoming request has MITM
411 * protection switched on. Unfortunately, some devices have MITM
412 * switched on even though their capabilities are NoInputNoOutput,
413 * so we may get this request many times. Also if we respond immediately,
414 * the other end is unable to handle it. Delay sending the message.
415 */
416 if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDED) {
417 Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT);
418 message.obj = address;
419 mHandler.sendMessageDelayed(message, 1500);
420 return;
421 }
422
423 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
424 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
425 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
426 BluetoothDevice.PAIRING_VARIANT_CONSENT);
427 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
428 return;
429 }
430
431 private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700432 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
433 if (address == null) return;
434
Nick Pelly005b2282009-09-10 10:21:56 -0700435 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
436 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
437 intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
438 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700439 BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700440 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
441 return;
442 }
443
444 private void onRequestPasskey(String objectPath, int nativeData) {
445 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
446 if (address == null) return;
447
Nick Pelly005b2282009-09-10 10:21:56 -0700448 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
449 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
450 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
451 BluetoothDevice.PAIRING_VARIANT_PASSKEY);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700452 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
453 return;
454 }
455
456 private void onRequestPinCode(String objectPath, int nativeData) {
457 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
458 if (address == null) return;
The Android Open Source Project10592532009-03-18 17:39:46 -0700459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 if (mBluetoothService.getBondState().getBondState(address) ==
461 BluetoothDevice.BOND_BONDING) {
462 // we initiated the bonding
Nick Pelly005b2282009-09-10 10:21:56 -0700463 BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464
465 // try 0000 once if the device looks dumb
Nick Pelly005b2282009-09-10 10:21:56 -0700466 switch (btClass.getDeviceClass()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
468 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
469 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
470 case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
471 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
472 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
473 if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) &&
474 !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) {
475 mBluetoothService.getBondState().attempt(address);
476 mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
477 return;
478 }
479 }
480 }
Nick Pelly005b2282009-09-10 10:21:56 -0700481 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
482 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
483 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700485 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700488 private void onDisplayPasskey(String objectPath, int passkey, int nativeData) {
489 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
490 if (address == null) return;
491
492 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
493 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
494 intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
495 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
496 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY);
497 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
498 }
499
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700500 private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
501 String address = mBluetoothService.getAddressFromObjectPath(objectPath);
502 if (address == null) {
503 Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
504 return false;
505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 boolean authorized = false;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700508 ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
Nick Pellyb23d4452009-08-26 09:33:40 -0700509 // Bluez sends the UUID of the local service being accessed, _not_ the
510 // remote service
Jaikumar Ganeshade40522009-07-30 13:32:25 -0700511 if (mBluetoothService.isEnabled() &&
Nick Pellyb23d4452009-08-26 09:33:40 -0700512 (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700513 || BluetoothUuid.isAdvAudioDist(uuid))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
Nick Pellybd022f42009-08-14 18:33:38 -0700515 BluetoothDevice device = mAdapter.getRemoteDevice(address);
516 authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 if (authorized) {
Jaikumar Ganeshade40522009-07-30 13:32:25 -0700518 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 } else {
Jaikumar Ganeshade40522009-07-30 13:32:25 -0700520 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 }
522 } else {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700523 Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
Nick Pellyb23d4452009-08-26 09:33:40 -0700525 log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 return authorized;
527 }
528
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700529 private void onAgentCancel() {
Nick Pelly005b2282009-09-10 10:21:56 -0700530 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700531 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
532 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 }
534
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700535 private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
536 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
537 // We don't parse the xml here, instead just query Bluez for the properties.
538 if (result) {
539 String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
540 mBluetoothService.addRemoteDeviceProperties(address, properties);
541 }
542 mBluetoothService.sendUuidIntent(address);
543 }
544
545 private void onCreateDeviceResult(String address, boolean result) {
546 if (DBG) {
547 log("Result of onCreateDeviceResult:" + result);
548 }
549 if (!result) {
550 mBluetoothService.sendUuidIntent(address);
551 }
552 }
553
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700554 private void onRestartRequired() {
555 if (mBluetoothService.isEnabled()) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700556 Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
557 "restarting Bluetooth ***");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700558 mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
559 }
560 }
561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 private static void log(String msg) {
563 Log.d(TAG, msg);
564 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700565
566 private native void initializeNativeDataNative();
567 private native void startEventLoopNative();
568 private native void stopEventLoopNative();
569 private native boolean isEventLoopRunningNative();
570 private native void cleanupNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571}