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