blob: 1ed5c49e592e8fa3d53ceddaf149647a8f75cd15 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
25import android.content.Intent;
26import android.os.Handler;
27import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.Log;
29
30import java.util.HashMap;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070031import java.util.UUID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
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 }
Nick Pellybd022f42009-08-14 18:33:38 -0700333 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700334 if (name.equals("Name")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700335 Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
336 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
337 intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700338 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
339 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
340 } else if (name.equals("Class")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700341 Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
342 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
343 intent.putExtra(BluetoothDevice.EXTRA_CLASS,
344 new BluetoothClass(Integer.valueOf(propValues[1])));
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700345 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
346 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
347 } else if (name.equals("Connected")) {
348 Intent intent = null;
349 if (propValues[1].equals("true")) {
Nick Pelly005b2282009-09-10 10:21:56 -0700350 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700351 } else {
Nick Pelly005b2282009-09-10 10:21:56 -0700352 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700353 }
Nick Pelly005b2282009-09-10 10:21:56 -0700354 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700355 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
356 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
357 } else if (name.equals("UUIDs")) {
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700358 String uuid = null;
359 int len = Integer.valueOf(propValues[1]);
360 if (len > 0) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700361 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700362 for (int i = 2; i < propValues.length; i++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700363 str.append(propValues[i]);
364 str.append(",");
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700365 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700366 uuid = str.toString();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700367 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700368 mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700369 } else if (name.equals("Paired")) {
370 if (propValues[1].equals("true")) {
371 mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
372 } else {
373 mBluetoothService.getBondState().setBondState(address,
Nick Pelly005b2282009-09-10 10:21:56 -0700374 BluetoothDevice.BOND_NONE);
Lixin Yueefa1dd72009-08-31 15:55:13 +0800375 mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700376 }
Lixin Yueefa1dd72009-08-31 15:55:13 +0800377 } else if (name.equals("Trusted")) {
378 if (DBG)
379 log("set trust state succeded, value is " + propValues[1]);
380 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700381 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700382 }
383
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700384 private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700385 String address = mBluetoothService.getAddressFromObjectPath(objectPath);
386 if (address == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700387 Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
388 "returning null");
389 return null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700390 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 address = address.toUpperCase();
392 mPasskeyAgentRequestData.put(address, new Integer(nativeData));
393
Nick Pellyde893f52009-09-08 13:15:33 -0700394 if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700395 // shutdown path
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700396 mBluetoothService.cancelPairingUserInput(address);
397 return null;
The Android Open Source Project10592532009-03-18 17:39:46 -0700398 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700399 return address;
400 }
401
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700402 private void onRequestPairingConsent(String objectPath, int nativeData) {
403 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
404 if (address == null) return;
405
406 /* The link key will not be stored if the incoming request has MITM
407 * protection switched on. Unfortunately, some devices have MITM
408 * switched on even though their capabilities are NoInputNoOutput,
409 * so we may get this request many times. Also if we respond immediately,
410 * the other end is unable to handle it. Delay sending the message.
411 */
412 if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDED) {
413 Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT);
414 message.obj = address;
415 mHandler.sendMessageDelayed(message, 1500);
416 return;
417 }
418
419 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
420 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
421 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
422 BluetoothDevice.PAIRING_VARIANT_CONSENT);
423 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
424 return;
425 }
426
427 private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700428 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
429 if (address == null) return;
430
Nick Pelly005b2282009-09-10 10:21:56 -0700431 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
432 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
433 intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
434 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700435 BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700436 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
437 return;
438 }
439
440 private void onRequestPasskey(String objectPath, int nativeData) {
441 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
442 if (address == null) return;
443
Nick Pelly005b2282009-09-10 10:21:56 -0700444 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
445 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
446 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
447 BluetoothDevice.PAIRING_VARIANT_PASSKEY);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700448 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
449 return;
450 }
451
452 private void onRequestPinCode(String objectPath, int nativeData) {
453 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
454 if (address == null) return;
The Android Open Source Project10592532009-03-18 17:39:46 -0700455
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 if (mBluetoothService.getBondState().getBondState(address) ==
457 BluetoothDevice.BOND_BONDING) {
458 // we initiated the bonding
Nick Pelly005b2282009-09-10 10:21:56 -0700459 BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460
461 // try 0000 once if the device looks dumb
Nick Pelly005b2282009-09-10 10:21:56 -0700462 switch (btClass.getDeviceClass()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
464 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
465 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
466 case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
467 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
468 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
469 if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) &&
470 !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) {
471 mBluetoothService.getBondState().attempt(address);
472 mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
473 return;
474 }
475 }
476 }
Nick Pelly005b2282009-09-10 10:21:56 -0700477 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
478 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
479 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700481 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
483
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700484 private void onDisplayPasskey(String objectPath, int passkey, int nativeData) {
485 String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
486 if (address == null) return;
487
488 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
489 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
490 intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
491 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
492 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY);
493 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
494 }
495
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700496 private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
497 String address = mBluetoothService.getAddressFromObjectPath(objectPath);
498 if (address == null) {
499 Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
500 return false;
501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 boolean authorized = false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700504 UUID uuid = UUID.fromString(deviceUuid);
Nick Pellyb23d4452009-08-26 09:33:40 -0700505 // Bluez sends the UUID of the local service being accessed, _not_ the
506 // remote service
Jaikumar Ganeshade40522009-07-30 13:32:25 -0700507 if (mBluetoothService.isEnabled() &&
Nick Pellyb23d4452009-08-26 09:33:40 -0700508 (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700509 || BluetoothUuid.isAdvAudioDist(uuid))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
Nick Pellybd022f42009-08-14 18:33:38 -0700511 BluetoothDevice device = mAdapter.getRemoteDevice(address);
512 authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 if (authorized) {
Jaikumar Ganeshade40522009-07-30 13:32:25 -0700514 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 } else {
Jaikumar Ganeshade40522009-07-30 13:32:25 -0700516 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518 } else {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700519 Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 }
Nick Pellyb23d4452009-08-26 09:33:40 -0700521 log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 return authorized;
523 }
524
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700525 private void onAgentCancel() {
Nick Pelly005b2282009-09-10 10:21:56 -0700526 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700527 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
528 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700531 private void onRestartRequired() {
532 if (mBluetoothService.isEnabled()) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700533 Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
534 "restarting Bluetooth ***");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700535 mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
536 }
537 }
538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 private static void log(String msg) {
540 Log.d(TAG, msg);
541 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700542
543 private native void initializeNativeDataNative();
544 private native void startEventLoopNative();
545 private native void stopEventLoopNative();
546 private native boolean isEventLoopRunningNative();
547 private native void cleanupNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548}