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 /**
     18  * TODO: Move this to
     19  * java/services/com/android/server/BluetoothService.java
     20  * and make the contructor package private again.
     21  *
     22  * @hide
     23  */
     24 
     25 package android.server;
     26 
     27 import android.bluetooth.BluetoothAdapter;
     28 import android.bluetooth.BluetoothClass;
     29 import android.bluetooth.BluetoothDevice;
     30 import android.bluetooth.BluetoothDeviceProfileState;
     31 import android.bluetooth.BluetoothHeadset;
     32 import android.bluetooth.BluetoothHealthAppConfiguration;
     33 import android.bluetooth.BluetoothInputDevice;
     34 import android.bluetooth.BluetoothPan;
     35 import android.bluetooth.BluetoothProfile;
     36 import android.bluetooth.BluetoothProfileState;
     37 import android.bluetooth.BluetoothSocket;
     38 import android.bluetooth.BluetoothUuid;
     39 import android.bluetooth.IBluetooth;
     40 import android.bluetooth.IBluetoothCallback;
     41 import android.bluetooth.IBluetoothHealthCallback;
     42 import android.bluetooth.IBluetoothStateChangeCallback;
     43 import android.content.BroadcastReceiver;
     44 import android.content.ContentResolver;
     45 import android.content.Context;
     46 import android.content.Intent;
     47 import android.content.IntentFilter;
     48 import android.content.SharedPreferences;
     49 import android.content.res.Resources;
     50 import android.os.Binder;
     51 import android.os.Handler;
     52 import android.os.IBinder;
     53 import android.os.Message;
     54 import android.os.ParcelFileDescriptor;
     55 import android.os.ParcelUuid;
     56 import android.os.RemoteException;
     57 import android.os.ServiceManager;
     58 import android.provider.Settings;
     59 import android.util.Log;
     60 import android.util.Pair;
     61 
     62 import com.android.internal.app.IBatteryStats;
     63 
     64 import java.io.BufferedInputStream;
     65 import java.io.BufferedReader;
     66 import java.io.BufferedWriter;
     67 import java.io.DataInputStream;
     68 import java.io.File;
     69 import java.io.FileDescriptor;
     70 import java.io.FileInputStream;
     71 import java.io.FileNotFoundException;
     72 import java.io.FileWriter;
     73 import java.io.IOException;
     74 import java.io.InputStreamReader;
     75 import java.io.PrintWriter;
     76 import java.io.RandomAccessFile;
     77 import java.io.UnsupportedEncodingException;
     78 import java.util.ArrayList;
     79 import java.util.Arrays;
     80 import java.util.Collection;
     81 import java.util.Collections;
     82 import java.util.HashMap;
     83 import java.util.Iterator;
     84 import java.util.List;
     85 import java.util.Map;
     86 
     87 public class BluetoothService extends IBluetooth.Stub {
     88     private static final String TAG = "BluetoothService";
     89     private static final boolean DBG = true;
     90 
     91     private int mNativeData;
     92     private BluetoothEventLoop mEventLoop;
     93     private BluetoothHeadset mHeadsetProxy;
     94     private BluetoothInputDevice mInputDevice;
     95     private BluetoothPan mPan;
     96     private boolean mIsAirplaneSensitive;
     97     private boolean mIsAirplaneToggleable;
     98     private BluetoothAdapterStateMachine mBluetoothState;
     99     private int[] mAdapterSdpHandles;
    100     private ParcelUuid[] mAdapterUuids;
    101 
    102     private BluetoothAdapter mAdapter;  // constant after init()
    103     private final BluetoothBondState mBondState;  // local cache of bondings
    104     private final IBatteryStats mBatteryStats;
    105     private final Context mContext;
    106     private Map<Integer, IBluetoothStateChangeCallback> mStateChangeTracker =
    107         Collections.synchronizedMap(new HashMap<Integer, IBluetoothStateChangeCallback>());
    108 
    109     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
    110     static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
    111 
    112     private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
    113     private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
    114 
    115     private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
    116     private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
    117 
    118     private static final int MESSAGE_UUID_INTENT = 1;
    119     private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
    120     private static final int MESSAGE_REMOVE_SERVICE_RECORD = 3;
    121 
    122     private static final int RFCOMM_RECORD_REAPER = 10;
    123     private static final int STATE_CHANGE_REAPER = 11;
    124 
    125     // The time (in millisecs) to delay the pairing attempt after the first
    126     // auto pairing attempt fails. We use an exponential delay with
    127     // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
    128     // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
    129     private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
    130     private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
    131 
    132     // The timeout used to sent the UUIDs Intent
    133     // This timeout should be greater than the page timeout
    134     private static final int UUID_INTENT_DELAY = 6000;
    135 
    136     /** Always retrieve RFCOMM channel for these SDP UUIDs */
    137     private static final ParcelUuid[] RFCOMM_UUIDS = {
    138             BluetoothUuid.Handsfree,
    139             BluetoothUuid.HSP,
    140             BluetoothUuid.ObexObjectPush };
    141 
    142     private final BluetoothAdapterProperties mAdapterProperties;
    143     private final BluetoothDeviceProperties mDeviceProperties;
    144 
    145     private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
    146     private final ArrayList<String> mUuidIntentTracker;
    147     private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
    148 
    149     private static class ServiceRecordClient {
    150         int pid;
    151         IBinder binder;
    152         IBinder.DeathRecipient death;
    153     }
    154     private final HashMap<Integer, ServiceRecordClient> mServiceRecordToPid;
    155 
    156     private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
    157     private final BluetoothProfileState mA2dpProfileState;
    158     private final BluetoothProfileState mHfpProfileState;
    159 
    160     private BluetoothA2dpService mA2dpService;
    161     private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
    162 
    163     private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
    164 
    165     private static String mDockAddress;
    166     private String mDockPin;
    167 
    168     private boolean mAllowConnect = true;
    169 
    170     private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
    171     private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
    172     private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
    173     private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler;
    174     private static final String INCOMING_CONNECTION_FILE =
    175       "/data/misc/bluetooth/incoming_connection.conf";
    176     private HashMap<String, Pair<Integer, String>> mIncomingConnections;
    177     private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState;
    178 
    179     private static class RemoteService {
    180         public String address;
    181         public ParcelUuid uuid;
    182         public RemoteService(String address, ParcelUuid uuid) {
    183             this.address = address;
    184             this.uuid = uuid;
    185         }
    186         @Override
    187         public boolean equals(Object o) {
    188             if (o instanceof RemoteService) {
    189                 RemoteService service = (RemoteService)o;
    190                 return address.equals(service.address) && uuid.equals(service.uuid);
    191             }
    192             return false;
    193         }
    194 
    195         @Override
    196         public int hashCode() {
    197             int hash = 1;
    198             hash = hash * 31 + (address == null ? 0 : address.hashCode());
    199             hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
    200             return hash;
    201         }
    202     }
    203 
    204     static {
    205         classInitNative();
    206     }
    207 
    208     public BluetoothService(Context context) {
    209         mContext = context;
    210 
    211         // Need to do this in place of:
    212         // mBatteryStats = BatteryStatsService.getService();
    213         // Since we can not import BatteryStatsService from here. This class really needs to be
    214         // moved to java/services/com/android/server/
    215         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
    216 
    217         initializeNativeDataNative();
    218 
    219         if (isEnabledNative() == 1) {
    220             Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
    221             disableNative();
    222         }
    223 
    224         mBondState = new BluetoothBondState(context, this);
    225         mAdapterProperties = new BluetoothAdapterProperties(context, this);
    226         mDeviceProperties = new BluetoothDeviceProperties(this);
    227 
    228         mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
    229         mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
    230         mUuidIntentTracker = new ArrayList<String>();
    231         mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
    232         mServiceRecordToPid = new HashMap<Integer, ServiceRecordClient>();
    233         mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
    234         mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
    235         mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
    236 
    237         mHfpProfileState.start();
    238         mA2dpProfileState.start();
    239 
    240         IntentFilter filter = new IntentFilter();
    241         registerForAirplaneMode(filter);
    242 
    243         filter.addAction(Intent.ACTION_DOCK_EVENT);
    244         mContext.registerReceiver(mReceiver, filter);
    245         mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
    246         mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
    247         mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
    248         mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
    249         mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
    250     }
    251 
    252     public static synchronized String readDockBluetoothAddress() {
    253         if (mDockAddress != null) return mDockAddress;
    254 
    255         BufferedInputStream file = null;
    256         String dockAddress;
    257         try {
    258             file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
    259             byte[] address = new byte[17];
    260             file.read(address);
    261             dockAddress = new String(address);
    262             dockAddress = dockAddress.toUpperCase();
    263             if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
    264                 mDockAddress = dockAddress;
    265                 return mDockAddress;
    266             } else {
    267                 Log.e(TAG, "CheckBluetoothAddress failed for car dock address: "
    268                         + dockAddress);
    269             }
    270         } catch (FileNotFoundException e) {
    271             Log.e(TAG, "FileNotFoundException while trying to read dock address");
    272         } catch (IOException e) {
    273             Log.e(TAG, "IOException while trying to read dock address");
    274         } finally {
    275             if (file != null) {
    276                 try {
    277                     file.close();
    278                 } catch (IOException e) {
    279                     // Ignore
    280                 }
    281             }
    282         }
    283         mDockAddress = null;
    284         return null;
    285     }
    286 
    287     private synchronized boolean writeDockPin() {
    288         BufferedWriter out = null;
    289         try {
    290             out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
    291 
    292             // Generate a random 4 digit pin between 0000 and 9999
    293             // This is not truly random but good enough for our purposes.
    294             int pin = (int) Math.floor(Math.random() * 10000);
    295 
    296             mDockPin = String.format("%04d", pin);
    297             out.write(mDockPin);
    298             return true;
    299         } catch (FileNotFoundException e) {
    300             Log.e(TAG, "FileNotFoundException while trying to write dock pairing pin");
    301         } catch (IOException e) {
    302             Log.e(TAG, "IOException while while trying to write dock pairing pin");
    303         } finally {
    304             if (out != null) {
    305                 try {
    306                     out.close();
    307                 } catch (IOException e) {
    308                     // Ignore
    309                 }
    310             }
    311         }
    312         mDockPin = null;
    313         return false;
    314     }
    315 
    316     /*package*/ synchronized String getDockPin() {
    317         return mDockPin;
    318     }
    319 
    320     public synchronized void initAfterRegistration() {
    321         mAdapter = BluetoothAdapter.getDefaultAdapter();
    322         mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter);
    323         mBluetoothState.start();
    324         if (mContext.getResources().getBoolean
    325             (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
    326             mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT);
    327         }
    328         mEventLoop = mBluetoothState.getBluetoothEventLoop();
    329     }
    330 
    331     public synchronized void initAfterA2dpRegistration() {
    332         mEventLoop.getProfileProxy();
    333     }
    334 
    335     @Override
    336     protected void finalize() throws Throwable {
    337         mContext.unregisterReceiver(mReceiver);
    338         try {
    339             cleanupNativeDataNative();
    340         } finally {
    341             super.finalize();
    342         }
    343     }
    344 
    345     public boolean isEnabled() {
    346         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    347         return isEnabledInternal();
    348     }
    349 
    350     private boolean isEnabledInternal() {
    351         return (getBluetoothStateInternal() == BluetoothAdapter.STATE_ON);
    352     }
    353 
    354     public int getBluetoothState() {
    355         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    356         return getBluetoothStateInternal();
    357     }
    358 
    359     int getBluetoothStateInternal() {
    360         return mBluetoothState.getBluetoothAdapterState();
    361     }
    362 
    363     /**
    364      * Bring down bluetooth and disable BT in settings. Returns true on success.
    365      */
    366     public boolean disable() {
    367         return disable(true);
    368     }
    369 
    370     /**
    371      * Bring down bluetooth. Returns true on success.
    372      *
    373      * @param saveSetting If true, persist the new setting
    374      */
    375     public synchronized boolean disable(boolean saveSetting) {
    376         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    377 
    378         int adapterState = getBluetoothStateInternal();
    379 
    380         switch (adapterState) {
    381         case BluetoothAdapter.STATE_OFF:
    382             return true;
    383         case BluetoothAdapter.STATE_ON:
    384             break;
    385         default:
    386             return false;
    387         }
    388 
    389         mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_OFF, saveSetting);
    390         return true;
    391     }
    392 
    393     synchronized void disconnectDevices() {
    394         // Disconnect devices handled by BluetoothService.
    395         for (BluetoothDevice device: getConnectedInputDevices()) {
    396             disconnectInputDevice(device);
    397         }
    398 
    399         for (BluetoothDevice device: getConnectedPanDevices()) {
    400             disconnectPanDevice(device);
    401         }
    402     }
    403 
    404     /**
    405      * The Bluetooth has been turned off, but hot. Do bonding, profile cleanup
    406      */
    407     synchronized void finishDisable() {
    408         // mark in progress bondings as cancelled
    409         for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
    410             mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
    411                                     BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
    412         }
    413 
    414         // Stop the profile state machine for bonded devices.
    415         for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) {
    416             removeProfileState(address);
    417         }
    418 
    419         // update mode
    420         Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
    421         intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
    422         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    423     }
    424 
    425     /**
    426      * Local clean up after broadcasting STATE_OFF intent
    427      */
    428     synchronized void cleanupAfterFinishDisable() {
    429         mAdapterProperties.clear();
    430 
    431         for (Integer srHandle : mServiceRecordToPid.keySet()) {
    432             removeServiceRecordNative(srHandle);
    433         }
    434         mServiceRecordToPid.clear();
    435 
    436         mProfilesConnected = 0;
    437         mProfilesConnecting = 0;
    438         mProfilesDisconnecting = 0;
    439         mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
    440         mAdapterUuids = null;
    441         mAdapterSdpHandles = null;
    442 
    443         // Log bluetooth off to battery stats.
    444         long ident = Binder.clearCallingIdentity();
    445         try {
    446             mBatteryStats.noteBluetoothOff();
    447         } catch (RemoteException e) {
    448         } finally {
    449             Binder.restoreCallingIdentity(ident);
    450         }
    451     }
    452 
    453     /**
    454      * power off Bluetooth
    455      */
    456     synchronized void shutoffBluetooth() {
    457         if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles);
    458         setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE,
    459                 BluetoothPanProfileHandler.NAP_BRIDGE);
    460         tearDownNativeDataNative();
    461     }
    462 
    463     /**
    464      * Data clean up after Bluetooth shutoff
    465      */
    466     synchronized void cleanNativeAfterShutoffBluetooth() {
    467         // Ths method is called after shutdown of event loop in the Bluetooth shut down
    468         // procedure
    469 
    470         // the adapter property could be changed before event loop is stoped, clear it again
    471         mAdapterProperties.clear();
    472         disableNative();
    473     }
    474 
    475     /** Bring up BT and persist BT on in settings */
    476     public boolean enable() {
    477         return enable(true, true);
    478     }
    479 
    480     /**
    481      * Enable this Bluetooth device, asynchronously.
    482      * This turns on/off the underlying hardware.
    483      *
    484      * @param saveSetting If true, persist the new state of BT in settings
    485      * @param allowConnect If true, auto-connects device when BT is turned on
    486      *                     and allows incoming A2DP/HSP connections
    487      * @return True on success (so far)
    488      */
    489     public synchronized boolean enable(boolean saveSetting, boolean allowConnect) {
    490         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    491                                                 "Need BLUETOOTH_ADMIN permission");
    492 
    493         // Airplane mode can prevent Bluetooth radio from being turned on.
    494         if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
    495             return false;
    496         }
    497         mAllowConnect = allowConnect;
    498         mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
    499         return true;
    500     }
    501 
    502     /**
    503      * Enable this Bluetooth device, asynchronously, but does not
    504      * auto-connect devices. In this state the Bluetooth adapter
    505      * also does not allow incoming A2DP/HSP connections (that
    506      * must go through this service), but does allow communication
    507      * on RFCOMM sockets implemented outside of this service (ie BTOPP).
    508      * This method is used to temporarily enable Bluetooth
    509      * for data transfer, without changing
    510      *
    511      * This turns on/off the underlying hardware.
    512      *
    513      * @return True on success (so far)
    514      */
    515     public boolean enableNoAutoConnect() {
    516         return enable(false, false);
    517     }
    518 
    519     /**
    520      * Turn on Bluetooth Module, Load firmware, and do all the preparation
    521      * needed to get the Bluetooth Module ready but keep it not discoverable
    522      * and not connectable.
    523      */
    524     /* package */ synchronized boolean prepareBluetooth() {
    525         if (!setupNativeDataNative()) {
    526             return false;
    527         }
    528         switchConnectable(false);
    529 
    530         // Bluetooth stack needs a small delay here before adding
    531         // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
    532         try {
    533             Thread.sleep(50);
    534         } catch (InterruptedException e) {}
    535         updateSdpRecords();
    536         return true;
    537     }
    538 
    539     private final Handler mHandler = new Handler() {
    540         @Override
    541         public void handleMessage(Message msg) {
    542             switch (msg.what) {
    543             case MESSAGE_UUID_INTENT:
    544                 String address = (String)msg.obj;
    545                 if (address != null) {
    546                     sendUuidIntent(address);
    547                     makeServiceChannelCallbacks(address);
    548                 }
    549                 break;
    550             case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
    551                 address = (String)msg.obj;
    552                 if (address == null) return;
    553                 int attempt = mBondState.getAttempt(address);
    554 
    555                 // Try only if attemps are in progress and cap it 2 attempts
    556                 // The 2 attempts cap is a fail safe if the stack returns
    557                 // an incorrect error code for bonding failures and if the pin
    558                 // is entered wrongly twice we should abort.
    559                 if (attempt > 0 && attempt <= 2) {
    560                     mBondState.attempt(address);
    561                     createBond(address);
    562                     return;
    563                 }
    564                 if (attempt > 0) mBondState.clearPinAttempts(address);
    565                 break;
    566             case MESSAGE_REMOVE_SERVICE_RECORD:
    567                 Pair<Integer, Integer> pair = (Pair<Integer, Integer>) msg.obj;
    568                 checkAndRemoveRecord(pair.first, pair.second);
    569                 break;
    570             }
    571         }
    572     };
    573 
    574     private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
    575         //Register SDP records.
    576         int[] svcIdentifiers = new int[uuids.size()];
    577         for (int i = 0; i < uuids.size(); i++) {
    578             svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
    579         }
    580         mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
    581     }
    582 
    583     private synchronized void updateSdpRecords() {
    584         ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
    585 
    586         Resources R = mContext.getResources();
    587 
    588         // Add the default records
    589         if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
    590             uuids.add(BluetoothUuid.HSP_AG);
    591             uuids.add(BluetoothUuid.ObexObjectPush);
    592         }
    593 
    594         if (R.getBoolean(com.android.internal.R.bool.config_voice_capable)) {
    595             uuids.add(BluetoothUuid.Handsfree_AG);
    596             uuids.add(BluetoothUuid.PBAP_PSE);
    597         }
    598 
    599         // Add SDP records for profiles maintained by Android userspace
    600         addReservedSdpRecords(uuids);
    601 
    602         // Bluetooth stack need some a small delay here before adding more
    603         // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
    604         try {
    605             Thread.sleep(50);
    606         } catch (InterruptedException e) {}
    607 
    608         if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
    609             // Enable profiles maintained by Bluez userspace.
    610             setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
    611                    BluetoothPanProfileHandler.NAP_BRIDGE);
    612 
    613             // Add SDP records for profiles maintained by Bluez userspace
    614             uuids.add(BluetoothUuid.AudioSource);
    615             uuids.add(BluetoothUuid.AvrcpTarget);
    616             uuids.add(BluetoothUuid.NAP);
    617         }
    618 
    619         // Cannot cast uuids.toArray directly since ParcelUuid is parcelable
    620         mAdapterUuids = new ParcelUuid[uuids.size()];
    621         for (int i = 0; i < uuids.size(); i++) {
    622             mAdapterUuids[i] = uuids.get(i);
    623         }
    624     }
    625 
    626     /**
    627      * This function is called from Bluetooth Event Loop when onPropertyChanged
    628      * for adapter comes in with UUID property.
    629      * @param uuidsThe uuids of adapter as reported by Bluez.
    630      */
    631     /*package*/ synchronized void updateBluetoothState(String uuids) {
    632         ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
    633 
    634         if (mAdapterUuids != null &&
    635             BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
    636             mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED);
    637         }
    638     }
    639 
    640     /**
    641      * This method is called immediately before Bluetooth module is turned on after
    642      * the adapter became pariable.
    643      * It inits bond state and profile state before STATE_ON intent is broadcasted.
    644      */
    645     /*package*/ void initBluetoothAfterTurningOn() {
    646         String discoverable = getProperty("Discoverable", false);
    647         String timeout = getProperty("DiscoverableTimeout", false);
    648         if (timeout == null) {
    649             Log.w(TAG, "Null DiscoverableTimeout property");
    650             // assign a number, anything not 0
    651             timeout = "1";
    652         }
    653         if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {
    654             setAdapterPropertyBooleanNative("Discoverable", 0);
    655         }
    656         mBondState.initBondState();
    657         initProfileState();
    658         getProfileProxy();
    659     }
    660 
    661     /**
    662      * This method is called immediately after Bluetooth module is turned on.
    663      * It starts auto-connection and places bluetooth on sign onto the battery
    664      * stats
    665      */
    666     /*package*/ void runBluetooth() {
    667         autoConnect();
    668 
    669         // Log bluetooth on to battery stats.
    670         long ident = Binder.clearCallingIdentity();
    671         try {
    672             mBatteryStats.noteBluetoothOn();
    673         } catch (RemoteException e) {
    674             Log.e(TAG, "", e);
    675         } finally {
    676             Binder.restoreCallingIdentity(ident);
    677         }
    678     }
    679 
    680     /*package*/ synchronized boolean attemptAutoPair(String address) {
    681         if (!mBondState.hasAutoPairingFailed(address) &&
    682                 !mBondState.isAutoPairingBlacklisted(address)) {
    683             mBondState.attempt(address);
    684             setPin(address, BluetoothDevice.convertPinToBytes("0000"));
    685             return true;
    686         }
    687         return false;
    688     }
    689 
    690     /*package*/ synchronized boolean isFixedPinZerosAutoPairKeyboard(String address) {
    691         // Check for keyboards which have fixed PIN 0000 as the pairing pin
    692         return mBondState.isFixedPinZerosAutoPairKeyboard(address);
    693     }
    694 
    695     /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
    696         if (result == BluetoothDevice.BOND_SUCCESS) {
    697             setBondState(address, BluetoothDevice.BOND_BONDED);
    698             if (mBondState.isAutoPairingAttemptsInProgress(address)) {
    699                 mBondState.clearPinAttempts(address);
    700             }
    701         } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
    702                 mBondState.getAttempt(address) == 1) {
    703             mBondState.addAutoPairingFailure(address);
    704             pairingAttempt(address, result);
    705         } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
    706               mBondState.isAutoPairingAttemptsInProgress(address)) {
    707             pairingAttempt(address, result);
    708         } else {
    709             setBondState(address, BluetoothDevice.BOND_NONE, result);
    710             if (mBondState.isAutoPairingAttemptsInProgress(address)) {
    711                 mBondState.clearPinAttempts(address);
    712             }
    713         }
    714     }
    715 
    716     /*package*/ synchronized String getPendingOutgoingBonding() {
    717         return mBondState.getPendingOutgoingBonding();
    718     }
    719 
    720     private void pairingAttempt(String address, int result) {
    721         // This happens when our initial guess of "0000" as the pass key
    722         // fails. Try to create the bond again and display the pin dialog
    723         // to the user. Use back-off while posting the delayed
    724         // message. The initial value is
    725         // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
    726         // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
    727         // reached, display an error to the user.
    728         int attempt = mBondState.getAttempt(address);
    729         if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
    730                     MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
    731             mBondState.clearPinAttempts(address);
    732             setBondState(address, BluetoothDevice.BOND_NONE, result);
    733             return;
    734         }
    735 
    736         Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
    737         message.obj = address;
    738         boolean postResult =  mHandler.sendMessageDelayed(message,
    739                                         attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
    740         if (!postResult) {
    741             mBondState.clearPinAttempts(address);
    742             setBondState(address,
    743                     BluetoothDevice.BOND_NONE, result);
    744             return;
    745         }
    746     }
    747 
    748     /*package*/ BluetoothDevice getRemoteDevice(String address) {
    749         return mAdapter.getRemoteDevice(address);
    750     }
    751 
    752     private static String toBondStateString(int bondState) {
    753         switch (bondState) {
    754         case BluetoothDevice.BOND_NONE:
    755             return "not bonded";
    756         case BluetoothDevice.BOND_BONDING:
    757             return "bonding";
    758         case BluetoothDevice.BOND_BONDED:
    759             return "bonded";
    760         default:
    761             return "??????";
    762         }
    763     }
    764 
    765     public synchronized boolean setName(String name) {
    766         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    767                                                 "Need BLUETOOTH_ADMIN permission");
    768         if (name == null) {
    769             return false;
    770         }
    771         return setPropertyString("Name", name);
    772     }
    773 
    774     //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
    775     // Either have a single property function with Object as the parameter
    776     // or have a function for each property and then obfuscate in the JNI layer.
    777     // The following looks dirty.
    778     private boolean setPropertyString(String key, String value) {
    779         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    780         if (!isEnabledInternal()) return false;
    781         return setAdapterPropertyStringNative(key, value);
    782     }
    783 
    784     private boolean setPropertyInteger(String key, int value) {
    785         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    786         if (!isEnabledInternal()) return false;
    787         return setAdapterPropertyIntegerNative(key, value);
    788     }
    789 
    790     private boolean setPropertyBoolean(String key, boolean value) {
    791         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    792         if (!isEnabledInternal()) return false;
    793         return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
    794     }
    795 
    796     /**
    797      * Set the discoverability window for the device.  A timeout of zero
    798      * makes the device permanently discoverable (if the device is
    799      * discoverable).  Setting the timeout to a nonzero value does not make
    800      * a device discoverable; you need to call setMode() to make the device
    801      * explicitly discoverable.
    802      *
    803      * @param timeout The discoverable timeout in seconds.
    804      */
    805     public synchronized boolean setDiscoverableTimeout(int timeout) {
    806         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    807                                                 "Need BLUETOOTH_ADMIN permission");
    808         return setPropertyInteger("DiscoverableTimeout", timeout);
    809     }
    810 
    811     public synchronized boolean setScanMode(int mode, int duration) {
    812         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
    813                                                 "Need WRITE_SECURE_SETTINGS permission");
    814         boolean pairable;
    815         boolean discoverable;
    816 
    817         switch (mode) {
    818         case BluetoothAdapter.SCAN_MODE_NONE:
    819             pairable = false;
    820             discoverable = false;
    821             break;
    822         case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
    823             pairable = true;
    824             discoverable = false;
    825             break;
    826         case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
    827             pairable = true;
    828             discoverable = true;
    829             if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
    830             break;
    831         default:
    832             Log.w(TAG, "Requested invalid scan mode " + mode);
    833             return false;
    834         }
    835 
    836         setPropertyBoolean("Discoverable", discoverable);
    837         setPropertyBoolean("Pairable", pairable);
    838         return true;
    839     }
    840 
    841     /**
    842      * @param on true set the local Bluetooth module to be connectable
    843      *                The dicoverability is recovered to what it was before
    844      *                switchConnectable(false) call
    845      *           false set the local Bluetooth module to be not connectable
    846      *                 and not dicoverable
    847      */
    848     /*package*/ synchronized void switchConnectable(boolean on) {
    849         setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);
    850     }
    851 
    852     /*package*/ synchronized void setPairable() {
    853         String pairableString = getProperty("Pairable", false);
    854         if (pairableString == null) {
    855             Log.e(TAG, "null pairableString");
    856             return;
    857         }
    858         if (pairableString.equals("false")) {
    859             setAdapterPropertyBooleanNative("Pairable", 1);
    860         }
    861     }
    862 
    863     /*package*/ String getProperty(String name, boolean checkState) {
    864         // If checkState is false, check if the event loop is running.
    865         // before making the call to Bluez
    866         if (checkState) {
    867             if (!isEnabledInternal()) return null;
    868         } else if (!mEventLoop.isEventLoopRunning()) {
    869             return null;
    870         }
    871 
    872         return mAdapterProperties.getProperty(name);
    873     }
    874 
    875     BluetoothAdapterProperties getAdapterProperties() {
    876         return mAdapterProperties;
    877     }
    878 
    879     BluetoothDeviceProperties getDeviceProperties() {
    880         return mDeviceProperties;
    881     }
    882 
    883     boolean isRemoteDeviceInCache(String address) {
    884         return mDeviceProperties.isInCache(address);
    885     }
    886 
    887     void setRemoteDeviceProperty(String address, String name, String value) {
    888         mDeviceProperties.setProperty(address, name, value);
    889     }
    890 
    891     void updateRemoteDevicePropertiesCache(String address) {
    892         mDeviceProperties.updateCache(address);
    893     }
    894 
    895     public synchronized String getAddress() {
    896         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    897         // Don't check state since we want to provide address, even if BT is off
    898         return getProperty("Address", false);
    899     }
    900 
    901     public synchronized String getName() {
    902         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    903         // Don't check state since we want to provide name, even if BT is off
    904         return getProperty("Name", false);
    905     }
    906 
    907     public ParcelUuid[] getUuids() {
    908         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    909         String value =  getProperty("UUIDs", true);
    910         if (value == null) return null;
    911         return convertStringToParcelUuid(value);
    912     }
    913 
    914     private ParcelUuid[] convertStringToParcelUuid(String value) {
    915         String[] uuidStrings = null;
    916         // The UUIDs are stored as a "," separated string.
    917         uuidStrings = value.split(",");
    918         ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
    919 
    920         for (int i = 0; i < uuidStrings.length; i++) {
    921             uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
    922         }
    923         return uuids;
    924     }
    925 
    926     /**
    927      * Returns the user-friendly name of a remote device.  This value is
    928      * returned from our local cache, which is updated when onPropertyChange
    929      * event is received.
    930      * Do not expect to retrieve the updated remote name immediately after
    931      * changing the name on the remote device.
    932      *
    933      * @param address Bluetooth address of remote device.
    934      *
    935      * @return The user-friendly name of the specified remote device.
    936      */
    937     public synchronized String getRemoteName(String address) {
    938         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    939         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
    940             return null;
    941         }
    942         return mDeviceProperties.getProperty(address, "Name");
    943     }
    944 
    945     /**
    946      * Returns alias of a remote device.  This value is returned from our
    947      * local cache, which is updated when onPropertyChange event is received.
    948      *
    949      * @param address Bluetooth address of remote device.
    950      *
    951      * @return The alias of the specified remote device.
    952      */
    953     public synchronized String getRemoteAlias(String address) {
    954 
    955         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    956         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
    957             return null;
    958         }
    959         return mDeviceProperties.getProperty(address, "Alias");
    960     }
    961 
    962     /**
    963      * Set the alias of a remote device.
    964      *
    965      * @param address Bluetooth address of remote device.
    966      * @param alias new alias to change to
    967      * @return true on success, false on error
    968      */
    969     public synchronized boolean setRemoteAlias(String address, String alias) {
    970         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    971         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
    972             return false;
    973         }
    974 
    975         return setDevicePropertyStringNative(getObjectPathFromAddress(address),
    976                                              "Alias", alias);
    977     }
    978 
    979     /**
    980      * Get the discoverability window for the device.  A timeout of zero
    981      * means that the device is permanently discoverable (if the device is
    982      * in the discoverable mode).
    983      *
    984      * @return The discoverability window of the device, in seconds.  A negative
    985      *         value indicates an error.
    986      */
    987     public int getDiscoverableTimeout() {
    988         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    989         String timeout = getProperty("DiscoverableTimeout", true);
    990         if (timeout != null)
    991            return Integer.valueOf(timeout);
    992         else
    993             return -1;
    994     }
    995 
    996     public int getScanMode() {
    997         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    998         if (!isEnabledInternal())
    999             return BluetoothAdapter.SCAN_MODE_NONE;
   1000 
   1001         boolean pairable = getProperty("Pairable", true).equals("true");
   1002         boolean discoverable = getProperty("Discoverable", true).equals("true");
   1003         return bluezStringToScanMode (pairable, discoverable);
   1004     }
   1005 
   1006     public synchronized boolean startDiscovery() {
   1007         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1008                                                 "Need BLUETOOTH_ADMIN permission");
   1009         if (!isEnabledInternal()) return false;
   1010 
   1011         return startDiscoveryNative();
   1012     }
   1013 
   1014     public synchronized boolean cancelDiscovery() {
   1015         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1016                                                 "Need BLUETOOTH_ADMIN permission");
   1017         if (!isEnabledInternal()) return false;
   1018 
   1019         return stopDiscoveryNative();
   1020     }
   1021 
   1022     public boolean isDiscovering() {
   1023         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1024 
   1025         String discoveringProperty = getProperty("Discovering", false);
   1026         if (discoveringProperty == null) {
   1027             return false;
   1028         }
   1029 
   1030         return discoveringProperty.equals("true");
   1031     }
   1032 
   1033     private boolean isBondingFeasible(String address) {
   1034         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1035                                                 "Need BLUETOOTH_ADMIN permission");
   1036         if (!isEnabledInternal()) return false;
   1037 
   1038         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1039             return false;
   1040         }
   1041         address = address.toUpperCase();
   1042 
   1043         if (mBondState.getPendingOutgoingBonding() != null) {
   1044             Log.d(TAG, "Ignoring createBond(): another device is bonding");
   1045             // a different device is currently bonding, fail
   1046             return false;
   1047         }
   1048 
   1049         // Check for bond state only if we are not performing auto
   1050         // pairing exponential back-off attempts.
   1051         if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
   1052                 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
   1053             Log.d(TAG, "Ignoring createBond(): this device is already bonding or bonded");
   1054             return false;
   1055         }
   1056 
   1057         if (address.equals(mDockAddress)) {
   1058             if (!writeDockPin()) {
   1059                 Log.e(TAG, "Error while writing Pin for the dock");
   1060                 return false;
   1061             }
   1062         }
   1063         return true;
   1064     }
   1065 
   1066     public synchronized boolean createBond(String address) {
   1067         if (!isBondingFeasible(address)) return false;
   1068 
   1069         if (!createPairedDeviceNative(address, 60000  /*1 minute*/ )) {
   1070             return false;
   1071         }
   1072 
   1073         mBondState.setPendingOutgoingBonding(address);
   1074         mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
   1075 
   1076         return true;
   1077     }
   1078 
   1079     public synchronized boolean createBondOutOfBand(String address, byte[] hash,
   1080                                                     byte[] randomizer) {
   1081         if (!isBondingFeasible(address)) return false;
   1082 
   1083         if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
   1084             return false;
   1085         }
   1086 
   1087         setDeviceOutOfBandData(address, hash, randomizer);
   1088         mBondState.setPendingOutgoingBonding(address);
   1089         mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
   1090 
   1091         return true;
   1092     }
   1093 
   1094     public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
   1095             byte[] randomizer) {
   1096         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1097                                                 "Need BLUETOOTH_ADMIN permission");
   1098         if (!isEnabledInternal()) return false;
   1099 
   1100         Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
   1101 
   1102         if (DBG) {
   1103             Log.d(TAG, "Setting out of band data for: " + address + ":" +
   1104                   Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
   1105         }
   1106 
   1107         mDeviceOobData.put(address, value);
   1108         return true;
   1109     }
   1110 
   1111     Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
   1112         return mDeviceOobData.get(device.getAddress());
   1113     }
   1114 
   1115 
   1116     public synchronized byte[] readOutOfBandData() {
   1117         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   1118                                                 "Need BLUETOOTH permission");
   1119         if (!isEnabledInternal()) return null;
   1120 
   1121         return readAdapterOutOfBandDataNative();
   1122     }
   1123 
   1124     public synchronized boolean cancelBondProcess(String address) {
   1125         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1126                                                 "Need BLUETOOTH_ADMIN permission");
   1127         if (!isEnabledInternal()) return false;
   1128 
   1129         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1130             return false;
   1131         }
   1132         address = address.toUpperCase();
   1133         if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
   1134             return false;
   1135         }
   1136 
   1137         mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
   1138                                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
   1139         cancelDeviceCreationNative(address);
   1140         return true;
   1141     }
   1142 
   1143     public synchronized boolean removeBond(String address) {
   1144         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1145                                                 "Need BLUETOOTH_ADMIN permission");
   1146         if (!isEnabledInternal()) return false;
   1147 
   1148         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1149             return false;
   1150         }
   1151         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   1152         if (state != null) {
   1153             state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
   1154             return true;
   1155         } else {
   1156             return false;
   1157         }
   1158     }
   1159 
   1160     public synchronized boolean removeBondInternal(String address) {
   1161         // Unset the trusted device state and then unpair
   1162         setTrust(address, false);
   1163         return removeDeviceNative(getObjectPathFromAddress(address));
   1164     }
   1165 
   1166     public synchronized String[] listBonds() {
   1167         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1168         return mBondState.listInState(BluetoothDevice.BOND_BONDED);
   1169     }
   1170 
   1171     /*package*/ synchronized String[] listInState(int state) {
   1172       return mBondState.listInState(state);
   1173     }
   1174 
   1175     public synchronized int getBondState(String address) {
   1176         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1177         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1178             return BluetoothDevice.ERROR;
   1179         }
   1180         return mBondState.getBondState(address.toUpperCase());
   1181     }
   1182 
   1183     /*package*/ synchronized boolean setBondState(String address, int state) {
   1184         return setBondState(address, state, 0);
   1185     }
   1186 
   1187     /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
   1188         mBondState.setBondState(address.toUpperCase(), state, reason);
   1189         return true;
   1190     }
   1191 
   1192     public synchronized boolean isBluetoothDock(String address) {
   1193         SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
   1194                 Context.MODE_PRIVATE);
   1195 
   1196         return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
   1197     }
   1198 
   1199     /*package*/ String[] getRemoteDeviceProperties(String address) {
   1200         if (!isEnabledInternal()) return null;
   1201 
   1202         String objectPath = getObjectPathFromAddress(address);
   1203         return (String [])getDevicePropertiesNative(objectPath);
   1204     }
   1205 
   1206     /**
   1207      * Sets the remote device trust state.
   1208      *
   1209      * @return boolean to indicate operation success or fail
   1210      */
   1211     public synchronized boolean setTrust(String address, boolean value) {
   1212         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1213             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1214                     "Need BLUETOOTH_ADMIN permission");
   1215             return false;
   1216         }
   1217 
   1218         if (!isEnabledInternal()) return false;
   1219 
   1220         return setDevicePropertyBooleanNative(
   1221                 getObjectPathFromAddress(address), "Trusted", value ? 1 : 0);
   1222     }
   1223 
   1224     /**
   1225      * Gets the remote device trust state as boolean.
   1226      * Note: this value may be
   1227      * retrieved from cache if we retrieved the data before *
   1228      *
   1229      * @return boolean to indicate trusted or untrusted state
   1230      */
   1231     public synchronized boolean getTrustState(String address) {
   1232         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1233             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1234             return false;
   1235         }
   1236 
   1237         String val = mDeviceProperties.getProperty(address, "Trusted");
   1238         if (val == null) {
   1239             return false;
   1240         } else {
   1241             return val.equals("true");
   1242         }
   1243     }
   1244 
   1245     /**
   1246      * Gets the remote major, minor classes encoded as a 32-bit
   1247      * integer.
   1248      *
   1249      * Note: this value is retrieved from cache, because we get it during
   1250      *       remote-device discovery.
   1251      *
   1252      * @return 32-bit integer encoding the remote major, minor, and service
   1253      *         classes.
   1254      */
   1255     public synchronized int getRemoteClass(String address) {
   1256         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1257             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1258             return BluetoothClass.ERROR;
   1259         }
   1260         String val = mDeviceProperties.getProperty(address, "Class");
   1261         if (val == null)
   1262             return BluetoothClass.ERROR;
   1263         else {
   1264             return Integer.valueOf(val);
   1265         }
   1266     }
   1267 
   1268 
   1269     /**
   1270      * Gets the UUIDs supported by the remote device
   1271      *
   1272      * @return array of 128bit ParcelUuids
   1273      */
   1274     public synchronized ParcelUuid[] getRemoteUuids(String address) {
   1275         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1276         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1277             return null;
   1278         }
   1279         return getUuidFromCache(address);
   1280     }
   1281 
   1282     ParcelUuid[] getUuidFromCache(String address) {
   1283         String value = mDeviceProperties.getProperty(address, "UUIDs");
   1284         if (value == null) return null;
   1285 
   1286         String[] uuidStrings = null;
   1287         // The UUIDs are stored as a "," separated string.
   1288         uuidStrings = value.split(",");
   1289         ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
   1290 
   1291         for (int i = 0; i < uuidStrings.length; i++) {
   1292             uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
   1293         }
   1294         return uuids;
   1295     }
   1296 
   1297     /**
   1298      * Connect and fetch new UUID's using SDP.
   1299      * The UUID's found are broadcast as intents.
   1300      * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
   1301      * a given uuid.
   1302      * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
   1303      * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
   1304      * callback and broadcast intents.
   1305      */
   1306     public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
   1307             IBluetoothCallback callback) {
   1308         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1309         if (!isEnabledInternal()) return false;
   1310 
   1311         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1312             return false;
   1313         }
   1314 
   1315         RemoteService service = new RemoteService(address, uuid);
   1316         if (uuid != null && mUuidCallbackTracker.get(service) != null) {
   1317             // An SDP query for this address & uuid is already in progress
   1318             // Do not add this callback for the uuid
   1319             return false;
   1320         }
   1321 
   1322         if (mUuidIntentTracker.contains(address)) {
   1323             // An SDP query for this address is already in progress
   1324             // Add this uuid onto the in-progress SDP query
   1325             if (uuid != null) {
   1326                 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
   1327             }
   1328             return true;
   1329         }
   1330 
   1331         // If the device is already created, we will
   1332         // do the SDP on the callback of createDeviceNative.
   1333         boolean ret= createDeviceNative(address);
   1334 
   1335         mUuidIntentTracker.add(address);
   1336         if (uuid != null) {
   1337             mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
   1338         }
   1339 
   1340         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
   1341         message.obj = address;
   1342         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
   1343         return ret;
   1344     }
   1345 
   1346     /**
   1347      * Gets the rfcomm channel associated with the UUID.
   1348      * Pulls records from the cache only.
   1349      *
   1350      * @param address Address of the remote device
   1351      * @param uuid ParcelUuid of the service attribute
   1352      *
   1353      * @return rfcomm channel associated with the service attribute
   1354      *         -1 on error
   1355      */
   1356     public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
   1357         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1358         if (!isEnabledInternal()) return -1;
   1359 
   1360         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1361             return BluetoothDevice.ERROR;
   1362         }
   1363         // Check if we are recovering from a crash.
   1364         if (mDeviceProperties.isEmpty()) {
   1365             if (mDeviceProperties.updateCache(address) == null)
   1366                 return -1;
   1367         }
   1368 
   1369         Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
   1370         if (value != null && value.containsKey(uuid))
   1371             return value.get(uuid);
   1372         return -1;
   1373     }
   1374 
   1375     public synchronized boolean setPin(String address, byte[] pin) {
   1376         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1377                                                 "Need BLUETOOTH_ADMIN permission");
   1378         if (!isEnabledInternal()) return false;
   1379 
   1380         if (pin == null || pin.length <= 0 || pin.length > 16 ||
   1381             !BluetoothAdapter.checkBluetoothAddress(address)) {
   1382             return false;
   1383         }
   1384         address = address.toUpperCase();
   1385         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
   1386         if (data == null) {
   1387             Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
   1388                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
   1389                   " or by bluez.\n");
   1390             return false;
   1391         }
   1392         // bluez API wants pin as a string
   1393         String pinString;
   1394         try {
   1395             pinString = new String(pin, "UTF8");
   1396         } catch (UnsupportedEncodingException uee) {
   1397             Log.e(TAG, "UTF8 not supported?!?");
   1398             return false;
   1399         }
   1400         return setPinNative(address, pinString, data.intValue());
   1401     }
   1402 
   1403     public synchronized boolean setPasskey(String address, int passkey) {
   1404         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1405                                                 "Need BLUETOOTH_ADMIN permission");
   1406         if (!isEnabledInternal()) return false;
   1407 
   1408         if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
   1409             return false;
   1410         }
   1411         address = address.toUpperCase();
   1412         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
   1413         if (data == null) {
   1414             Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
   1415                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
   1416                   " or by bluez.\n");
   1417             return false;
   1418         }
   1419         return setPasskeyNative(address, passkey, data.intValue());
   1420     }
   1421 
   1422     public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
   1423         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1424                                                 "Need BLUETOOTH_ADMIN permission");
   1425         if (!isEnabledInternal()) return false;
   1426 
   1427         address = address.toUpperCase();
   1428         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
   1429         if (data == null) {
   1430             Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
   1431                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
   1432                   " or by bluez.\n");
   1433             return false;
   1434         }
   1435         return setPairingConfirmationNative(address, confirm, data.intValue());
   1436     }
   1437 
   1438     public synchronized boolean setRemoteOutOfBandData(String address) {
   1439         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1440                                                 "Need BLUETOOTH_ADMIN permission");
   1441         if (!isEnabledInternal()) return false;
   1442         address = address.toUpperCase();
   1443         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
   1444         if (data == null) {
   1445             Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
   1446                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
   1447                   " or by bluez.\n");
   1448             return false;
   1449         }
   1450 
   1451         Pair<byte[], byte[]> val = mDeviceOobData.get(address);
   1452         byte[] hash, randomizer;
   1453         if (val == null) {
   1454             // TODO: check what should be passed in this case.
   1455             hash = new byte[16];
   1456             randomizer = new byte[16];
   1457         } else {
   1458             hash = val.first;
   1459             randomizer = val.second;
   1460         }
   1461         return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
   1462     }
   1463 
   1464     public synchronized boolean cancelPairingUserInput(String address) {
   1465         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   1466                                                 "Need BLUETOOTH_ADMIN permission");
   1467         if (!isEnabledInternal()) return false;
   1468 
   1469         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   1470             return false;
   1471         }
   1472         mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
   1473                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
   1474         address = address.toUpperCase();
   1475         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
   1476         if (data == null) {
   1477             Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
   1478                 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
   1479                 "by the remote or by bluez.\n");
   1480             return false;
   1481         }
   1482         return cancelPairingUserInputNative(address, data.intValue());
   1483     }
   1484 
   1485     /*package*/ void updateDeviceServiceChannelCache(String address) {
   1486         if (DBG) Log.d(TAG, "updateDeviceServiceChannelCache(" + address + ")");
   1487 
   1488         // We are storing the rfcomm channel numbers only for the uuids
   1489         // we are interested in.
   1490         ParcelUuid[] deviceUuids = getRemoteUuids(address);
   1491 
   1492         ArrayList<ParcelUuid> applicationUuids = new ArrayList<ParcelUuid>();
   1493 
   1494         synchronized (this) {
   1495             for (RemoteService service : mUuidCallbackTracker.keySet()) {
   1496                 if (service.address.equals(address)) {
   1497                     applicationUuids.add(service.uuid);
   1498                 }
   1499             }
   1500         }
   1501 
   1502         Map <ParcelUuid, Integer> uuidToChannelMap = new HashMap<ParcelUuid, Integer>();
   1503 
   1504         // Retrieve RFCOMM channel for default uuids
   1505         for (ParcelUuid uuid : RFCOMM_UUIDS) {
   1506             if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
   1507                 int channel = getDeviceServiceChannelForUuid(address, uuid);
   1508                 uuidToChannelMap.put(uuid, channel);
   1509                 if (DBG) Log.d(TAG, "\tuuid(system): " + uuid + " " + channel);
   1510             }
   1511         }
   1512         // Retrieve RFCOMM channel for application requested uuids
   1513         for (ParcelUuid uuid : applicationUuids) {
   1514             if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
   1515                 int channel = getDeviceServiceChannelForUuid(address, uuid);
   1516                 uuidToChannelMap.put(uuid, channel);
   1517                 if (DBG) Log.d(TAG, "\tuuid(application): " + uuid + " " + channel);
   1518             }
   1519         }
   1520 
   1521         synchronized (this) {
   1522             // Make application callbacks
   1523             for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
   1524                     iter.hasNext();) {
   1525                 RemoteService service = iter.next();
   1526                 if (service.address.equals(address)) {
   1527                     if (uuidToChannelMap.containsKey(service.uuid)) {
   1528                         int channel = uuidToChannelMap.get(service.uuid);
   1529 
   1530                         if (DBG) Log.d(TAG, "Making callback for " + service.uuid +
   1531                                     " with result " + channel);
   1532                         IBluetoothCallback callback = mUuidCallbackTracker.get(service);
   1533                         if (callback != null) {
   1534                             try {
   1535                                 callback.onRfcommChannelFound(channel);
   1536                             } catch (RemoteException e) {Log.e(TAG, "", e);}
   1537                         }
   1538 
   1539                         iter.remove();
   1540                     }
   1541                 }
   1542             }
   1543 
   1544             // Update cache
   1545             mDeviceServiceChannelCache.put(address, uuidToChannelMap);
   1546         }
   1547     }
   1548 
   1549     private int getDeviceServiceChannelForUuid(String address,
   1550             ParcelUuid uuid) {
   1551         return getDeviceServiceChannelNative(getObjectPathFromAddress(address),
   1552                 uuid.toString(), 0x0004);
   1553     }
   1554 
   1555     /**
   1556      * b is a handle to a Binder instance, so that this service can be notified
   1557      * for Applications that terminate unexpectedly, to clean there service
   1558      * records
   1559      */
   1560     public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
   1561             int channel, IBinder b) {
   1562         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1563         if (!isEnabledInternal()) return -1;
   1564 
   1565         if (serviceName == null || uuid == null || channel < 1 ||
   1566                 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
   1567             return -1;
   1568         }
   1569         if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
   1570             Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
   1571             return -1;
   1572         }
   1573         int handle = addRfcommServiceRecordNative(serviceName,
   1574                 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
   1575                 (short)channel);
   1576         if (DBG) Log.d(TAG, "new handle " + Integer.toHexString(handle));
   1577         if (handle == -1) {
   1578             return -1;
   1579         }
   1580 
   1581         ServiceRecordClient client = new ServiceRecordClient();
   1582         client.pid = Binder.getCallingPid();
   1583         client.binder = b;
   1584         client.death = new Reaper(handle, client.pid, RFCOMM_RECORD_REAPER);
   1585         mServiceRecordToPid.put(new Integer(handle), client);
   1586         try {
   1587             b.linkToDeath(client.death, 0);
   1588         } catch (RemoteException e) {
   1589             Log.e(TAG, "", e);
   1590             client.death = null;
   1591         }
   1592         return handle;
   1593     }
   1594 
   1595     public void removeServiceRecord(int handle) {
   1596         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   1597                                                 "Need BLUETOOTH permission");
   1598         // Since this is a binder call check if Bluetooth is off
   1599         if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
   1600         Message message = mHandler.obtainMessage(MESSAGE_REMOVE_SERVICE_RECORD);
   1601         message.obj = new Pair<Integer, Integer>(handle, Binder.getCallingPid());
   1602         mHandler.sendMessage(message);
   1603     }
   1604 
   1605     private synchronized void checkAndRemoveRecord(int handle, int pid) {
   1606         ServiceRecordClient client = mServiceRecordToPid.get(handle);
   1607         if (client != null && pid == client.pid) {
   1608             if (DBG) Log.d(TAG, "Removing service record " +
   1609                 Integer.toHexString(handle) + " for pid " + pid);
   1610 
   1611             if (client.death != null) {
   1612                 client.binder.unlinkToDeath(client.death, 0);
   1613             }
   1614 
   1615             mServiceRecordToPid.remove(handle);
   1616             removeServiceRecordNative(handle);
   1617         }
   1618     }
   1619 
   1620     private class Reaper implements IBinder.DeathRecipient {
   1621         int mPid;
   1622         int mHandle;
   1623         int mType;
   1624 
   1625         Reaper(int handle, int pid, int type) {
   1626             mPid = pid;
   1627             mHandle = handle;
   1628             mType = type;
   1629         }
   1630 
   1631         Reaper(int pid, int type) {
   1632             mPid = pid;
   1633             mType = type;
   1634         }
   1635 
   1636         @Override
   1637         public void binderDied() {
   1638             synchronized (BluetoothService.this) {
   1639                 if (DBG) Log.d(TAG, "Tracked app " + mPid + " died" + "Type:" + mType);
   1640                 if (mType == RFCOMM_RECORD_REAPER) {
   1641                     checkAndRemoveRecord(mHandle, mPid);
   1642                 } else if (mType == STATE_CHANGE_REAPER) {
   1643                     mStateChangeTracker.remove(mPid);
   1644                 }
   1645             }
   1646         }
   1647     }
   1648 
   1649 
   1650     @Override
   1651     public boolean changeApplicationBluetoothState(boolean on,
   1652             IBluetoothStateChangeCallback callback, IBinder binder) {
   1653         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1654 
   1655         int pid = Binder.getCallingPid();
   1656         //mStateChangeTracker is a synchronized map
   1657         if (!mStateChangeTracker.containsKey(pid)) {
   1658             if (on) {
   1659                 mStateChangeTracker.put(pid, callback);
   1660             } else {
   1661                 return false;
   1662             }
   1663         } else if (!on) {
   1664             mStateChangeTracker.remove(pid);
   1665         }
   1666 
   1667         if (binder != null) {
   1668             try {
   1669                 binder.linkToDeath(new Reaper(pid, STATE_CHANGE_REAPER), 0);
   1670             } catch (RemoteException e) {
   1671                 Log.e(TAG, "", e);
   1672                 return false;
   1673             }
   1674         }
   1675 
   1676         int type;
   1677         if (on) {
   1678             type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_ON;
   1679         } else {
   1680             type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_OFF;
   1681         }
   1682 
   1683         mBluetoothState.sendMessage(type, callback);
   1684         return true;
   1685     }
   1686 
   1687     boolean isApplicationStateChangeTrackerEmpty() {
   1688         return mStateChangeTracker.isEmpty();
   1689     }
   1690 
   1691     void clearApplicationStateChangeTracker() {
   1692         mStateChangeTracker.clear();
   1693     }
   1694 
   1695     Collection<IBluetoothStateChangeCallback> getApplicationStateChangeCallbacks() {
   1696         return mStateChangeTracker.values();
   1697     }
   1698 
   1699     int getNumberOfApplicationStateChangeTrackers() {
   1700         return mStateChangeTracker.size();
   1701     }
   1702 
   1703     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
   1704         @Override
   1705         public void onReceive(Context context, Intent intent) {
   1706             if (intent == null) return;
   1707 
   1708             String action = intent.getAction();
   1709             if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
   1710                 ContentResolver resolver = context.getContentResolver();
   1711                 // Query the airplane mode from Settings.System just to make sure that
   1712                 // some random app is not sending this intent and disabling bluetooth
   1713                 if (isAirplaneModeOn()) {
   1714                     mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_ON);
   1715                 } else {
   1716                     mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_OFF);
   1717                 }
   1718             } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
   1719                 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
   1720                         Intent.EXTRA_DOCK_STATE_UNDOCKED);
   1721                 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
   1722                 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
   1723                     mDockAddress = null;
   1724                     mDockPin = null;
   1725                 } else {
   1726                     SharedPreferences.Editor editor =
   1727                         mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
   1728                                 mContext.MODE_PRIVATE).edit();
   1729                     editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
   1730                     editor.apply();
   1731                 }
   1732             }
   1733         }
   1734     };
   1735 
   1736     private void registerForAirplaneMode(IntentFilter filter) {
   1737         final ContentResolver resolver = mContext.getContentResolver();
   1738         final String airplaneModeRadios = Settings.System.getString(resolver,
   1739                 Settings.System.AIRPLANE_MODE_RADIOS);
   1740         final String toggleableRadios = Settings.System.getString(resolver,
   1741                 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
   1742 
   1743         mIsAirplaneSensitive = airplaneModeRadios == null ? true :
   1744                 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
   1745         mIsAirplaneToggleable = toggleableRadios == null ? false :
   1746                 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
   1747 
   1748         if (mIsAirplaneSensitive) {
   1749             filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
   1750         }
   1751     }
   1752 
   1753     /* Returns true if airplane mode is currently on */
   1754     /*package*/ final boolean isAirplaneModeOn() {
   1755         return Settings.System.getInt(mContext.getContentResolver(),
   1756                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
   1757     }
   1758 
   1759     /* Broadcast the Uuid intent */
   1760     /*package*/ synchronized void sendUuidIntent(String address) {
   1761         ParcelUuid[] uuid = getUuidFromCache(address);
   1762         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
   1763         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
   1764         intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
   1765         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
   1766         mUuidIntentTracker.remove(address);
   1767     }
   1768 
   1769     /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
   1770         for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
   1771                 iter.hasNext();) {
   1772             RemoteService service = iter.next();
   1773             if (service.address.equals(address)) {
   1774                 if (DBG) Log.d(TAG, "Cleaning up failed UUID channel lookup: "
   1775                     + service.address + " " + service.uuid);
   1776                 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
   1777                 if (callback != null) {
   1778                     try {
   1779                         callback.onRfcommChannelFound(-1);
   1780                     } catch (RemoteException e) {Log.e(TAG, "", e);}
   1781                 }
   1782 
   1783                 iter.remove();
   1784             }
   1785         }
   1786     }
   1787 
   1788     @Override
   1789     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1790         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   1791 
   1792         if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
   1793             return;
   1794         }
   1795 
   1796         pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
   1797         pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
   1798 
   1799         pw.println("Local address = " + getAddress());
   1800         pw.println("Local name = " + getName());
   1801         pw.println("isDiscovering() = " + isDiscovering());
   1802 
   1803         mAdapter.getProfileProxy(mContext,
   1804                                  mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
   1805         mAdapter.getProfileProxy(mContext,
   1806                 mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE);
   1807         mAdapter.getProfileProxy(mContext,
   1808                 mBluetoothProfileServiceListener, BluetoothProfile.PAN);
   1809 
   1810         dumpKnownDevices(pw);
   1811         dumpAclConnectedDevices(pw);
   1812         dumpHeadsetService(pw);
   1813         dumpInputDeviceProfile(pw);
   1814         dumpPanProfile(pw);
   1815         dumpApplicationServiceRecords(pw);
   1816         dumpProfileState(pw);
   1817     }
   1818 
   1819     private void dumpProfileState(PrintWriter pw) {
   1820         pw.println("\n--Profile State dump--");
   1821         pw.println("\n Headset profile state:" +
   1822                 mAdapter.getProfileConnectionState(BluetoothProfile.HEADSET));
   1823         pw.println("\n A2dp profile state:" +
   1824                 mAdapter.getProfileConnectionState(BluetoothProfile.A2DP));
   1825         pw.println("\n HID profile state:" +
   1826                 mAdapter.getProfileConnectionState(BluetoothProfile.INPUT_DEVICE));
   1827         pw.println("\n PAN profile state:" +
   1828                 mAdapter.getProfileConnectionState(BluetoothProfile.PAN));
   1829     }
   1830 
   1831     private void dumpHeadsetService(PrintWriter pw) {
   1832         pw.println("\n--Headset Service--");
   1833         if (mHeadsetProxy != null) {
   1834             List<BluetoothDevice> deviceList = mHeadsetProxy.getConnectedDevices();
   1835             if (deviceList.size() == 0) {
   1836                 pw.println("No headsets connected");
   1837             } else {
   1838                 BluetoothDevice device = deviceList.get(0);
   1839                 pw.println("\ngetConnectedDevices[0] = " + device);
   1840                 dumpHeadsetConnectionState(pw, device);
   1841                 pw.println("getBatteryUsageHint() = " +
   1842                              mHeadsetProxy.getBatteryUsageHint(device));
   1843             }
   1844 
   1845             deviceList.clear();
   1846             deviceList = mHeadsetProxy.getDevicesMatchingConnectionStates(new int[] {
   1847                      BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
   1848             pw.println("--Connected and Disconnected Headsets");
   1849             for (BluetoothDevice device: deviceList) {
   1850                 pw.println(device);
   1851                 if (mHeadsetProxy.isAudioConnected(device)) {
   1852                     pw.println("SCO audio connected to device:" + device);
   1853                 }
   1854             }
   1855         }
   1856     }
   1857 
   1858     private void dumpInputDeviceProfile(PrintWriter pw) {
   1859         pw.println("\n--Bluetooth Service- Input Device Profile");
   1860         if (mInputDevice != null) {
   1861             List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices();
   1862             if (deviceList.size() == 0) {
   1863                 pw.println("No input devices connected");
   1864             } else {
   1865                 pw.println("Number of connected devices:" + deviceList.size());
   1866                 BluetoothDevice device = deviceList.get(0);
   1867                 pw.println("getConnectedDevices[0] = " + device);
   1868                 pw.println("Priority of Connected device = " + mInputDevice.getPriority(device));
   1869 
   1870                 switch (mInputDevice.getConnectionState(device)) {
   1871                     case BluetoothInputDevice.STATE_CONNECTING:
   1872                         pw.println("getConnectionState() = STATE_CONNECTING");
   1873                         break;
   1874                     case BluetoothInputDevice.STATE_CONNECTED:
   1875                         pw.println("getConnectionState() = STATE_CONNECTED");
   1876                         break;
   1877                     case BluetoothInputDevice.STATE_DISCONNECTING:
   1878                         pw.println("getConnectionState() = STATE_DISCONNECTING");
   1879                         break;
   1880                 }
   1881             }
   1882             deviceList.clear();
   1883             deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] {
   1884                      BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
   1885             pw.println("--Connected and Disconnected input devices");
   1886             for (BluetoothDevice device: deviceList) {
   1887                 pw.println(device);
   1888             }
   1889         }
   1890     }
   1891 
   1892     private void dumpPanProfile(PrintWriter pw) {
   1893         pw.println("\n--Bluetooth Service- Pan Profile");
   1894         if (mPan != null) {
   1895             List<BluetoothDevice> deviceList = mPan.getConnectedDevices();
   1896             if (deviceList.size() == 0) {
   1897                 pw.println("No Pan devices connected");
   1898             } else {
   1899                 pw.println("Number of connected devices:" + deviceList.size());
   1900                 BluetoothDevice device = deviceList.get(0);
   1901                 pw.println("getConnectedDevices[0] = " + device);
   1902 
   1903                 switch (mPan.getConnectionState(device)) {
   1904                     case BluetoothInputDevice.STATE_CONNECTING:
   1905                         pw.println("getConnectionState() = STATE_CONNECTING");
   1906                         break;
   1907                     case BluetoothInputDevice.STATE_CONNECTED:
   1908                         pw.println("getConnectionState() = STATE_CONNECTED");
   1909                         break;
   1910                     case BluetoothInputDevice.STATE_DISCONNECTING:
   1911                         pw.println("getConnectionState() = STATE_DISCONNECTING");
   1912                         break;
   1913                 }
   1914             }
   1915             deviceList.clear();
   1916             deviceList = mPan.getDevicesMatchingConnectionStates(new int[] {
   1917                      BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
   1918             pw.println("--Connected and Disconnected Pan devices");
   1919             for (BluetoothDevice device: deviceList) {
   1920                 pw.println(device);
   1921             }
   1922         }
   1923     }
   1924 
   1925     private void dumpHeadsetConnectionState(PrintWriter pw,
   1926             BluetoothDevice device) {
   1927         switch (mHeadsetProxy.getConnectionState(device)) {
   1928             case BluetoothHeadset.STATE_CONNECTING:
   1929                 pw.println("getConnectionState() = STATE_CONNECTING");
   1930                 break;
   1931             case BluetoothHeadset.STATE_CONNECTED:
   1932                 pw.println("getConnectionState() = STATE_CONNECTED");
   1933                 break;
   1934             case BluetoothHeadset.STATE_DISCONNECTING:
   1935                 pw.println("getConnectionState() = STATE_DISCONNECTING");
   1936                 break;
   1937             case BluetoothHeadset.STATE_AUDIO_CONNECTED:
   1938                 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
   1939                 break;
   1940         }
   1941     }
   1942 
   1943     private void dumpApplicationServiceRecords(PrintWriter pw) {
   1944         pw.println("\n--Application Service Records--");
   1945         for (Integer handle : mServiceRecordToPid.keySet()) {
   1946             Integer pid = mServiceRecordToPid.get(handle).pid;
   1947             pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
   1948         }
   1949     }
   1950 
   1951     private void dumpAclConnectedDevices(PrintWriter pw) {
   1952         String[] devicesObjectPath = getKnownDevices();
   1953         pw.println("\n--ACL connected devices--");
   1954         if (devicesObjectPath != null) {
   1955             for (String device : devicesObjectPath) {
   1956                 pw.println(getAddressFromObjectPath(device));
   1957             }
   1958         }
   1959     }
   1960 
   1961     private void dumpKnownDevices(PrintWriter pw) {
   1962         pw.println("\n--Known devices--");
   1963         for (String address : mDeviceProperties.keySet()) {
   1964             int bondState = mBondState.getBondState(address);
   1965             pw.printf("%s %10s (%d) %s\n", address,
   1966                        toBondStateString(bondState),
   1967                        mBondState.getAttempt(address),
   1968                        getRemoteName(address));
   1969 
   1970             Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
   1971             if (uuidChannels == null) {
   1972                 pw.println("\tuuids = null");
   1973             } else {
   1974                 for (ParcelUuid uuid : uuidChannels.keySet()) {
   1975                     Integer channel = uuidChannels.get(uuid);
   1976                     if (channel == null) {
   1977                         pw.println("\t" + uuid);
   1978                     } else {
   1979                         pw.println("\t" + uuid + " RFCOMM channel = " + channel);
   1980                     }
   1981                 }
   1982             }
   1983             for (RemoteService service : mUuidCallbackTracker.keySet()) {
   1984                 if (service.address.equals(address)) {
   1985                     pw.println("\tPENDING CALLBACK: " + service.uuid);
   1986                 }
   1987             }
   1988         }
   1989     }
   1990 
   1991     private void getProfileProxy() {
   1992         mAdapter.getProfileProxy(mContext,
   1993                                  mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
   1994     }
   1995 
   1996     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
   1997         new BluetoothProfile.ServiceListener() {
   1998         public void onServiceConnected(int profile, BluetoothProfile proxy) {
   1999             if (profile == BluetoothProfile.HEADSET) {
   2000                 mHeadsetProxy = (BluetoothHeadset) proxy;
   2001             } else if (profile == BluetoothProfile.INPUT_DEVICE) {
   2002                 mInputDevice = (BluetoothInputDevice) proxy;
   2003             } else if (profile == BluetoothProfile.PAN) {
   2004                 mPan = (BluetoothPan) proxy;
   2005             }
   2006         }
   2007         public void onServiceDisconnected(int profile) {
   2008             if (profile == BluetoothProfile.HEADSET) {
   2009                 mHeadsetProxy = null;
   2010             } else if (profile == BluetoothProfile.INPUT_DEVICE) {
   2011                 mInputDevice = null;
   2012             } else if (profile == BluetoothProfile.PAN) {
   2013                 mPan = null;
   2014             }
   2015         }
   2016     };
   2017 
   2018     /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
   2019         if (pairable && discoverable)
   2020             return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
   2021         else if (pairable && !discoverable)
   2022             return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
   2023         else
   2024             return BluetoothAdapter.SCAN_MODE_NONE;
   2025     }
   2026 
   2027     /* package */ static String scanModeToBluezString(int mode) {
   2028         switch (mode) {
   2029         case BluetoothAdapter.SCAN_MODE_NONE:
   2030             return "off";
   2031         case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
   2032             return "connectable";
   2033         case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
   2034             return "discoverable";
   2035         }
   2036         return null;
   2037     }
   2038 
   2039     /*package*/ String getAddressFromObjectPath(String objectPath) {
   2040         String adapterObjectPath = mAdapterProperties.getObjectPath();
   2041         if (adapterObjectPath == null || objectPath == null) {
   2042             Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
   2043                     "  or deviceObjectPath:" + objectPath + " is null");
   2044             return null;
   2045         }
   2046         if (!objectPath.startsWith(adapterObjectPath)) {
   2047             Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
   2048                     "  is not a prefix of deviceObjectPath:" + objectPath +
   2049                     "bluetoothd crashed ?");
   2050             return null;
   2051         }
   2052         String address = objectPath.substring(adapterObjectPath.length());
   2053         if (address != null) return address.replace('_', ':');
   2054 
   2055         Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
   2056         return null;
   2057     }
   2058 
   2059     /*package*/ String getObjectPathFromAddress(String address) {
   2060         String path = mAdapterProperties.getObjectPath();
   2061         if (path == null) {
   2062             Log.e(TAG, "Error: Object Path is null");
   2063             return null;
   2064         }
   2065         path = path + address.replace(":", "_");
   2066         return path;
   2067     }
   2068 
   2069     /*package */ void setLinkTimeout(String address, int num_slots) {
   2070         String path = getObjectPathFromAddress(address);
   2071         boolean result = setLinkTimeoutNative(path, num_slots);
   2072 
   2073         if (!result) Log.d(TAG, "Set Link Timeout to " + num_slots + " slots failed");
   2074     }
   2075 
   2076     /**** Handlers for PAN  Profile ****/
   2077     // TODO: This needs to be converted to a state machine.
   2078 
   2079     public boolean isTetheringOn() {
   2080         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2081         synchronized (mBluetoothPanProfileHandler) {
   2082             return mBluetoothPanProfileHandler.isTetheringOn();
   2083         }
   2084     }
   2085 
   2086     /*package*/boolean allowIncomingTethering() {
   2087         synchronized (mBluetoothPanProfileHandler) {
   2088             return mBluetoothPanProfileHandler.allowIncomingTethering();
   2089         }
   2090     }
   2091 
   2092     public void setBluetoothTethering(boolean value) {
   2093         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2094         synchronized (mBluetoothPanProfileHandler) {
   2095             mBluetoothPanProfileHandler.setBluetoothTethering(value);
   2096         }
   2097     }
   2098 
   2099     public int getPanDeviceConnectionState(BluetoothDevice device) {
   2100         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2101         synchronized (mBluetoothPanProfileHandler) {
   2102             return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
   2103         }
   2104     }
   2105 
   2106     public boolean connectPanDevice(BluetoothDevice device) {
   2107         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   2108             "Need BLUETOOTH_ADMIN permission");
   2109         synchronized (mBluetoothPanProfileHandler) {
   2110             return mBluetoothPanProfileHandler.connectPanDevice(device);
   2111         }
   2112     }
   2113 
   2114     public List<BluetoothDevice> getConnectedPanDevices() {
   2115         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2116         synchronized (mBluetoothPanProfileHandler) {
   2117             return mBluetoothPanProfileHandler.getConnectedPanDevices();
   2118         }
   2119     }
   2120 
   2121     public List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
   2122             int[] states) {
   2123         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2124         synchronized (mBluetoothPanProfileHandler) {
   2125             return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
   2126         }
   2127     }
   2128 
   2129     public boolean disconnectPanDevice(BluetoothDevice device) {
   2130         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   2131             "Need BLUETOOTH_ADMIN permission");
   2132         synchronized (mBluetoothPanProfileHandler) {
   2133             return mBluetoothPanProfileHandler.disconnectPanDevice(device);
   2134         }
   2135     }
   2136 
   2137     /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
   2138                                                              String iface,
   2139                                                              int state,
   2140                                                              int role) {
   2141         synchronized (mBluetoothPanProfileHandler) {
   2142             mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
   2143         }
   2144     }
   2145 
   2146     /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
   2147                                                              int state, int role) {
   2148         synchronized (mBluetoothPanProfileHandler) {
   2149             mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
   2150         }
   2151     }
   2152 
   2153     /**** Handlers for Input Device Profile ****/
   2154     // This needs to be converted to state machine
   2155 
   2156     public boolean connectInputDevice(BluetoothDevice device) {
   2157         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   2158                                                 "Need BLUETOOTH_ADMIN permission");
   2159         BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
   2160         synchronized (mBluetoothInputProfileHandler) {
   2161             return mBluetoothInputProfileHandler.connectInputDevice(device, state);
   2162         }
   2163     }
   2164 
   2165     public boolean connectInputDeviceInternal(BluetoothDevice device) {
   2166         synchronized (mBluetoothInputProfileHandler) {
   2167             return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
   2168         }
   2169     }
   2170 
   2171     public boolean disconnectInputDevice(BluetoothDevice device) {
   2172         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   2173                                                 "Need BLUETOOTH_ADMIN permission");
   2174         BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
   2175         synchronized (mBluetoothInputProfileHandler) {
   2176             return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
   2177         }
   2178     }
   2179 
   2180     public boolean disconnectInputDeviceInternal(BluetoothDevice device) {
   2181         synchronized (mBluetoothInputProfileHandler) {
   2182             return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
   2183         }
   2184     }
   2185 
   2186     public int getInputDeviceConnectionState(BluetoothDevice device) {
   2187         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2188         synchronized (mBluetoothInputProfileHandler) {
   2189             return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
   2190         }
   2191     }
   2192 
   2193     public List<BluetoothDevice> getConnectedInputDevices() {
   2194         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2195         synchronized (mBluetoothInputProfileHandler) {
   2196             return mBluetoothInputProfileHandler.getConnectedInputDevices();
   2197         }
   2198     }
   2199 
   2200     public List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
   2201             int[] states) {
   2202         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2203         synchronized (mBluetoothInputProfileHandler) {
   2204             return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
   2205         }
   2206     }
   2207 
   2208 
   2209     public int getInputDevicePriority(BluetoothDevice device) {
   2210         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2211         synchronized (mBluetoothInputProfileHandler) {
   2212             return mBluetoothInputProfileHandler.getInputDevicePriority(device);
   2213         }
   2214     }
   2215 
   2216     public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
   2217         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   2218                                                 "Need BLUETOOTH_ADMIN permission");
   2219         synchronized (mBluetoothInputProfileHandler) {
   2220             return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
   2221         }
   2222     }
   2223 
   2224     /**
   2225      * Handle incoming profile acceptance for profiles handled by Bluetooth Service,
   2226      * currently PAN and HID. This also is the catch all for all rejections for profiles
   2227      * that is not supported.
   2228      *
   2229      * @param device - Bluetooth Device
   2230      * @param allow - true / false
   2231      * @return
   2232      */
   2233     public boolean allowIncomingProfileConnect(BluetoothDevice device, boolean allow) {
   2234         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
   2235                                                 "Need BLUETOOTH_ADMIN permission");
   2236         String address = device.getAddress();
   2237         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   2238             return false;
   2239         }
   2240 
   2241         Integer data = getAuthorizationAgentRequestData(address);
   2242         if (data == null) {
   2243             Log.w(TAG, "allowIncomingProfileConnect(" + device +
   2244                   ") called but no native data available");
   2245             return false;
   2246         }
   2247         if (DBG) log("allowIncomingProfileConnect: " + device + " : " + allow + " : " + data);
   2248         return setAuthorizationNative(address, allow, data.intValue());
   2249     }
   2250 
   2251     /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
   2252         synchronized (mBluetoothInputProfileHandler) {
   2253             return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
   2254         }
   2255     }
   2256 
   2257     /*package*/void handleInputDevicePropertyChange(String address, boolean connected) {
   2258         synchronized (mBluetoothInputProfileHandler) {
   2259             mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
   2260         }
   2261     }
   2262 
   2263     /**** Handlers for Health Device Profile ****/
   2264     // TODO: All these need to be converted to a state machine.
   2265 
   2266     public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
   2267                                             IBluetoothHealthCallback callback) {
   2268         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2269                 "Need BLUETOOTH permission");
   2270         synchronized (mBluetoothHealthProfileHandler) {
   2271                 return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback);
   2272         }
   2273     }
   2274 
   2275     public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
   2276         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2277                 "Need BLUETOOTH permission");
   2278         synchronized (mBluetoothHealthProfileHandler) {
   2279                 return mBluetoothHealthProfileHandler.unregisterAppConfiguration(config);
   2280         }
   2281     }
   2282 
   2283 
   2284     public boolean connectChannelToSource(BluetoothDevice device,
   2285             BluetoothHealthAppConfiguration config) {
   2286         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2287                 "Need BLUETOOTH permission");
   2288         synchronized (mBluetoothHealthProfileHandler) {
   2289             return mBluetoothHealthProfileHandler.connectChannelToSource(device,
   2290                     config);
   2291         }
   2292     }
   2293 
   2294     public boolean connectChannelToSink(BluetoothDevice device,
   2295             BluetoothHealthAppConfiguration config, int channelType) {
   2296         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2297                                                 "Need BLUETOOTH permission");
   2298         synchronized (mBluetoothHealthProfileHandler) {
   2299             return mBluetoothHealthProfileHandler.connectChannel(device, config,
   2300                     channelType);
   2301         }
   2302     }
   2303 
   2304     public boolean disconnectChannel(BluetoothDevice device,
   2305             BluetoothHealthAppConfiguration config, int id) {
   2306         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2307                 "Need BLUETOOTH permission");
   2308         synchronized (mBluetoothHealthProfileHandler) {
   2309             return mBluetoothHealthProfileHandler.disconnectChannel(device, config, id);
   2310         }
   2311     }
   2312 
   2313     public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
   2314             BluetoothHealthAppConfiguration config) {
   2315         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2316                 "Need BLUETOOTH permission");
   2317         synchronized (mBluetoothHealthProfileHandler) {
   2318             return mBluetoothHealthProfileHandler.getMainChannelFd(device, config);
   2319         }
   2320     }
   2321 
   2322     /*package*/ void onHealthDevicePropertyChanged(String devicePath,
   2323             String channelPath) {
   2324         synchronized (mBluetoothHealthProfileHandler) {
   2325             mBluetoothHealthProfileHandler.onHealthDevicePropertyChanged(devicePath,
   2326                     channelPath);
   2327         }
   2328     }
   2329 
   2330     /*package*/ void onHealthDeviceChannelChanged(String devicePath,
   2331             String channelPath, boolean exists) {
   2332         synchronized(mBluetoothHealthProfileHandler) {
   2333             mBluetoothHealthProfileHandler.onHealthDeviceChannelChanged(devicePath,
   2334                     channelPath, exists);
   2335         }
   2336     }
   2337 
   2338     /*package*/ void onHealthDeviceChannelConnectionError(int channelCode,
   2339             int newState) {
   2340         synchronized(mBluetoothHealthProfileHandler) {
   2341             mBluetoothHealthProfileHandler.onHealthDeviceChannelConnectionError(channelCode,
   2342                                                                                 newState);
   2343         }
   2344     }
   2345 
   2346     public int getHealthDeviceConnectionState(BluetoothDevice device) {
   2347         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2348                 "Need BLUETOOTH permission");
   2349         synchronized (mBluetoothHealthProfileHandler) {
   2350             return mBluetoothHealthProfileHandler.getHealthDeviceConnectionState(device);
   2351         }
   2352     }
   2353 
   2354     public List<BluetoothDevice> getConnectedHealthDevices() {
   2355         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2356                 "Need BLUETOOTH permission");
   2357         synchronized (mBluetoothHealthProfileHandler) {
   2358             return mBluetoothHealthProfileHandler.getConnectedHealthDevices();
   2359         }
   2360     }
   2361 
   2362     public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(
   2363             int[] states) {
   2364         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   2365                 "Need BLUETOOTH permission");
   2366         synchronized (mBluetoothHealthProfileHandler) {
   2367             return mBluetoothHealthProfileHandler.
   2368                     getHealthDevicesMatchingConnectionStates(states);
   2369         }
   2370     }
   2371 
   2372     /*package*/boolean notifyIncomingHidConnection(String address) {
   2373         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2374         if (state == null) {
   2375             return false;
   2376         }
   2377         Message msg = new Message();
   2378         msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING;
   2379         state.sendMessage(msg);
   2380         return true;
   2381     }
   2382 
   2383     public boolean connectHeadset(String address) {
   2384         if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
   2385 
   2386         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2387         if (state != null) {
   2388             Message msg = new Message();
   2389             msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
   2390             msg.obj = state;
   2391             mHfpProfileState.sendMessage(msg);
   2392             return true;
   2393         }
   2394         return false;
   2395     }
   2396 
   2397     public boolean disconnectHeadset(String address) {
   2398         if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
   2399 
   2400         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2401         if (state != null) {
   2402             Message msg = new Message();
   2403             msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
   2404             msg.obj = state;
   2405             mHfpProfileState.sendMessage(msg);
   2406             return true;
   2407         }
   2408         return false;
   2409     }
   2410 
   2411     public boolean connectSink(String address) {
   2412         if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
   2413 
   2414         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2415         if (state != null) {
   2416             Message msg = new Message();
   2417             msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
   2418             msg.obj = state;
   2419             mA2dpProfileState.sendMessage(msg);
   2420             return true;
   2421         }
   2422         return false;
   2423     }
   2424 
   2425     public boolean disconnectSink(String address) {
   2426         if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
   2427 
   2428         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2429         if (state != null) {
   2430             Message msg = new Message();
   2431             msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
   2432             msg.obj = state;
   2433             mA2dpProfileState.sendMessage(msg);
   2434             return true;
   2435         }
   2436         return false;
   2437     }
   2438 
   2439     BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) {
   2440         BluetoothDeviceProfileState state =
   2441             new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust);
   2442         mDeviceProfileState.put(address, state);
   2443         state.start();
   2444         return state;
   2445     }
   2446 
   2447     void removeProfileState(String address) {
   2448         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2449         if (state == null) return;
   2450 
   2451         state.quit();
   2452         mDeviceProfileState.remove(address);
   2453     }
   2454 
   2455     String[] getKnownDevices() {
   2456         String[] bonds = null;
   2457         String val = getProperty("Devices", true);
   2458         if (val != null) {
   2459             bonds = val.split(",");
   2460         }
   2461         return bonds;
   2462     }
   2463 
   2464     private void initProfileState() {
   2465         String[] bonds = null;
   2466         String val = getProperty("Devices", false);
   2467         if (val != null) {
   2468             bonds = val.split(",");
   2469         }
   2470         if (bonds == null) {
   2471             return;
   2472         }
   2473         for (String path : bonds) {
   2474             String address = getAddressFromObjectPath(path);
   2475             BluetoothDeviceProfileState state = addProfileState(address, false);
   2476         }
   2477     }
   2478 
   2479     private void autoConnect() {
   2480         synchronized (this) {
   2481             if (!mAllowConnect) {
   2482                 Log.d(TAG, "Not auto-connecting devices because of temporary BT on state.");
   2483                 return;
   2484             }
   2485         }
   2486 
   2487         String[] bonds = getKnownDevices();
   2488         if (bonds == null) {
   2489             return;
   2490         }
   2491         for (String path : bonds) {
   2492             String address = getAddressFromObjectPath(path);
   2493             BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2494             if (state != null) {
   2495                 Message msg = new Message();
   2496                 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
   2497                 state.sendMessage(msg);
   2498             }
   2499         }
   2500     }
   2501 
   2502     public boolean notifyIncomingConnection(String address, boolean rejected) {
   2503         synchronized (this) {
   2504             if (!mAllowConnect) {
   2505                 Log.d(TAG, "Not allowing incoming connection because of temporary BT on state.");
   2506                 return false;
   2507             }
   2508         }
   2509         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2510         if (state != null) {
   2511             Message msg = new Message();
   2512             if (rejected) {
   2513                 if (mA2dpService.getPriority(getRemoteDevice(address)) >=
   2514                     BluetoothProfile.PRIORITY_ON) {
   2515                     msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES;
   2516                     msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
   2517                     state.sendMessageDelayed(msg,
   2518                         BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY);
   2519                 }
   2520             } else {
   2521                 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
   2522                 state.sendMessage(msg);
   2523             }
   2524             return true;
   2525         }
   2526         return false;
   2527     }
   2528 
   2529     /*package*/ boolean notifyIncomingA2dpConnection(String address, boolean rejected) {
   2530         synchronized (this) {
   2531             if (!mAllowConnect) {
   2532                 Log.d(TAG, "Not allowing a2dp connection because of temporary BT on state.");
   2533                 return false;
   2534             }
   2535         }
   2536 
   2537        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
   2538        if (state != null) {
   2539            Message msg = new Message();
   2540            if (rejected) {
   2541                if (mHeadsetProxy.getPriority(getRemoteDevice(address)) >=
   2542                    BluetoothProfile.PRIORITY_ON) {
   2543                    msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES;
   2544                    msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
   2545                    state.sendMessageDelayed(msg,
   2546                              BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY);
   2547                }
   2548            } else {
   2549                msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
   2550                state.sendMessage(msg);
   2551            }
   2552            return true;
   2553        }
   2554        return false;
   2555     }
   2556 
   2557     /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
   2558         mA2dpService = a2dpService;
   2559     }
   2560 
   2561     /*package*/ Integer getAuthorizationAgentRequestData(String address) {
   2562         Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
   2563         return data;
   2564     }
   2565 
   2566     public void sendProfileStateMessage(int profile, int cmd) {
   2567         Message msg = new Message();
   2568         msg.what = cmd;
   2569         if (profile == BluetoothProfileState.HFP) {
   2570             mHfpProfileState.sendMessage(msg);
   2571         } else if (profile == BluetoothProfileState.A2DP) {
   2572             mA2dpProfileState.sendMessage(msg);
   2573         }
   2574     }
   2575 
   2576     public int getAdapterConnectionState() {
   2577         return mAdapterConnectionState;
   2578     }
   2579 
   2580     public int getProfileConnectionState(int profile) {
   2581         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   2582 
   2583         Pair<Integer, Integer> state = mProfileConnectionState.get(profile);
   2584         if (state == null) return BluetoothProfile.STATE_DISCONNECTED;
   2585 
   2586         return state.first;
   2587     }
   2588 
   2589     private void updateProfileConnectionState(int profile, int newState, int oldState) {
   2590         // mProfileConnectionState is a hashmap -
   2591         // <Integer, Pair<Integer, Integer>>
   2592         // The key is the profile, the value is a pair. first element
   2593         // is the state and the second element is the number of devices
   2594         // in that state.
   2595         int numDev = 1;
   2596         int newHashState = newState;
   2597         boolean update = true;
   2598 
   2599         // The following conditions are considered in this function:
   2600         // 1. If there is no record of profile and state - update
   2601         // 2. If a new device's state is current hash state - increment
   2602         //    number of devices in the state.
   2603         // 3. If a state change has happened to Connected or Connecting
   2604         //    (if current state is not connected), update.
   2605         // 4. If numDevices is 1 and that device state is being updated, update
   2606         // 5. If numDevices is > 1 and one of the devices is changing state,
   2607         //    decrement numDevices but maintain oldState if it is Connected or
   2608         //    Connecting
   2609         Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
   2610         if (stateNumDev != null) {
   2611             int currHashState = stateNumDev.first;
   2612             numDev = stateNumDev.second;
   2613 
   2614             if (newState == currHashState) {
   2615                 numDev ++;
   2616             } else if (newState == BluetoothProfile.STATE_CONNECTED ||
   2617                    (newState == BluetoothProfile.STATE_CONNECTING &&
   2618                     currHashState != BluetoothProfile.STATE_CONNECTED)) {
   2619                  numDev = 1;
   2620             } else if (numDev == 1 && oldState == currHashState) {
   2621                  update = true;
   2622             } else if (numDev > 1 && oldState == currHashState) {
   2623                  numDev --;
   2624 
   2625                  if (currHashState == BluetoothProfile.STATE_CONNECTED ||
   2626                      currHashState == BluetoothProfile.STATE_CONNECTING) {
   2627                     newHashState = currHashState;
   2628                  }
   2629             } else {
   2630                  update = false;
   2631             }
   2632         }
   2633 
   2634         if (update) {
   2635             mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
   2636                     numDev));
   2637         }
   2638     }
   2639 
   2640     public synchronized void sendConnectionStateChange(BluetoothDevice
   2641             device, int profile, int state, int prevState) {
   2642         // Since this is a binder call check if Bluetooth is on still
   2643         if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
   2644 
   2645         if (!validateProfileConnectionState(state) ||
   2646                 !validateProfileConnectionState(prevState)) {
   2647             // Previously, an invalid state was broadcast anyway,
   2648             // with the invalid state converted to -1 in the intent.
   2649             // Better to log an error and not send an intent with
   2650             // invalid contents or set mAdapterConnectionState to -1.
   2651             Log.e(TAG, "Error in sendConnectionStateChange: "
   2652                     + "prevState " + prevState + " state " + state);
   2653             return;
   2654         }
   2655 
   2656         updateProfileConnectionState(profile, state, prevState);
   2657 
   2658         if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
   2659             mAdapterConnectionState = state;
   2660 
   2661             if (state == BluetoothProfile.STATE_DISCONNECTED) {
   2662                 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.ALL_DEVICES_DISCONNECTED);
   2663             }
   2664 
   2665             Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
   2666             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   2667             intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
   2668                     convertToAdapterState(state));
   2669             intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
   2670                     convertToAdapterState(prevState));
   2671             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   2672             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
   2673             Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
   2674                     + prevState + " -> " + state);
   2675         }
   2676     }
   2677 
   2678     private boolean validateProfileConnectionState(int state) {
   2679         return (state == BluetoothProfile.STATE_DISCONNECTED ||
   2680                 state == BluetoothProfile.STATE_CONNECTING ||
   2681                 state == BluetoothProfile.STATE_CONNECTED ||
   2682                 state == BluetoothProfile.STATE_DISCONNECTING);
   2683     }
   2684 
   2685     private int convertToAdapterState(int state) {
   2686         switch (state) {
   2687             case BluetoothProfile.STATE_DISCONNECTED:
   2688                 return BluetoothAdapter.STATE_DISCONNECTED;
   2689             case BluetoothProfile.STATE_DISCONNECTING:
   2690                 return BluetoothAdapter.STATE_DISCONNECTING;
   2691             case BluetoothProfile.STATE_CONNECTED:
   2692                 return BluetoothAdapter.STATE_CONNECTED;
   2693             case BluetoothProfile.STATE_CONNECTING:
   2694                 return BluetoothAdapter.STATE_CONNECTING;
   2695         }
   2696         Log.e(TAG, "Error in convertToAdapterState");
   2697         return -1;
   2698     }
   2699 
   2700     private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
   2701         switch (prevState) {
   2702             case BluetoothProfile.STATE_CONNECTING:
   2703                 mProfilesConnecting--;
   2704                 break;
   2705 
   2706             case BluetoothProfile.STATE_CONNECTED:
   2707                 mProfilesConnected--;
   2708                 break;
   2709 
   2710             case BluetoothProfile.STATE_DISCONNECTING:
   2711                 mProfilesDisconnecting--;
   2712                 break;
   2713         }
   2714 
   2715         switch (state) {
   2716             case BluetoothProfile.STATE_CONNECTING:
   2717                 mProfilesConnecting++;
   2718                 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
   2719 
   2720             case BluetoothProfile.STATE_CONNECTED:
   2721                 mProfilesConnected++;
   2722                 return (mProfilesConnected == 1);
   2723 
   2724             case BluetoothProfile.STATE_DISCONNECTING:
   2725                 mProfilesDisconnecting++;
   2726                 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
   2727 
   2728             case BluetoothProfile.STATE_DISCONNECTED:
   2729                 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
   2730 
   2731             default:
   2732                 return true;
   2733         }
   2734     }
   2735 
   2736     private void createIncomingConnectionStateFile() {
   2737         File f = new File(INCOMING_CONNECTION_FILE);
   2738         if (!f.exists()) {
   2739             try {
   2740                 f.createNewFile();
   2741             } catch (IOException e) {
   2742                 Log.e(TAG, "IOException: cannot create file");
   2743             }
   2744         }
   2745     }
   2746 
   2747     /** @hide */
   2748     public Pair<Integer, String> getIncomingState(String address) {
   2749         if (mIncomingConnections.isEmpty()) {
   2750             createIncomingConnectionStateFile();
   2751             readIncomingConnectionState();
   2752         }
   2753         return mIncomingConnections.get(address);
   2754     }
   2755 
   2756     private void readIncomingConnectionState() {
   2757         synchronized(mIncomingConnections) {
   2758             FileInputStream fstream = null;
   2759             try {
   2760               fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
   2761               DataInputStream in = new DataInputStream(fstream);
   2762               BufferedReader file = new BufferedReader(new InputStreamReader(in));
   2763               String line;
   2764               while((line = file.readLine()) != null) {
   2765                   line = line.trim();
   2766                   if (line.length() == 0) continue;
   2767                   String[] value = line.split(",");
   2768                   if (value != null && value.length == 3) {
   2769                       Integer val1 = Integer.parseInt(value[1]);
   2770                       Pair<Integer, String> val = new Pair(val1, value[2]);
   2771                       mIncomingConnections.put(value[0], val);
   2772                   }
   2773               }
   2774             } catch (FileNotFoundException e) {
   2775                 log("FileNotFoundException: readIncomingConnectionState" + e.toString());
   2776             } catch (IOException e) {
   2777                 log("IOException: readIncomingConnectionState" + e.toString());
   2778             } finally {
   2779                 if (fstream != null) {
   2780                     try {
   2781                         fstream.close();
   2782                     } catch (IOException e) {
   2783                         // Ignore
   2784                     }
   2785                 }
   2786             }
   2787         }
   2788     }
   2789 
   2790     private void truncateIncomingConnectionFile() {
   2791         RandomAccessFile r = null;
   2792         try {
   2793             r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
   2794             r.setLength(0);
   2795         } catch (FileNotFoundException e) {
   2796             log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
   2797         } catch (IOException e) {
   2798             log("IOException: truncateIncomingConnectionState" + e.toString());
   2799         } finally {
   2800             if (r != null) {
   2801                 try {
   2802                     r.close();
   2803                 } catch (IOException e) {
   2804                     // ignore
   2805                  }
   2806             }
   2807         }
   2808     }
   2809 
   2810     /** @hide */
   2811     public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
   2812         synchronized(mIncomingConnections) {
   2813             mIncomingConnections.put(address, data);
   2814 
   2815             truncateIncomingConnectionFile();
   2816             BufferedWriter out = null;
   2817             StringBuilder value = new StringBuilder();
   2818             try {
   2819                 out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
   2820                 for (String devAddress: mIncomingConnections.keySet()) {
   2821                   Pair<Integer, String> val = mIncomingConnections.get(devAddress);
   2822                   value.append(devAddress);
   2823                   value.append(",");
   2824                   value.append(val.first.toString());
   2825                   value.append(",");
   2826                   value.append(val.second);
   2827                   value.append("\n");
   2828                 }
   2829                 out.write(value.toString());
   2830             } catch (FileNotFoundException e) {
   2831                 log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
   2832             } catch (IOException e) {
   2833                 log("IOException: writeIncomingConnectionState" + e.toString());
   2834             } finally {
   2835                 if (out != null) {
   2836                     try {
   2837                         out.close();
   2838                     } catch (IOException e) {
   2839                         // Ignore
   2840                     }
   2841                 }
   2842             }
   2843         }
   2844     }
   2845 
   2846     private static void log(String msg) {
   2847         Log.d(TAG, msg);
   2848     }
   2849 
   2850     private native static void classInitNative();
   2851     private native void initializeNativeDataNative();
   2852     private native boolean setupNativeDataNative();
   2853     private native boolean tearDownNativeDataNative();
   2854     private native void cleanupNativeDataNative();
   2855     /*package*/ native String getAdapterPathNative();
   2856 
   2857     private native int isEnabledNative();
   2858     /*package*/ native int enableNative();
   2859     /*package*/ native int disableNative();
   2860 
   2861     /*package*/ native Object[] getAdapterPropertiesNative();
   2862     private native Object[] getDevicePropertiesNative(String objectPath);
   2863     private native boolean setAdapterPropertyStringNative(String key, String value);
   2864     private native boolean setAdapterPropertyIntegerNative(String key, int value);
   2865     private native boolean setAdapterPropertyBooleanNative(String key, int value);
   2866 
   2867     private native boolean startDiscoveryNative();
   2868     private native boolean stopDiscoveryNative();
   2869 
   2870     private native boolean createPairedDeviceNative(String address, int timeout_ms);
   2871     private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
   2872     private native byte[] readAdapterOutOfBandDataNative();
   2873 
   2874     private native boolean cancelDeviceCreationNative(String address);
   2875     private native boolean removeDeviceNative(String objectPath);
   2876     private native int getDeviceServiceChannelNative(String objectPath, String uuid,
   2877             int attributeId);
   2878 
   2879     private native boolean cancelPairingUserInputNative(String address, int nativeData);
   2880     private native boolean setPinNative(String address, String pin, int nativeData);
   2881     private native boolean setPasskeyNative(String address, int passkey, int nativeData);
   2882     private native boolean setPairingConfirmationNative(String address, boolean confirm,
   2883             int nativeData);
   2884     private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
   2885                                                         byte[] randomizer, int nativeData);
   2886 
   2887     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
   2888             int value);
   2889     private native boolean setDevicePropertyStringNative(String objectPath, String key,
   2890             String value);
   2891     private native boolean createDeviceNative(String address);
   2892     /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
   2893 
   2894     private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
   2895             short channel);
   2896     private native boolean removeServiceRecordNative(int handle);
   2897     private native boolean setLinkTimeoutNative(String path, int num_slots);
   2898 
   2899     native boolean connectInputDeviceNative(String path);
   2900     native boolean disconnectInputDeviceNative(String path);
   2901 
   2902     native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
   2903     native boolean connectPanDeviceNative(String path, String dstRole);
   2904     native boolean disconnectPanDeviceNative(String path);
   2905     native boolean disconnectPanServerDeviceNative(String path,
   2906             String address, String iface);
   2907 
   2908     private native int[] addReservedServiceRecordsNative(int[] uuuids);
   2909     private native boolean removeReservedServiceRecordsNative(int[] handles);
   2910 
   2911     // Health API
   2912     native String registerHealthApplicationNative(int dataType, String role, String name,
   2913             String channelType);
   2914     native String registerHealthApplicationNative(int dataType, String role, String name);
   2915     native boolean unregisterHealthApplicationNative(String path);
   2916     native boolean createChannelNative(String devicePath, String appPath, String channelType,
   2917                                        int code);
   2918     native boolean destroyChannelNative(String devicePath, String channelpath, int code);
   2919     native String getMainChannelNative(String path);
   2920     native String getChannelApplicationNative(String channelPath);
   2921     native ParcelFileDescriptor getChannelFdNative(String channelPath);
   2922     native boolean releaseChannelFdNative(String channelPath);
   2923     native boolean setAuthorizationNative(String address, boolean value, int data);
   2924 }
   2925