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