Home | History | Annotate | Download | only in server
      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 }
    616