BluetoothEventLoop.java revision 10eac971b3a6e5f34a420dd68ebfa796553ad2b9
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
17package android.server;
18
19import android.bluetooth.BluetoothA2dp;
20import android.bluetooth.BluetoothAdapter;
21import android.bluetooth.BluetoothClass;
22import android.bluetooth.BluetoothDevice;
23import android.bluetooth.BluetoothUuid;
24import android.bluetooth.ParcelUuid;
25import android.content.Context;
26import android.content.Intent;
27import android.os.Handler;
28import android.os.Message;
29import 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;
46    private boolean mStarted;
47    private boolean mInterrupted;
48
49    private final HashMap<String, Integer> mPasskeyAgentRequestData;
50    private final BluetoothService mBluetoothService;
51    private final BluetoothAdapter mAdapter;
52    private final Context mContext;
53
54    private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1;
55    private static final int EVENT_RESTART_BLUETOOTH = 2;
56    private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3;
57
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) {
71            String address = null;
72            switch (msg.what) {
73            case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
74                address = (String)msg.obj;
75                if (address != null) {
76                    mBluetoothService.createBond(address);
77                    return;
78                }
79                break;
80            case EVENT_RESTART_BLUETOOTH:
81                mBluetoothService.restart();
82                break;
83            case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT:
84                address = (String)msg.obj;
85                if (address != null) {
86                    mBluetoothService.setPairingConfirmation(address, true);
87                }
88                break;
89            }
90        }
91    };
92
93    static { classInitNative(); }
94    private static native void classInitNative();
95
96    /* pacakge */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
97            BluetoothService bluetoothService) {
98        mBluetoothService = bluetoothService;
99        mContext = context;
100        mPasskeyAgentRequestData = new HashMap();
101        mAdapter = adapter;
102        initializeNativeDataNative();
103    }
104
105    protected void finalize() throws Throwable {
106        try {
107            cleanupNativeDataNative();
108        } finally {
109            super.finalize();
110        }
111    }
112
113    /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
114        return mPasskeyAgentRequestData;
115    }
116
117    /* package */ void start() {
118
119        if (!isEventLoopRunningNative()) {
120            if (DBG) log("Starting Event Loop thread");
121            startEventLoopNative();
122        }
123    }
124
125    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();
134    }
135
136    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) {
150            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);
156
157            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
158        } else {
159            log ("ClassValue: " + classValue + " for remote device: " + address + " is null");
160        }
161    }
162
163    private void onDeviceFound(String address, String[] properties) {
164        if (properties == null) {
165            Log.e(TAG, "ERROR: Remote device properties are null");
166            return;
167        }
168        addDevice(address, properties);
169    }
170
171    private void onDeviceDisappeared(String address) {
172        Intent intent = new Intent(BluetoothDevice.ACTION_DISAPPEARED);
173        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
174        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
175    }
176
177    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
188    private void onCreatePairedDeviceResult(String address, int result) {
189        address = address.toUpperCase();
190        if (result == BluetoothDevice.BOND_SUCCESS) {
191            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,
204                                                          BluetoothDevice.BOND_NONE, result);
205            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,
224                    BluetoothDevice.BOND_NONE, result);
225            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,
235                    BluetoothDevice.BOND_NONE, result);
236            return;
237        }
238        mBluetoothService.getBondState().attempt(address);
239    }
240
241    private void onDeviceCreated(String deviceObjectPath) {
242        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        }
250        mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING);
251        return;
252    }
253
254    private void onDeviceRemoved(String deviceObjectPath) {
255        String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
256        if (address != null)
257            mBluetoothService.getBondState().setBondState(address.toUpperCase(),
258                    BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
259    }
260
261    /*package*/ void onPropertyChanged(String[] propValues) {
262        if (mBluetoothService.isAdapterPropertiesEmpty()) {
263            // We have got a property change before
264            // we filled up our cache.
265            mBluetoothService.getAllProperties();
266        }
267        String name = propValues[0];
268        if (name.equals("Name")) {
269            Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
270            intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]);
271            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
283            int mode = BluetoothService.bluezStringToScanMode(
284                    pairable.equals("true"),
285                    discoverable.equals("true"));
286            if (mode >= 0) {
287                Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
288                intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode);
289                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);
297                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
298            } else {
299                // Stop the discovery.
300                mBluetoothService.cancelDiscovery();
301                mBluetoothService.setIsDiscovering(false);
302                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
303            }
304            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
305            mBluetoothService.setProperty(name, propValues[1]);
306        } else if (name.equals("Devices")) {
307            String value = null;
308            int len = Integer.valueOf(propValues[1]);
309            if (len > 0) {
310                StringBuilder str = new StringBuilder();
311                for (int i = 2; i < propValues.length; i++) {
312                    str.append(propValues[i]);
313                    str.append(",");
314                }
315                value = str.toString();
316            }
317            mBluetoothService.setProperty(name, value);
318        } 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        }
324    }
325
326    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        }
333        if (DBG) {
334            log("Device property changed:" + address + "property:" + name);
335        }
336        BluetoothDevice device = mAdapter.getRemoteDevice(address);
337        if (name.equals("Name")) {
338            Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
339            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
340            intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
341            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
342            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
343        } else if (name.equals("Class")) {
344            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])));
348            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")) {
353                intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
354            } else {
355                intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
356            }
357            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
358            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
359            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
360        } else if (name.equals("UUIDs")) {
361            String uuid = null;
362            int len = Integer.valueOf(propValues[1]);
363            if (len > 0) {
364                StringBuilder str = new StringBuilder();
365                for (int i = 2; i < propValues.length; i++) {
366                    str.append(propValues[i]);
367                    str.append(",");
368                }
369                uuid = str.toString();
370            }
371            mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
372
373            // UUIDs have changed, query remote service channel and update cache.
374            mBluetoothService.updateDeviceServiceChannelCache(address);
375
376            mBluetoothService.sendUuidIntent(address);
377        } else if (name.equals("Paired")) {
378            if (propValues[1].equals("true")) {
379                mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
380            } else {
381                mBluetoothService.getBondState().setBondState(address,
382                        BluetoothDevice.BOND_NONE);
383                mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
384            }
385        } else if (name.equals("Trusted")) {
386            if (DBG)
387                log("set trust state succeded, value is  " + propValues[1]);
388            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
389        }
390    }
391
392    private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
393        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
394        if (address == null) {
395            Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
396                  "returning null");
397            return null;
398        }
399        address = address.toUpperCase();
400        mPasskeyAgentRequestData.put(address, new Integer(nativeData));
401
402        if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) {
403            // shutdown path
404            mBluetoothService.cancelPairingUserInput(address);
405            return null;
406        }
407        return address;
408    }
409
410    private void onRequestPairingConsent(String objectPath, int nativeData) {
411        String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
412        if (address == null) return;
413
414        /* The link key will not be stored if the incoming request has MITM
415         * protection switched on. Unfortunately, some devices have MITM
416         * switched on even though their capabilities are NoInputNoOutput,
417         * so we may get this request many times. Also if we respond immediately,
418         * the other end is unable to handle it. Delay sending the message.
419         */
420        if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDED) {
421            Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT);
422            message.obj = address;
423            mHandler.sendMessageDelayed(message, 1500);
424            return;
425        }
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_CONSENT);
431        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
432        return;
433    }
434
435    private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) {
436        String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
437        if (address == null) return;
438
439        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
440        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
441        intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
442        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
443                BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
444        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
445        return;
446    }
447
448    private void onRequestPasskey(String objectPath, int nativeData) {
449        String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
450        if (address == null) return;
451
452        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
453        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
454        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
455                BluetoothDevice.PAIRING_VARIANT_PASSKEY);
456        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
457        return;
458    }
459
460    private void onRequestPinCode(String objectPath, int nativeData) {
461        String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
462        if (address == null) return;
463
464        String pendingOutgoingAddress =
465                mBluetoothService.getBondState().getPendingOutgoingBonding();
466        if (address.equals(pendingOutgoingAddress)) {
467            // we initiated the bonding
468            BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address));
469
470            // try 0000 once if the device looks dumb
471            switch (btClass.getDeviceClass()) {
472            case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
473            case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
474            case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
475            case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
476            case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
477            case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
478                if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) &&
479                    !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) {
480                    mBluetoothService.getBondState().attempt(address);
481                    mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
482                    return;
483                }
484           }
485        }
486        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
487        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
488        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
489        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
490        return;
491    }
492
493    private void onDisplayPasskey(String objectPath, int passkey, int nativeData) {
494        String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
495        if (address == null) return;
496
497        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
498        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
499        intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
500        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
501                        BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY);
502        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
503    }
504
505    private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
506        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
507        if (address == null) {
508            Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
509            return false;
510        }
511
512        boolean authorized = false;
513        ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
514        // Bluez sends the UUID of the local service being accessed, _not_ the
515        // remote service
516        if (mBluetoothService.isEnabled() &&
517                (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
518                        || BluetoothUuid.isAdvAudioDist(uuid))) {
519            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
520            BluetoothDevice device = mAdapter.getRemoteDevice(address);
521            authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
522            if (authorized) {
523                Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
524            } else {
525                Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
526            }
527        } else {
528            Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
529        }
530        log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
531        return authorized;
532    }
533
534    private void onAgentCancel() {
535        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
536        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
537        return;
538    }
539
540    private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
541        String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
542        // We don't parse the xml here, instead just query Bluez for the properties.
543        if (result) {
544            mBluetoothService.updateRemoteDevicePropertiesCache(address);
545        }
546        mBluetoothService.sendUuidIntent(address);
547    }
548
549    private void onCreateDeviceResult(String address, boolean result) {
550        if (DBG) {
551            log("Result of onCreateDeviceResult:" + result);
552        }
553        if (!result) {
554            mBluetoothService.sendUuidIntent(address);
555        }
556    }
557
558    private void onRestartRequired() {
559        if (mBluetoothService.isEnabled()) {
560            Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
561                       "restarting Bluetooth ***");
562            mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
563        }
564    }
565
566    private static void log(String msg) {
567        Log.d(TAG, msg);
568    }
569
570    private native void initializeNativeDataNative();
571    private native void startEventLoopNative();
572    private native void stopEventLoopNative();
573    private native boolean isEventLoopRunningNative();
574    private native void cleanupNativeDataNative();
575}
576