OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / server / BluetoothEventLoop.java
1 /*
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
17 package android.server;
18
19 import android.bluetooth.BluetoothA2dp;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothClass;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothUuid;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.os.ParcelUuid;
29 import android.util.Log;
30
31 import java.util.HashMap;
32 import java.util.Set;
33
34 /**
35  * TODO: Move this to
36  * java/services/com/android/server/BluetoothEventLoop.java
37  * and make the constructor package private again.
38  *
39  * @hide
40  */
41 class BluetoothEventLoop {
42     private static final String TAG = "BluetoothEventLoop";
43     private static final boolean DBG = false;
44
45     private int mNativeData;
46     private Thread mThread;
47     private boolean mStarted;
48     private boolean mInterrupted;
49
50     private final HashMap<String, Integer> mPasskeyAgentRequestData;
51     private final BluetoothService mBluetoothService;
52     private final BluetoothAdapter mAdapter;
53     private final Context mContext;
54
55     private static final int EVENT_RESTART_BLUETOOTH = 1;
56     private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 2;
57     private static final int EVENT_AGENT_CANCEL = 3;
58
59     private static final int CREATE_DEVICE_ALREADY_EXISTS = 1;
60     private static final int CREATE_DEVICE_SUCCESS = 0;
61     private static final int CREATE_DEVICE_FAILED = -1;
62
63     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
64     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
65
66     private final Handler mHandler = new Handler() {
67         @Override
68         public void handleMessage(Message msg) {
69             String address = null;
70             switch (msg.what) {
71             case EVENT_RESTART_BLUETOOTH:
72                 mBluetoothService.restart();
73                 break;
74             case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT:
75                 address = (String)msg.obj;
76                 if (address != null) {
77                     mBluetoothService.setPairingConfirmation(address, true);
78                 }
79                 break;
80             case EVENT_AGENT_CANCEL:
81                 // Set the Bond State to BOND_NONE.
82                 // We always have only 1 device in BONDING state.
83                 String[] devices = mBluetoothService.listInState(BluetoothDevice.BOND_BONDING);
84                 if (devices.length == 0) {
85                     break;
86                 } else if (devices.length > 1) {
87                     Log.e(TAG, " There is more than one device in the Bonding State");
88                     break;
89                 }
90                 address = devices[0];
91                 mBluetoothService.setBondState(address,
92                         BluetoothDevice.BOND_NONE,
93                         BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED);
94                 break;
95             }
96         }
97     };
98
99     static { classInitNative(); }
100     private static native void classInitNative();
101
102     /* package */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
103             BluetoothService bluetoothService) {
104         mBluetoothService = bluetoothService;
105         mContext = context;
106         mPasskeyAgentRequestData = new HashMap();
107         mAdapter = adapter;
108         initializeNativeDataNative();
109     }
110
111     protected void finalize() throws Throwable {
112         try {
113             cleanupNativeDataNative();
114         } finally {
115             super.finalize();
116         }
117     }
118
119     /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
120         return mPasskeyAgentRequestData;
121     }
122
123     /* package */ void start() {
124
125         if (!isEventLoopRunningNative()) {
126             if (DBG) log("Starting Event Loop thread");
127             startEventLoopNative();
128         }
129     }
130
131     public void stop() {
132         if (isEventLoopRunningNative()) {
133             if (DBG) log("Stopping Event Loop thread");
134             stopEventLoopNative();
135         }
136     }
137
138     public boolean isEventLoopRunning() {
139         return isEventLoopRunningNative();
140     }
141
142     private void addDevice(String address, String[] properties) {
143         mBluetoothService.addRemoteDeviceProperties(address, properties);
144         String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI");
145         String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class");
146         String name = mBluetoothService.getRemoteDeviceProperty(address, "Name");
147         short rssiValue;
148         // For incoming connections, we don't get the RSSI value. Use a default of MIN_VALUE.
149         // If we accept the pairing, we will automatically show it at the top of the list.
150         if (rssi != null) {
151             rssiValue = (short)Integer.valueOf(rssi).intValue();
152         } else {
153             rssiValue = Short.MIN_VALUE;
154         }
155         if (classValue != null) {
156             Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
157             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
158             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
159                     new BluetoothClass(Integer.valueOf(classValue)));
160             intent.putExtra(BluetoothDevice.EXTRA_RSSI, rssiValue);
161             intent.putExtra(BluetoothDevice.EXTRA_NAME, name);
162
163             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
164         } else {
165             log ("ClassValue: " + classValue + " for remote device: " + address + " is null");
166         }
167     }
168
169     private void onDeviceFound(String address, String[] properties) {
170         if (properties == null) {
171             Log.e(TAG, "ERROR: Remote device properties are null");
172             return;
173         }
174         addDevice(address, properties);
175     }
176
177     private void onDeviceDisappeared(String address) {
178         Intent intent = new Intent(BluetoothDevice.ACTION_DISAPPEARED);
179         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
180         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
181     }
182
183     private void onDeviceDisconnectRequested(String deviceObjectPath) {
184         String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
185         if (address == null) {
186             Log.e(TAG, "onDeviceDisconnectRequested: Address of the remote device in null");
187             return;
188         }
189         Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
190         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
191         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
192     }
193
194     private void onCreatePairedDeviceResult(String address, int result) {
195         address = address.toUpperCase();
196         mBluetoothService.onCreatePairedDeviceResult(address, result);
197     }
198
199     private void onDeviceCreated(String deviceObjectPath) {
200         String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
201         if (!mBluetoothService.isRemoteDeviceInCache(address)) {
202             // Incoming connection, we haven't seen this device, add to cache.
203             String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
204             if (properties != null) {
205                 addDevice(address, properties);
206             }
207         }
208         return;
209     }
210
211     private void onDeviceRemoved(String deviceObjectPath) {
212         String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
213         if (address != null) {
214             mBluetoothService.setBondState(address.toUpperCase(), BluetoothDevice.BOND_NONE,
215                 BluetoothDevice.UNBOND_REASON_REMOVED);
216             mBluetoothService.setRemoteDeviceProperty(address, "UUIDs", null);
217         }
218     }
219
220     /*package*/ void onPropertyChanged(String[] propValues) {
221         if (mBluetoothService.isAdapterPropertiesEmpty()) {
222             // We have got a property change before
223             // we filled up our cache.
224             mBluetoothService.getAllProperties();
225         }
226         String name = propValues[0];
227         if (name.equals("Name")) {
228             mBluetoothService.setProperty(name, propValues[1]);
229             Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
230             intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]);
231             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
232         } else if (name.equals("Pairable") || name.equals("Discoverable")) {
233             String pairable = name.equals("Pairable") ? propValues[1] :
234                 mBluetoothService.getPropertyInternal("Pairable");
235             String discoverable = name.equals("Discoverable") ? propValues[1] :
236                 mBluetoothService.getPropertyInternal("Discoverable");
237
238             // This shouldn't happen, unless Adapter Properties are null.
239             if (pairable == null || discoverable == null)
240                 return;
241
242             mBluetoothService.setProperty(name, propValues[1]);
243             int mode = BluetoothService.bluezStringToScanMode(
244                     pairable.equals("true"),
245                     discoverable.equals("true"));
246             if (mode >= 0) {
247                 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
248                 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode);
249                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
250                 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
251             }
252         } else if (name.equals("Discovering")) {
253             Intent intent;
254             mBluetoothService.setProperty(name, propValues[1]);
255             if (propValues[1].equals("true")) {
256                 mBluetoothService.setIsDiscovering(true);
257                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
258             } else {
259                 // Stop the discovery.
260                 mBluetoothService.cancelDiscovery();
261                 mBluetoothService.setIsDiscovering(false);
262                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
263             }
264             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
265         } else if (name.equals("Devices")) {
266             String value = null;
267             int len = Integer.valueOf(propValues[1]);
268             if (len > 0) {
269                 StringBuilder str = new StringBuilder();
270                 for (int i = 2; i < propValues.length; i++) {
271                     str.append(propValues[i]);
272                     str.append(",");
273                 }
274                 value = str.toString();
275             }
276             mBluetoothService.setProperty(name, value);
277         } else if (name.equals("Powered")) {
278             // bluetoothd has restarted, re-read all our properties.
279             // Note: bluez only sends this property change when it restarts.
280             if (propValues[1].equals("true"))
281                 onRestartRequired();
282         }
283     }
284
285     private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
286         String name = propValues[0];
287         String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
288         if (address == null) {
289             Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
290             return;
291         }
292         if (DBG) {
293             log("Device property changed:" + address + "property:" + name);
294         }
295         BluetoothDevice device = mAdapter.getRemoteDevice(address);
296         if (name.equals("Name")) {
297             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
298             Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
299             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
300             intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
301             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
302         } else if (name.equals("Class")) {
303             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
304             Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
305             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
306             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
307                     new BluetoothClass(Integer.valueOf(propValues[1])));
308             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
309         } else if (name.equals("Connected")) {
310             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
311             Intent intent = null;
312             if (propValues[1].equals("true")) {
313                 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
314                 // Set the link timeout to 8000 slots (5 sec timeout)
315                 // for bluetooth docks.
316                 if (mBluetoothService.isBluetoothDock(address)) {
317                     mBluetoothService.setLinkTimeout(address, 8000);
318                 }
319             } else {
320                 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
321             }
322             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
323             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
324         } else if (name.equals("UUIDs")) {
325             String uuid = null;
326             int len = Integer.valueOf(propValues[1]);
327             if (len > 0) {
328                 StringBuilder str = new StringBuilder();
329                 for (int i = 2; i < propValues.length; i++) {
330                     str.append(propValues[i]);
331                     str.append(",");
332                 }
333                 uuid = str.toString();
334             }
335             mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
336
337             // UUIDs have changed, query remote service channel and update cache.
338             mBluetoothService.updateDeviceServiceChannelCache(address);
339
340             mBluetoothService.sendUuidIntent(address);
341         } else if (name.equals("Paired")) {
342             if (propValues[1].equals("true")) {
343                 // If locally initiated pairing, we will
344                 // not go to BOND_BONDED state until we have received a
345                 // successful return value in onCreatePairedDeviceResult
346                 if (null == mBluetoothService.getPendingOutgoingBonding()) {
347                     mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDED);
348                 }
349             } else {
350                 mBluetoothService.setBondState(address, BluetoothDevice.BOND_NONE);
351                 mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
352             }
353         } else if (name.equals("Trusted")) {
354             if (DBG)
355                 log("set trust state succeded, value is  " + propValues[1]);
356             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
357         }
358     }
359
360     private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
361         String address = mBluetoothService.getAddressFromObjectPath(objectPath);
362         if (address == null) {
363             Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
364                   "returning null");
365             return null;
366         }
367         address = address.toUpperCase();
368         mPasskeyAgentRequestData.put(address, new Integer(nativeData));
369
370         if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) {
371             // shutdown path
372             mBluetoothService.cancelPairingUserInput(address);
373             return null;
374         }
375         // Set state to BONDING. For incoming connections it will be set here.
376         // For outgoing connections, it gets set when we call createBond.
377         // Also set it only when the state is not already Bonded, we can sometimes
378         // get an authorization request from the remote end if it doesn't have the link key
379         // while we still have it.
380         if (mBluetoothService.getBondState(address) != BluetoothDevice.BOND_BONDED)
381             mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDING);
382         return address;
383     }
384
385     private void onRequestPairingConsent(String objectPath, int nativeData) {
386         String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
387         if (address == null) return;
388
389         /* The link key will not be stored if the incoming request has MITM
390          * protection switched on. Unfortunately, some devices have MITM
391          * switched on even though their capabilities are NoInputNoOutput,
392          * so we may get this request many times. Also if we respond immediately,
393          * the other end is unable to handle it. Delay sending the message.
394          */
395         if (mBluetoothService.getBondState(address) == BluetoothDevice.BOND_BONDED) {
396             Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT);
397             message.obj = address;
398             mHandler.sendMessageDelayed(message, 1500);
399             return;
400         }
401
402         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
403         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
404         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
405                         BluetoothDevice.PAIRING_VARIANT_CONSENT);
406         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
407         return;
408     }
409
410     private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) {
411         String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
412         if (address == null) return;
413
414         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
415         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
416         intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
417         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
418                 BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
419         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
420         return;
421     }
422
423     private void onRequestPasskey(String objectPath, int nativeData) {
424         String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
425         if (address == null) return;
426
427         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
428         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
429         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
430                 BluetoothDevice.PAIRING_VARIANT_PASSKEY);
431         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
432         return;
433     }
434
435     private void onRequestPinCode(String objectPath, int nativeData) {
436         String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
437         if (address == null) return;
438
439         String pendingOutgoingAddress =
440                 mBluetoothService.getPendingOutgoingBonding();
441         if (address.equals(pendingOutgoingAddress)) {
442             // we initiated the bonding
443
444             // Check if its a dock
445             if (mBluetoothService.isBluetoothDock(address)) {
446                 String pin = mBluetoothService.getDockPin();
447                 mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes(pin));
448                 return;
449             }
450
451             BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address));
452
453             // try 0000 once if the device looks dumb
454             switch (btClass.getDeviceClass()) {
455             case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
456             case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
457             case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
458             case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
459             case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
460             case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
461                 if (mBluetoothService.attemptAutoPair(address)) return;
462            }
463         }
464         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
465         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
466         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
467         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
468         return;
469     }
470
471     private void onDisplayPasskey(String objectPath, int passkey, int nativeData) {
472         String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
473         if (address == null) return;
474
475         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
476         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
477         intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
478         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
479                         BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY);
480         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
481     }
482
483     private void onRequestOobData(String objectPath , int nativeData) {
484         String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
485         if (address == null) return;
486
487         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
488         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
489         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
490                 BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT);
491         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
492     }
493
494     private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
495         String address = mBluetoothService.getAddressFromObjectPath(objectPath);
496         if (address == null) {
497             Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
498             return false;
499         }
500
501         boolean authorized = false;
502         ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
503         BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
504
505         // Bluez sends the UUID of the local service being accessed, _not_ the
506         // remote service
507         if (mBluetoothService.isEnabled() &&
508                 (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
509                         || BluetoothUuid.isAdvAudioDist(uuid)) &&
510                         !isOtherSinkInNonDisconnectingState(address)) {
511             BluetoothDevice device = mAdapter.getRemoteDevice(address);
512             authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
513             if (authorized) {
514                 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
515                 // Some headsets try to connect AVCTP before AVDTP - against the recommendation
516                 // If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
517                 // machine.  We don't handle AVCTP signals currently. We only send
518                 // intents for AVDTP state changes. We need to handle both of them in
519                 // some cases. For now, just don't move to incoming state in this case.
520                 if (!BluetoothUuid.isAvrcpTarget(uuid)) {
521                     mBluetoothService.notifyIncomingA2dpConnection(address);
522                 }
523             } else {
524                 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
525             }
526         } else {
527             Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
528         }
529         log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
530         return authorized;
531     }
532
533     private boolean onAgentOutOfBandDataAvailable(String objectPath) {
534         if (!mBluetoothService.isEnabled()) return false;
535
536         String address = mBluetoothService.getAddressFromObjectPath(objectPath);
537         if (address == null) return false;
538
539         if (mBluetoothService.getDeviceOutOfBandData(
540             mAdapter.getRemoteDevice(address)) != null) {
541             return true;
542         }
543         return false;
544
545     }
546
547     private boolean isOtherSinkInNonDisconnectingState(String address) {
548         BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
549         Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
550         if (devices.size() == 0) return false;
551         for(BluetoothDevice dev: devices) {
552             if (!dev.getAddress().equals(address)) return true;
553         }
554         return false;
555     }
556
557     private void onAgentCancel() {
558         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
559         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
560
561         mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_AGENT_CANCEL),
562                    1500);
563
564         return;
565     }
566
567     private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
568         String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
569         // We don't parse the xml here, instead just query Bluez for the properties.
570         if (result) {
571             mBluetoothService.updateRemoteDevicePropertiesCache(address);
572         }
573         mBluetoothService.sendUuidIntent(address);
574         mBluetoothService.makeServiceChannelCallbacks(address);
575     }
576
577     private void onCreateDeviceResult(String address, int result) {
578         if (DBG) log("Result of onCreateDeviceResult:" + result);
579
580         switch (result) {
581         case CREATE_DEVICE_ALREADY_EXISTS:
582             String path = mBluetoothService.getObjectPathFromAddress(address);
583             if (path != null) {
584                 mBluetoothService.discoverServicesNative(path, "");
585                 break;
586             }
587             Log.w(TAG, "Device exists, but we dont have the bluez path, failing");
588             // fall-through
589         case CREATE_DEVICE_FAILED:
590             mBluetoothService.sendUuidIntent(address);
591             mBluetoothService.makeServiceChannelCallbacks(address);
592             break;
593         case CREATE_DEVICE_SUCCESS:
594             // nothing to do, UUID intent's will be sent via property changed
595         }
596     }
597
598     private void onRestartRequired() {
599         if (mBluetoothService.isEnabled()) {
600             Log.e(TAG, "*** A serious error occurred (did bluetoothd crash?) - " +
601                        "restarting Bluetooth ***");
602             mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
603         }
604     }
605
606     private static void log(String msg) {
607         Log.d(TAG, msg);
608     }
609
610     private native void initializeNativeDataNative();
611     private native void startEventLoopNative();
612     private native void stopEventLoopNative();
613     private native boolean isEventLoopRunningNative();
614     private native void cleanupNativeDataNative();
615 }