OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / server / BluetoothService.java
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.BluetoothHeadset;
31 import android.bluetooth.BluetoothDeviceProfileState;
32 import android.bluetooth.BluetoothProfileState;
33 import android.bluetooth.BluetoothSocket;
34 import android.bluetooth.BluetoothUuid;
35 import android.bluetooth.IBluetooth;
36 import android.bluetooth.IBluetoothCallback;
37 import android.content.BroadcastReceiver;
38 import android.content.ContentResolver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.SharedPreferences;
43 import android.os.Binder;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.os.Message;
47 import android.os.ParcelUuid;
48 import android.os.RemoteException;
49 import android.os.ServiceManager;
50 import android.os.SystemService;
51 import android.provider.Settings;
52 import android.util.Log;
53 import android.util.Pair;
54
55 import com.android.internal.app.IBatteryStats;
56
57 import java.io.BufferedInputStream;
58 import java.io.BufferedReader;
59 import java.io.BufferedWriter;
60 import java.io.DataInputStream;
61 import java.io.File;
62 import java.io.FileDescriptor;
63 import java.io.FileInputStream;
64 import java.io.FileNotFoundException;
65 import java.io.FileOutputStream;
66 import java.io.FileWriter;
67 import java.io.IOException;
68 import java.io.InputStreamReader;
69 import java.io.PrintWriter;
70 import java.io.UnsupportedEncodingException;
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.HashMap;
74 import java.util.Iterator;
75 import java.util.Map;
76
77 public class BluetoothService extends IBluetooth.Stub {
78     private static final String TAG = "BluetoothService";
79     private static final boolean DBG = true;
80
81     private int mNativeData;
82     private BluetoothEventLoop mEventLoop;
83     private boolean mIsAirplaneSensitive;
84     private boolean mIsAirplaneToggleable;
85     private int mBluetoothState;
86     private boolean mRestart = false;  // need to call enable() after disable()
87     private boolean mIsDiscovering;
88
89     private BluetoothAdapter mAdapter;  // constant after init()
90     private final BondState mBondState = new BondState();  // local cache of bondings
91     private final IBatteryStats mBatteryStats;
92     private final Context mContext;
93
94     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
95     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
96
97     private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
98     private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
99
100     private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
101     private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
102
103     private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
104     private static final int MESSAGE_FINISH_DISABLE = 2;
105     private static final int MESSAGE_UUID_INTENT = 3;
106     private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
107     private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 5;
108
109     // The time (in millisecs) to delay the pairing attempt after the first
110     // auto pairing attempt fails. We use an exponential delay with
111     // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
112     // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
113     private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
114     private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
115
116     // The timeout used to sent the UUIDs Intent
117     // This timeout should be greater than the page timeout
118     private static final int UUID_INTENT_DELAY = 6000;
119
120     /** Always retrieve RFCOMM channel for these SDP UUIDs */
121     private static final ParcelUuid[] RFCOMM_UUIDS = {
122             BluetoothUuid.Handsfree,
123             BluetoothUuid.HSP,
124             BluetoothUuid.ObexObjectPush };
125
126     // TODO(): Optimize all these string handling
127     private final Map<String, String> mAdapterProperties;
128     private final HashMap<String, Map<String, String>> mDeviceProperties;
129
130     private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
131     private final ArrayList<String> mUuidIntentTracker;
132     private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
133
134     private final HashMap<Integer, Integer> mServiceRecordToPid;
135
136     private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
137     private final BluetoothProfileState mA2dpProfileState;
138     private final BluetoothProfileState mHfpProfileState;
139
140     private BluetoothA2dpService mA2dpService;
141     private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
142
143     private static String mDockAddress;
144     private String mDockPin;
145
146     private static class RemoteService {
147         public String address;
148         public ParcelUuid uuid;
149         public RemoteService(String address, ParcelUuid uuid) {
150             this.address = address;
151             this.uuid = uuid;
152         }
153         @Override
154         public boolean equals(Object o) {
155             if (o instanceof RemoteService) {
156                 RemoteService service = (RemoteService)o;
157                 return address.equals(service.address) && uuid.equals(service.uuid);
158             }
159             return false;
160         }
161
162         @Override
163         public int hashCode() {
164             int hash = 1;
165             hash = hash * 31 + (address == null ? 0 : address.hashCode());
166             hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
167             return hash;
168         }
169     }
170
171     static {
172         classInitNative();
173     }
174
175     public BluetoothService(Context context) {
176         mContext = context;
177
178         // Need to do this in place of:
179         // mBatteryStats = BatteryStatsService.getService();
180         // Since we can not import BatteryStatsService from here. This class really needs to be
181         // moved to java/services/com/android/server/
182         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
183
184         initializeNativeDataNative();
185
186         if (isEnabledNative() == 1) {
187             Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
188             disableNative();
189         }
190
191         mBluetoothState = BluetoothAdapter.STATE_OFF;
192         mIsDiscovering = false;
193         mAdapterProperties = new HashMap<String, String>();
194         mDeviceProperties = new HashMap<String, Map<String,String>>();
195
196         mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
197         mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
198         mUuidIntentTracker = new ArrayList<String>();
199         mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
200         mServiceRecordToPid = new HashMap<Integer, Integer>();
201         mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
202         mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
203         mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
204
205         mHfpProfileState.start();
206         mA2dpProfileState.start();
207
208         IntentFilter filter = new IntentFilter();
209         registerForAirplaneMode(filter);
210
211         filter.addAction(Intent.ACTION_DOCK_EVENT);
212         mContext.registerReceiver(mReceiver, filter);
213     }
214
215     public static synchronized String readDockBluetoothAddress() {
216         if (mDockAddress != null) return mDockAddress;
217
218         BufferedInputStream file = null;
219         String dockAddress;
220         try {
221             file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
222             byte[] address = new byte[17];
223             file.read(address);
224             dockAddress = new String(address);
225             dockAddress = dockAddress.toUpperCase();
226             if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
227                 mDockAddress = dockAddress;
228                 return mDockAddress;
229             } else {
230                 log("CheckBluetoothAddress failed for car dock address:" + dockAddress);
231             }
232         } catch (FileNotFoundException e) {
233             log("FileNotFoundException while trying to read dock address");
234         } catch (IOException e) {
235             log("IOException while trying to read dock address");
236         } finally {
237             if (file != null) {
238                 try {
239                     file.close();
240                 } catch (IOException e) {
241                     // Ignore
242                 }
243             }
244         }
245         mDockAddress = null;
246         return null;
247     }
248
249     private synchronized boolean writeDockPin() {
250         BufferedWriter out = null;
251         try {
252             out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
253
254             // Generate a random 4 digit pin between 0000 and 9999
255             // This is not truly random but good enough for our purposes.
256             int pin = (int) Math.floor(Math.random() * 10000);
257
258             mDockPin = String.format("%04d", pin);
259             out.write(mDockPin);
260             return true;
261         } catch (FileNotFoundException e) {
262             log("FileNotFoundException while trying to write dock pairing pin");
263         } catch (IOException e) {
264             log("IOException while while trying to write dock pairing pin");
265         } finally {
266             if (out != null) {
267                 try {
268                     out.close();
269                 } catch (IOException e) {
270                     // Ignore
271                 }
272             }
273         }
274         mDockPin = null;
275         return false;
276     }
277
278     /*package*/ synchronized String getDockPin() {
279         return mDockPin;
280     }
281
282     public synchronized void initAfterRegistration() {
283         mAdapter = BluetoothAdapter.getDefaultAdapter();
284         mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
285     }
286
287     @Override
288     protected void finalize() throws Throwable {
289         mContext.unregisterReceiver(mReceiver);
290         try {
291             cleanupNativeDataNative();
292         } finally {
293             super.finalize();
294         }
295     }
296
297     public boolean isEnabled() {
298         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
299         return isEnabledInternal();
300     }
301
302     private boolean isEnabledInternal() {
303         return mBluetoothState == BluetoothAdapter.STATE_ON;
304     }
305
306     public int getBluetoothState() {
307         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
308         return mBluetoothState;
309     }
310
311
312     /**
313      * Bring down bluetooth and disable BT in settings. Returns true on success.
314      */
315     public boolean disable() {
316         return disable(true);
317     }
318
319     /**
320      * Bring down bluetooth. Returns true on success.
321      *
322      * @param saveSetting If true, persist the new setting
323      */
324     public synchronized boolean disable(boolean saveSetting) {
325         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
326
327         switch (mBluetoothState) {
328         case BluetoothAdapter.STATE_OFF:
329             return true;
330         case BluetoothAdapter.STATE_ON:
331             break;
332         default:
333             return false;
334         }
335         if (mEnableThread != null && mEnableThread.isAlive()) {
336             return false;
337         }
338         setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
339         mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
340
341         // Allow 3 seconds for profiles to gracefully disconnect
342         // TODO: Introduce a callback mechanism so that each profile can notify
343         // BluetoothService when it is done shutting down
344         mHandler.sendMessageDelayed(
345                 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
346         return true;
347     }
348
349
350     private synchronized void finishDisable(boolean saveSetting) {
351         if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
352             return;
353         }
354         mEventLoop.stop();
355         tearDownNativeDataNative();
356         disableNative();
357
358         // mark in progress bondings as cancelled
359         for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
360             mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
361                                     BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
362         }
363
364         // update mode
365         Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
366         intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
367         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
368
369         mIsDiscovering = false;
370         mAdapterProperties.clear();
371         mServiceRecordToPid.clear();
372
373         if (saveSetting) {
374             persistBluetoothOnSetting(false);
375         }
376
377         setBluetoothState(BluetoothAdapter.STATE_OFF);
378
379         // Log bluetooth off to battery stats.
380         long ident = Binder.clearCallingIdentity();
381         try {
382             mBatteryStats.noteBluetoothOff();
383         } catch (RemoteException e) {
384         } finally {
385             Binder.restoreCallingIdentity(ident);
386         }
387
388         if (mRestart) {
389             mRestart = false;
390             enable();
391         }
392     }
393
394     /** Bring up BT and persist BT on in settings */
395     public boolean enable() {
396         return enable(true);
397     }
398
399     /**
400      * Enable this Bluetooth device, asynchronously.
401      * This turns on/off the underlying hardware.
402      *
403      * @param saveSetting If true, persist the new state of BT in settings
404      * @return True on success (so far)
405      */
406     public synchronized boolean enable(boolean saveSetting) {
407         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
408                                                 "Need BLUETOOTH_ADMIN permission");
409
410         // Airplane mode can prevent Bluetooth radio from being turned on.
411         if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
412             return false;
413         }
414         if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
415             return false;
416         }
417         if (mEnableThread != null && mEnableThread.isAlive()) {
418             return false;
419         }
420         setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
421         mEnableThread = new EnableThread(saveSetting);
422         mEnableThread.start();
423         return true;
424     }
425
426     /** Forcibly restart Bluetooth if it is on */
427     /* package */ synchronized void restart() {
428         if (mBluetoothState != BluetoothAdapter.STATE_ON) {
429             return;
430         }
431         mRestart = true;
432         if (!disable(false)) {
433             mRestart = false;
434         }
435     }
436
437     private synchronized void setBluetoothState(int state) {
438         if (state == mBluetoothState) {
439             return;
440         }
441
442         if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
443
444         Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
445         intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
446         intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
447         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
448
449         mBluetoothState = state;
450
451         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
452     }
453
454     private final Handler mHandler = new Handler() {
455         @Override
456         public void handleMessage(Message msg) {
457             switch (msg.what) {
458             case MESSAGE_REGISTER_SDP_RECORDS:
459                 if (!isEnabledInternal()) {
460                     return;
461                 }
462                 // SystemService.start() forks sdptool to register service
463                 // records. It can fail to register some records if it is
464                 // forked multiple times in a row, probably because there is
465                 // some race in sdptool or bluez when operated in parallel.
466                 // As a workaround, delay 500ms between each fork of sdptool.
467                 // TODO: Don't fork sdptool in order to register service
468                 // records, use a DBUS call instead.
469                 switch (msg.arg1) {
470                 case 1:
471                     Log.d(TAG, "Registering hfag record");
472                     SystemService.start("hfag");
473                     mHandler.sendMessageDelayed(
474                             mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500);
475                     break;
476                 case 2:
477                     Log.d(TAG, "Registering hsag record");
478                     SystemService.start("hsag");
479                     mHandler.sendMessageDelayed(
480                             mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500);
481                     break;
482                 case 3:
483                     Log.d(TAG, "Registering opush record");
484                     SystemService.start("opush");
485                     mHandler.sendMessageDelayed(
486                             mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500);
487                     break;
488                 case 4:
489                     Log.d(TAG, "Registering pbap record");
490                     SystemService.start("pbap");
491                     break;
492                 }
493                 break;
494             case MESSAGE_FINISH_DISABLE:
495                 finishDisable(msg.arg1 != 0);
496                 break;
497             case MESSAGE_UUID_INTENT:
498                 String address = (String)msg.obj;
499                 if (address != null) {
500                     sendUuidIntent(address);
501                     makeServiceChannelCallbacks(address);
502                 }
503                 break;
504             case MESSAGE_DISCOVERABLE_TIMEOUT:
505                 int mode = msg.arg1;
506                 if (isEnabledInternal()) {
507                     // TODO: Switch back to the previous scan mode
508                     // This is ok for now, because we only use
509                     // CONNECTABLE and CONNECTABLE_DISCOVERABLE
510                     setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
511                 }
512                 break;
513             case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
514                 address = (String)msg.obj;
515                 if (address != null) {
516                     createBond(address);
517                     return;
518                 }
519                 break;
520             }
521         }
522     };
523
524     private EnableThread mEnableThread;
525
526     private class EnableThread extends Thread {
527         private final boolean mSaveSetting;
528         public EnableThread(boolean saveSetting) {
529             mSaveSetting = saveSetting;
530         }
531         public void run() {
532             boolean res = (enableNative() == 0);
533             if (res) {
534                 int retryCount = 2;
535                 boolean running = false;
536                 while ((retryCount-- > 0) && !running) {
537                     mEventLoop.start();
538                     // it may take a momement for the other thread to do its
539                     // thing.  Check periodically for a while.
540                     int pollCount = 5;
541                     while ((pollCount-- > 0) && !running) {
542                         if (mEventLoop.isEventLoopRunning()) {
543                             running = true;
544                             break;
545                         }
546                         try {
547                             Thread.sleep(100);
548                         } catch (InterruptedException e) {}
549                     }
550                 }
551                 if (!running) {
552                     log("bt EnableThread giving up");
553                     res = false;
554                     disableNative();
555                 }
556             }
557
558
559             if (res) {
560                 if (!setupNativeDataNative()) {
561                     return;
562                 }
563                 if (mSaveSetting) {
564                     persistBluetoothOnSetting(true);
565                 }
566                 mIsDiscovering = false;
567                 mBondState.readAutoPairingData();
568                 mBondState.loadBondState();
569                 initProfileState();
570                 mHandler.sendMessageDelayed(
571                         mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
572
573                 // Log bluetooth on to battery stats.
574                 long ident = Binder.clearCallingIdentity();
575                 try {
576                     mBatteryStats.noteBluetoothOn();
577                 } catch (RemoteException e) {
578                 } finally {
579                     Binder.restoreCallingIdentity(ident);
580                 }
581             }
582
583             mEnableThread = null;
584
585             setBluetoothState(res ?
586                               BluetoothAdapter.STATE_ON :
587                               BluetoothAdapter.STATE_OFF);
588
589             if (res) {
590                 // Update mode
591                 String[] propVal = {"Pairable", getProperty("Pairable")};
592                 mEventLoop.onPropertyChanged(propVal);
593             }
594
595             if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
596                 disable(false);
597             }
598
599         }
600     }
601
602     private void persistBluetoothOnSetting(boolean bluetoothOn) {
603         long origCallerIdentityToken = Binder.clearCallingIdentity();
604         Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
605                 bluetoothOn ? 1 : 0);
606         Binder.restoreCallingIdentity(origCallerIdentityToken);
607     }
608
609     /*package*/ synchronized boolean attemptAutoPair(String address) {
610         if (!mBondState.hasAutoPairingFailed(address) &&
611                 !mBondState.isAutoPairingBlacklisted(address)) {
612             mBondState.attempt(address);
613             setPin(address, BluetoothDevice.convertPinToBytes("0000"));
614             return true;
615         }
616         return false;
617     }
618
619     /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
620         if (result == BluetoothDevice.BOND_SUCCESS) {
621             setBondState(address, BluetoothDevice.BOND_BONDED);
622             if (mBondState.isAutoPairingAttemptsInProgress(address)) {
623                 mBondState.clearPinAttempts(address);
624             }
625         } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
626                 mBondState.getAttempt(address) == 1) {
627             mBondState.addAutoPairingFailure(address);
628             pairingAttempt(address, result);
629         } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
630               mBondState.isAutoPairingAttemptsInProgress(address)) {
631             pairingAttempt(address, result);
632         } else {
633             setBondState(address, BluetoothDevice.BOND_NONE, result);
634             if (mBondState.isAutoPairingAttemptsInProgress(address)) {
635                 mBondState.clearPinAttempts(address);
636             }
637         }
638     }
639
640     /*package*/ synchronized String getPendingOutgoingBonding() {
641         return mBondState.getPendingOutgoingBonding();
642     }
643
644     private void pairingAttempt(String address, int result) {
645         // This happens when our initial guess of "0000" as the pass key
646         // fails. Try to create the bond again and display the pin dialog
647         // to the user. Use back-off while posting the delayed
648         // message. The initial value is
649         // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
650         // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
651         // reached, display an error to the user.
652         int attempt = mBondState.getAttempt(address);
653         if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
654                     MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
655             mBondState.clearPinAttempts(address);
656             setBondState(address, BluetoothDevice.BOND_NONE, result);
657             return;
658         }
659
660         Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
661         message.obj = address;
662         boolean postResult =  mHandler.sendMessageDelayed(message,
663                                         attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
664         if (!postResult) {
665             mBondState.clearPinAttempts(address);
666             setBondState(address,
667                     BluetoothDevice.BOND_NONE, result);
668             return;
669         }
670         mBondState.attempt(address);
671     }
672
673     /** local cache of bonding state.
674     /* we keep our own state to track the intermediate state BONDING, which
675     /* bluez does not track.
676      * All addresses must be passed in upper case.
677      */
678     public class BondState {
679         private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
680         private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
681
682         private static final String AUTO_PAIRING_BLACKLIST =
683             "/etc/bluetooth/auto_pairing.conf";
684         private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST =
685             "/data/misc/bluetooth/dynamic_auto_pairing.conf";
686         private ArrayList<String>  mAutoPairingAddressBlacklist;
687         private ArrayList<String> mAutoPairingExactNameBlacklist;
688         private ArrayList<String> mAutoPairingPartialNameBlacklist;
689         // Addresses added to blacklist dynamically based on usage.
690         private ArrayList<String> mAutoPairingDynamicAddressBlacklist;
691
692
693         // If this is an outgoing connection, store the address.
694         // There can be only 1 pending outgoing connection at a time,
695         private String mPendingOutgoingBonding;
696
697         private synchronized void setPendingOutgoingBonding(String address) {
698             mPendingOutgoingBonding = address;
699         }
700
701         public synchronized String getPendingOutgoingBonding() {
702             return mPendingOutgoingBonding;
703         }
704
705         public synchronized void loadBondState() {
706             if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
707                 return;
708             }
709             String []bonds = null;
710             String val = getPropertyInternal("Devices");
711             if (val != null) {
712                 bonds = val.split(",");
713             }
714             if (bonds == null) {
715                 return;
716             }
717             mState.clear();
718             if (DBG) log("found " + bonds.length + " bonded devices");
719             for (String device : bonds) {
720                 mState.put(getAddressFromObjectPath(device).toUpperCase(),
721                         BluetoothDevice.BOND_BONDED);
722             }
723         }
724
725         public synchronized void setBondState(String address, int state) {
726             setBondState(address, state, 0);
727         }
728
729         /** reason is ignored unless state == BOND_NOT_BONDED */
730         public synchronized void setBondState(String address, int state, int reason) {
731             int oldState = getBondState(address);
732             if (oldState == state) {
733                 return;
734             }
735
736             // Check if this was an pending outgoing bonding.
737             // If yes, reset the state.
738             if (oldState == BluetoothDevice.BOND_BONDING) {
739                 if (address.equals(mPendingOutgoingBonding)) {
740                     mPendingOutgoingBonding = null;
741                 }
742             }
743
744             if (state == BluetoothDevice.BOND_BONDED) {
745                 addProfileState(address);
746             } else if (state == BluetoothDevice.BOND_NONE) {
747                 removeProfileState(address);
748             }
749
750             if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
751                          reason + ")");
752             Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
753             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
754             intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
755             intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
756             if (state == BluetoothDevice.BOND_NONE) {
757                 if (reason <= 0) {
758                     Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
759                           "invalid. Overriding reason code with BOND_RESULT_REMOVED");
760                     reason = BluetoothDevice.UNBOND_REASON_REMOVED;
761                 }
762                 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
763                 mState.remove(address);
764             } else {
765                 mState.put(address, state);
766             }
767
768             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
769         }
770
771         public boolean isAutoPairingBlacklisted(String address) {
772             if (mAutoPairingAddressBlacklist != null) {
773                 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
774                     if (address.startsWith(blacklistAddress)) return true;
775                 }
776             }
777
778             if (mAutoPairingDynamicAddressBlacklist != null) {
779                 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) {
780                     if (address.equals(blacklistAddress)) return true;
781                 }
782             }
783             String name = getRemoteName(address);
784             if (name != null) {
785                 if (mAutoPairingExactNameBlacklist != null) {
786                     for (String blacklistName : mAutoPairingExactNameBlacklist) {
787                         if (name.equals(blacklistName)) return true;
788                     }
789                 }
790
791                 if (mAutoPairingPartialNameBlacklist != null) {
792                     for (String blacklistName : mAutoPairingPartialNameBlacklist) {
793                         if (name.startsWith(blacklistName)) return true;
794                     }
795                 }
796             }
797             return false;
798         }
799
800         public synchronized int getBondState(String address) {
801             Integer state = mState.get(address);
802             if (state == null) {
803                 return BluetoothDevice.BOND_NONE;
804             }
805             return state.intValue();
806         }
807
808         /*package*/ synchronized String[] listInState(int state) {
809             ArrayList<String> result = new ArrayList<String>(mState.size());
810             for (Map.Entry<String, Integer> e : mState.entrySet()) {
811                 if (e.getValue().intValue() == state) {
812                     result.add(e.getKey());
813                 }
814             }
815             return result.toArray(new String[result.size()]);
816         }
817
818         public synchronized void addAutoPairingFailure(String address) {
819             if (mAutoPairingDynamicAddressBlacklist == null) {
820                 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>();
821             }
822
823             updateAutoPairingData(address);
824             mAutoPairingDynamicAddressBlacklist.add(address);
825         }
826
827         public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
828             return getAttempt(address) != 0;
829         }
830
831         public synchronized void clearPinAttempts(String address) {
832             mPinAttempt.remove(address);
833         }
834
835         public synchronized boolean hasAutoPairingFailed(String address) {
836             if (mAutoPairingDynamicAddressBlacklist == null) return false;
837
838             return mAutoPairingDynamicAddressBlacklist.contains(address);
839         }
840
841         public synchronized int getAttempt(String address) {
842             Integer attempt = mPinAttempt.get(address);
843             if (attempt == null) {
844                 return 0;
845             }
846             return attempt.intValue();
847         }
848
849         public synchronized void attempt(String address) {
850             Integer attempt = mPinAttempt.get(address);
851             int newAttempt;
852             if (attempt == null) {
853                 newAttempt = 1;
854             } else {
855                 newAttempt = attempt.intValue() + 1;
856             }
857             mPinAttempt.put(address, new Integer(newAttempt));
858         }
859
860         private void copyAutoPairingData() {
861             File file = null;
862             FileInputStream in = null;
863             FileOutputStream out = null;
864             try {
865                 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST);
866                 if (file.exists()) return;
867
868                 in = new FileInputStream(AUTO_PAIRING_BLACKLIST);
869                 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
870
871                 byte[] buf = new byte[1024];
872                 int len;
873                 while ((len = in.read(buf)) > 0) {
874                     out.write(buf, 0, len);
875                 }
876             } catch (FileNotFoundException e) {
877                 log("FileNotFoundException: in copyAutoPairingData");
878             } catch (IOException e) {
879                 log("IOException: in copyAutoPairingData");
880             } finally {
881                  try {
882                      if (in != null) in.close();
883                      if (out != null) out.close();
884                  } catch (IOException e) {}
885             }
886         }
887
888         public void readAutoPairingData() {
889             if (mAutoPairingAddressBlacklist != null) return;
890             copyAutoPairingData();
891             FileInputStream fstream = null;
892             try {
893                 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
894                 DataInputStream in = new DataInputStream(fstream);
895                 BufferedReader file = new BufferedReader(new InputStreamReader(in));
896                 String line;
897                 while((line = file.readLine()) != null) {
898                     line = line.trim();
899                     if (line.length() == 0 || line.startsWith("//")) continue;
900                     String[] value = line.split("=");
901                     if (value != null && value.length == 2) {
902                         String[] val = value[1].split(",");
903                         if (value[0].equalsIgnoreCase("AddressBlacklist")) {
904                             mAutoPairingAddressBlacklist =
905                                 new ArrayList<String>(Arrays.asList(val));
906                         } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) {
907                             mAutoPairingExactNameBlacklist =
908                                 new ArrayList<String>(Arrays.asList(val));
909                         } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) {
910                             mAutoPairingPartialNameBlacklist =
911                                 new ArrayList<String>(Arrays.asList(val));
912                         } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) {
913                             mAutoPairingDynamicAddressBlacklist =
914                                 new ArrayList<String>(Arrays.asList(val));
915                         } else {
916                             Log.e(TAG, "Error parsing Auto pairing blacklist file");
917                         }
918                     }
919                 }
920             } catch (FileNotFoundException e) {
921                 log("FileNotFoundException: readAutoPairingData" + e.toString());
922             } catch (IOException e) {
923                 log("IOException: readAutoPairingData" + e.toString());
924             } finally {
925                 if (fstream != null) {
926                     try {
927                         fstream.close();
928                     } catch (IOException e) {
929                         // Ignore
930                     }
931                 }
932             }
933         }
934
935         // This function adds a bluetooth address to the auto pairing blacklist
936         // file. These addresses are added to DynamicAddressBlacklistSection
937         private void updateAutoPairingData(String address) {
938             BufferedWriter out = null;
939             try {
940                 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true));
941                 StringBuilder str = new StringBuilder();
942                 if (mAutoPairingDynamicAddressBlacklist.size() == 0) {
943                     str.append("DynamicAddressBlacklist=");
944                 }
945                 str.append(address);
946                 str.append(",");
947                 out.write(str.toString());
948             } catch (FileNotFoundException e) {
949                 log("FileNotFoundException: updateAutoPairingData" + e.toString());
950             } catch (IOException e) {
951                 log("IOException: updateAutoPairingData" + e.toString());
952             } finally {
953                 if (out != null) {
954                     try {
955                         out.close();
956                     } catch (IOException e) {
957                         // Ignore
958                     }
959                 }
960             }
961         }
962     }
963
964     private static String toBondStateString(int bondState) {
965         switch (bondState) {
966         case BluetoothDevice.BOND_NONE:
967             return "not bonded";
968         case BluetoothDevice.BOND_BONDING:
969             return "bonding";
970         case BluetoothDevice.BOND_BONDED:
971             return "bonded";
972         default:
973             return "??????";
974         }
975     }
976
977     /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
978         return mAdapterProperties.isEmpty();
979     }
980
981     /*package*/synchronized void getAllProperties() {
982
983         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
984         mAdapterProperties.clear();
985
986         String properties[] = (String [])getAdapterPropertiesNative();
987         // The String Array consists of key-value pairs.
988         if (properties == null) {
989             Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
990             return;
991         }
992
993         for (int i = 0; i < properties.length; i++) {
994             String name = properties[i];
995             String newValue = null;
996             int len;
997             if (name == null) {
998                 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
999                 continue;
1000             }
1001             if (name.equals("Devices") || name.equals("UUIDs")) {
1002                 StringBuilder str = new StringBuilder();
1003                 len = Integer.valueOf(properties[++i]);
1004                 for (int j = 0; j < len; j++) {
1005                     str.append(properties[++i]);
1006                     str.append(",");
1007                 }
1008                 if (len > 0) {
1009                     newValue = str.toString();
1010                 }
1011             } else {
1012                 newValue = properties[++i];
1013             }
1014             mAdapterProperties.put(name, newValue);
1015         }
1016
1017         // Add adapter object path property.
1018         String adapterPath = getAdapterPathNative();
1019         if (adapterPath != null)
1020             mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
1021     }
1022
1023     /* package */ synchronized void setProperty(String name, String value) {
1024         mAdapterProperties.put(name, value);
1025     }
1026
1027     public synchronized boolean setName(String name) {
1028         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1029                                                 "Need BLUETOOTH_ADMIN permission");
1030         if (name == null) {
1031             return false;
1032         }
1033         return setPropertyString("Name", name);
1034     }
1035
1036     //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
1037     // Either have a single property function with Object as the parameter
1038     // or have a function for each property and then obfuscate in the JNI layer.
1039     // The following looks dirty.
1040     private boolean setPropertyString(String key, String value) {
1041         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1042         if (!isEnabledInternal()) return false;
1043         return setAdapterPropertyStringNative(key, value);
1044     }
1045
1046     private boolean setPropertyInteger(String key, int value) {
1047         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1048         if (!isEnabledInternal()) return false;
1049         return setAdapterPropertyIntegerNative(key, value);
1050     }
1051
1052     private boolean setPropertyBoolean(String key, boolean value) {
1053         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1054         if (!isEnabledInternal()) return false;
1055         return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
1056     }
1057
1058     /**
1059      * Set the discoverability window for the device.  A timeout of zero
1060      * makes the device permanently discoverable (if the device is
1061      * discoverable).  Setting the timeout to a nonzero value does not make
1062      * a device discoverable; you need to call setMode() to make the device
1063      * explicitly discoverable.
1064      *
1065      * @param timeout The discoverable timeout in seconds.
1066      */
1067     public synchronized boolean setDiscoverableTimeout(int timeout) {
1068         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1069                                                 "Need BLUETOOTH_ADMIN permission");
1070         return setPropertyInteger("DiscoverableTimeout", timeout);
1071     }
1072
1073     public synchronized boolean setScanMode(int mode, int duration) {
1074         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
1075                                                 "Need WRITE_SECURE_SETTINGS permission");
1076         boolean pairable = false;
1077         boolean discoverable = false;
1078
1079         switch (mode) {
1080         case BluetoothAdapter.SCAN_MODE_NONE:
1081             mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
1082             pairable = false;
1083             discoverable = false;
1084             break;
1085         case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
1086             mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
1087             pairable = true;
1088             discoverable = false;
1089             break;
1090         case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
1091             mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
1092             pairable = true;
1093             discoverable = true;
1094             Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
1095             mHandler.sendMessageDelayed(msg, duration * 1000);
1096             if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
1097             break;
1098         default:
1099             Log.w(TAG, "Requested invalid scan mode " + mode);
1100             return false;
1101         }
1102         setPropertyBoolean("Pairable", pairable);
1103         setPropertyBoolean("Discoverable", discoverable);
1104
1105         return true;
1106     }
1107
1108     /*package*/ synchronized String getProperty(String name) {
1109         if (!isEnabledInternal()) return null;
1110         return getPropertyInternal(name);
1111     }
1112
1113     /*package*/ synchronized String getPropertyInternal(String name) {
1114         if (!mAdapterProperties.isEmpty())
1115             return mAdapterProperties.get(name);
1116         getAllProperties();
1117         return mAdapterProperties.get(name);
1118     }
1119
1120     public synchronized String getAddress() {
1121         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1122         return getProperty("Address");
1123     }
1124
1125     public synchronized String getName() {
1126         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1127         return getProperty("Name");
1128     }
1129
1130     /**
1131      * Returns the user-friendly name of a remote device.  This value is
1132      * returned from our local cache, which is updated when onPropertyChange
1133      * event is received.
1134      * Do not expect to retrieve the updated remote name immediately after
1135      * changing the name on the remote device.
1136      *
1137      * @param address Bluetooth address of remote device.
1138      *
1139      * @return The user-friendly name of the specified remote device.
1140      */
1141     public synchronized String getRemoteName(String address) {
1142         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1143         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1144             return null;
1145         }
1146         return getRemoteDeviceProperty(address, "Name");
1147     }
1148
1149     /**
1150      * Get the discoverability window for the device.  A timeout of zero
1151      * means that the device is permanently discoverable (if the device is
1152      * in the discoverable mode).
1153      *
1154      * @return The discoverability window of the device, in seconds.  A negative
1155      *         value indicates an error.
1156      */
1157     public synchronized int getDiscoverableTimeout() {
1158         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1159         String timeout = getProperty("DiscoverableTimeout");
1160         if (timeout != null)
1161            return Integer.valueOf(timeout);
1162         else
1163             return -1;
1164     }
1165
1166     public synchronized int getScanMode() {
1167         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1168         if (!isEnabledInternal())
1169             return BluetoothAdapter.SCAN_MODE_NONE;
1170
1171         boolean pairable = getProperty("Pairable").equals("true");
1172         boolean discoverable = getProperty("Discoverable").equals("true");
1173         return bluezStringToScanMode (pairable, discoverable);
1174     }
1175
1176     public synchronized boolean startDiscovery() {
1177         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1178                                                 "Need BLUETOOTH_ADMIN permission");
1179         if (!isEnabledInternal()) return false;
1180
1181         return startDiscoveryNative();
1182     }
1183
1184     public synchronized boolean cancelDiscovery() {
1185         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1186                                                 "Need BLUETOOTH_ADMIN permission");
1187         if (!isEnabledInternal()) return false;
1188
1189         return stopDiscoveryNative();
1190     }
1191
1192     public synchronized boolean isDiscovering() {
1193         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1194         return mIsDiscovering;
1195     }
1196
1197     /* package */ void setIsDiscovering(boolean isDiscovering) {
1198         mIsDiscovering = isDiscovering;
1199     }
1200
1201     private boolean isBondingFeasible(String address) {
1202         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1203                                                 "Need BLUETOOTH_ADMIN permission");
1204         if (!isEnabledInternal()) return false;
1205
1206         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1207             return false;
1208         }
1209         address = address.toUpperCase();
1210
1211         if (mBondState.getPendingOutgoingBonding() != null) {
1212             log("Ignoring createBond(): another device is bonding");
1213             // a different device is currently bonding, fail
1214             return false;
1215         }
1216
1217         // Check for bond state only if we are not performing auto
1218         // pairing exponential back-off attempts.
1219         if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
1220                 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
1221             log("Ignoring createBond(): this device is already bonding or bonded");
1222             return false;
1223         }
1224
1225         if (address.equals(mDockAddress)) {
1226             if (!writeDockPin()) {
1227                 log("Error while writing Pin for the dock");
1228                 return false;
1229             }
1230         }
1231         return true;
1232     }
1233
1234     public synchronized boolean createBond(String address) {
1235         if (!isBondingFeasible(address)) return false;
1236
1237         if (!createPairedDeviceNative(address, 60000  /*1 minute*/ )) {
1238             return false;
1239         }
1240
1241         mBondState.setPendingOutgoingBonding(address);
1242         mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1243
1244         return true;
1245     }
1246
1247     public synchronized boolean createBondOutOfBand(String address, byte[] hash,
1248                                                     byte[] randomizer) {
1249         if (!isBondingFeasible(address)) return false;
1250
1251         if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1252             return false;
1253         }
1254
1255         setDeviceOutOfBandData(address, hash, randomizer);
1256         mBondState.setPendingOutgoingBonding(address);
1257         mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1258
1259         return true;
1260     }
1261
1262     public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1263             byte[] randomizer) {
1264         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1265                                                 "Need BLUETOOTH_ADMIN permission");
1266         if (!isEnabledInternal()) return false;
1267
1268         Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1269
1270         if (DBG) {
1271             log("Setting out of band data for:" + address + ":" +
1272               Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
1273         }
1274
1275         mDeviceOobData.put(address, value);
1276         return true;
1277     }
1278
1279     Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1280         return mDeviceOobData.get(device.getAddress());
1281     }
1282
1283
1284     public synchronized byte[] readOutOfBandData() {
1285         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1286                                                 "Need BLUETOOTH permission");
1287         if (!isEnabledInternal()) return null;
1288
1289         return readAdapterOutOfBandDataNative();
1290     }
1291
1292     public synchronized boolean cancelBondProcess(String address) {
1293         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1294                                                 "Need BLUETOOTH_ADMIN permission");
1295         if (!isEnabledInternal()) return false;
1296
1297         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1298             return false;
1299         }
1300         address = address.toUpperCase();
1301         if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1302             return false;
1303         }
1304
1305         mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
1306                                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
1307         cancelDeviceCreationNative(address);
1308         return true;
1309     }
1310
1311     public synchronized boolean removeBond(String address) {
1312         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1313                                                 "Need BLUETOOTH_ADMIN permission");
1314         if (!isEnabledInternal()) return false;
1315
1316         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1317             return false;
1318         }
1319         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
1320         if (state != null) {
1321             state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
1322             return true;
1323         } else {
1324             return false;
1325         }
1326     }
1327
1328     public synchronized boolean removeBondInternal(String address) {
1329         return removeDeviceNative(getObjectPathFromAddress(address));
1330     }
1331
1332     public synchronized String[] listBonds() {
1333         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1334         return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1335     }
1336
1337     /*package*/ synchronized String[] listInState(int state) {
1338       return mBondState.listInState(state);
1339     }
1340
1341     public synchronized int getBondState(String address) {
1342         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1343         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1344             return BluetoothDevice.ERROR;
1345         }
1346         return mBondState.getBondState(address.toUpperCase());
1347     }
1348
1349     /*package*/ synchronized boolean setBondState(String address, int state) {
1350         return setBondState(address, state, 0);
1351     }
1352
1353     /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
1354         mBondState.setBondState(address.toUpperCase(), state);
1355         return true;
1356     }
1357
1358     public synchronized boolean isBluetoothDock(String address) {
1359         SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1360                 mContext.MODE_PRIVATE);
1361
1362         return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
1363     }
1364
1365     /*package*/ boolean isRemoteDeviceInCache(String address) {
1366         return (mDeviceProperties.get(address) != null);
1367     }
1368
1369     /*package*/ String[] getRemoteDeviceProperties(String address) {
1370         if (!isEnabledInternal()) return null;
1371
1372         String objectPath = getObjectPathFromAddress(address);
1373         return (String [])getDevicePropertiesNative(objectPath);
1374     }
1375
1376     /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
1377         Map<String, String> properties = mDeviceProperties.get(address);
1378         if (properties != null) {
1379             return properties.get(property);
1380         } else {
1381             // Query for remote device properties, again.
1382             // We will need to reload the cache when we switch Bluetooth on / off
1383             // or if we crash.
1384             if (updateRemoteDevicePropertiesCache(address))
1385                 return getRemoteDeviceProperty(address, property);
1386         }
1387         Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
1388         return null;
1389     }
1390
1391     /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
1392         String[] propValues = getRemoteDeviceProperties(address);
1393         if (propValues != null) {
1394             addRemoteDeviceProperties(address, propValues);
1395             return true;
1396         }
1397         return false;
1398     }
1399
1400     /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
1401         /*
1402          * We get a DeviceFound signal every time RSSI changes or name changes.
1403          * Don't create a new Map object every time */
1404         Map<String, String> propertyValues = mDeviceProperties.get(address);
1405         if (propertyValues == null) {
1406             propertyValues = new HashMap<String, String>();
1407         }
1408
1409         for (int i = 0; i < properties.length; i++) {
1410             String name = properties[i];
1411             String newValue = null;
1412             int len;
1413             if (name == null) {
1414                 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1415                 continue;
1416             }
1417             if (name.equals("UUIDs") || name.equals("Nodes")) {
1418                 StringBuilder str = new StringBuilder();
1419                 len = Integer.valueOf(properties[++i]);
1420                 for (int j = 0; j < len; j++) {
1421                     str.append(properties[++i]);
1422                     str.append(",");
1423                 }
1424                 if (len > 0) {
1425                     newValue = str.toString();
1426                 }
1427             } else {
1428                 newValue = properties[++i];
1429             }
1430
1431             propertyValues.put(name, newValue);
1432         }
1433         mDeviceProperties.put(address, propertyValues);
1434
1435         // We have added a new remote device or updated its properties.
1436         // Also update the serviceChannel cache.
1437         updateDeviceServiceChannelCache(address);
1438     }
1439
1440     /* package */ void removeRemoteDeviceProperties(String address) {
1441         mDeviceProperties.remove(address);
1442     }
1443
1444     /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1445                                                               String value) {
1446         Map <String, String> propVal = mDeviceProperties.get(address);
1447         if (propVal != null) {
1448             propVal.put(name, value);
1449             mDeviceProperties.put(address, propVal);
1450         } else {
1451             Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
1452         }
1453     }
1454
1455     /**
1456      * Sets the remote device trust state.
1457      *
1458      * @return boolean to indicate operation success or fail
1459      */
1460     public synchronized boolean setTrust(String address, boolean value) {
1461         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1462             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1463                     "Need BLUETOOTH_ADMIN permission");
1464             return false;
1465         }
1466
1467         if (!isEnabledInternal()) return false;
1468
1469         return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1470                 value ? 1 : 0);
1471     }
1472
1473     /**
1474      * Gets the remote device trust state as boolean.
1475      * Note: this value may be
1476      * retrieved from cache if we retrieved the data before *
1477      *
1478      * @return boolean to indicate trust or untrust state
1479      */
1480     public synchronized boolean getTrustState(String address) {
1481         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1482             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1483             return false;
1484         }
1485
1486         String val = getRemoteDeviceProperty(address, "Trusted");
1487         if (val == null) {
1488             return false;
1489         } else {
1490             return val.equals("true") ? true : false;
1491         }
1492     }
1493
1494     /**
1495      * Gets the remote major, minor classes encoded as a 32-bit
1496      * integer.
1497      *
1498      * Note: this value is retrieved from cache, because we get it during
1499      *       remote-device discovery.
1500      *
1501      * @return 32-bit integer encoding the remote major, minor, and service
1502      *         classes.
1503      */
1504     public synchronized int getRemoteClass(String address) {
1505         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1506             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1507             return BluetoothClass.ERROR;
1508         }
1509         String val = getRemoteDeviceProperty(address, "Class");
1510         if (val == null)
1511             return BluetoothClass.ERROR;
1512         else {
1513             return Integer.valueOf(val);
1514         }
1515     }
1516
1517
1518     /**
1519      * Gets the UUIDs supported by the remote device
1520      *
1521      * @return array of 128bit ParcelUuids
1522      */
1523     public synchronized ParcelUuid[] getRemoteUuids(String address) {
1524         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1525         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1526             return null;
1527         }
1528         return getUuidFromCache(address);
1529     }
1530
1531     private ParcelUuid[] getUuidFromCache(String address) {
1532         String value = getRemoteDeviceProperty(address, "UUIDs");
1533         if (value == null) return null;
1534
1535         String[] uuidStrings = null;
1536         // The UUIDs are stored as a "," separated string.
1537         uuidStrings = value.split(",");
1538         ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1539
1540         for (int i = 0; i < uuidStrings.length; i++) {
1541             uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1542         }
1543         return uuids;
1544     }
1545
1546     /**
1547      * Connect and fetch new UUID's using SDP.
1548      * The UUID's found are broadcast as intents.
1549      * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1550      * a given uuid.
1551      * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1552      * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1553      * callback and broadcast intents.
1554      */
1555     public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1556             IBluetoothCallback callback) {
1557         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1558         if (!isEnabledInternal()) return false;
1559
1560         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1561             return false;
1562         }
1563
1564         RemoteService service = new RemoteService(address, uuid);
1565         if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1566             // An SDP query for this address & uuid is already in progress
1567             // Do not add this callback for the uuid
1568             return false;
1569         }
1570
1571         if (mUuidIntentTracker.contains(address)) {
1572             // An SDP query for this address is already in progress
1573             // Add this uuid onto the in-progress SDP query
1574             if (uuid != null) {
1575                 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1576             }
1577             return true;
1578         }
1579
1580         boolean ret;
1581         // Just do the SDP if the device is already  created and UUIDs are not
1582         // NULL, else create the device and then do SDP.
1583         if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) {
1584             String path = getObjectPathFromAddress(address);
1585             if (path == null) return false;
1586
1587             // Use an empty string for the UUID pattern
1588             ret = discoverServicesNative(path, "");
1589         } else {
1590             ret = createDeviceNative(address);
1591         }
1592
1593         mUuidIntentTracker.add(address);
1594         if (uuid != null) {
1595             mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1596         }
1597
1598         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1599         message.obj = address;
1600         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1601         return ret;
1602     }
1603
1604     /**
1605      * Gets the rfcomm channel associated with the UUID.
1606      * Pulls records from the cache only.
1607      *
1608      * @param address Address of the remote device
1609      * @param uuid ParcelUuid of the service attribute
1610      *
1611      * @return rfcomm channel associated with the service attribute
1612      *         -1 on error
1613      */
1614     public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
1615         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1616         if (!isEnabledInternal()) return -1;
1617
1618         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1619             return BluetoothDevice.ERROR;
1620         }
1621         // Check if we are recovering from a crash.
1622         if (mDeviceProperties.isEmpty()) {
1623             if (!updateRemoteDevicePropertiesCache(address))
1624                 return -1;
1625         }
1626
1627         Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1628         if (value != null && value.containsKey(uuid))
1629             return value.get(uuid);
1630         return -1;
1631     }
1632
1633     public synchronized boolean setPin(String address, byte[] pin) {
1634         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1635                                                 "Need BLUETOOTH_ADMIN permission");
1636         if (!isEnabledInternal()) return false;
1637
1638         if (pin == null || pin.length <= 0 || pin.length > 16 ||
1639             !BluetoothAdapter.checkBluetoothAddress(address)) {
1640             return false;
1641         }
1642         address = address.toUpperCase();
1643         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1644         if (data == null) {
1645             Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1646                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1647                   " or by bluez.\n");
1648             return false;
1649         }
1650         // bluez API wants pin as a string
1651         String pinString;
1652         try {
1653             pinString = new String(pin, "UTF8");
1654         } catch (UnsupportedEncodingException uee) {
1655             Log.e(TAG, "UTF8 not supported?!?");
1656             return false;
1657         }
1658         return setPinNative(address, pinString, data.intValue());
1659     }
1660
1661     public synchronized boolean setPasskey(String address, int passkey) {
1662         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1663                                                 "Need BLUETOOTH_ADMIN permission");
1664         if (!isEnabledInternal()) return false;
1665
1666         if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
1667             return false;
1668         }
1669         address = address.toUpperCase();
1670         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1671         if (data == null) {
1672             Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1673                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1674                   " or by bluez.\n");
1675             return false;
1676         }
1677         return setPasskeyNative(address, passkey, data.intValue());
1678     }
1679
1680     public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1681         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1682                                                 "Need BLUETOOTH_ADMIN permission");
1683         if (!isEnabledInternal()) return false;
1684
1685         address = address.toUpperCase();
1686         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1687         if (data == null) {
1688             Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1689                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1690                   " or by bluez.\n");
1691             return false;
1692         }
1693         return setPairingConfirmationNative(address, confirm, data.intValue());
1694     }
1695
1696     public synchronized boolean setRemoteOutOfBandData(String address) {
1697         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1698                                                 "Need BLUETOOTH_ADMIN permission");
1699         if (!isEnabledInternal()) return false;
1700         address = address.toUpperCase();
1701         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1702         if (data == null) {
1703             Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
1704                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1705                   " or by bluez.\n");
1706             return false;
1707         }
1708
1709         Pair<byte[], byte[]> val = mDeviceOobData.get(address);
1710         byte[] hash, randomizer;
1711         if (val == null) {
1712             // TODO: check what should be passed in this case.
1713             hash = new byte[16];
1714             randomizer = new byte[16];
1715         } else {
1716             hash = val.first;
1717             randomizer = val.second;
1718         }
1719         return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
1720     }
1721
1722     public synchronized boolean cancelPairingUserInput(String address) {
1723         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1724                                                 "Need BLUETOOTH_ADMIN permission");
1725         if (!isEnabledInternal()) return false;
1726
1727         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1728             return false;
1729         }
1730         mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
1731                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
1732         address = address.toUpperCase();
1733         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1734         if (data == null) {
1735             Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1736                 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1737                 "by the remote or by bluez.\n");
1738             return false;
1739         }
1740         return cancelPairingUserInputNative(address, data.intValue());
1741     }
1742
1743     /*package*/ void updateDeviceServiceChannelCache(String address) {
1744         ParcelUuid[] deviceUuids = getRemoteUuids(address);
1745         // We are storing the rfcomm channel numbers only for the uuids
1746         // we are interested in.
1747         int channel;
1748         if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
1749
1750         ArrayList<ParcelUuid> applicationUuids = new ArrayList();
1751
1752         synchronized (this) {
1753             for (RemoteService service : mUuidCallbackTracker.keySet()) {
1754                 if (service.address.equals(address)) {
1755                     applicationUuids.add(service.uuid);
1756                 }
1757             }
1758         }
1759
1760         Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
1761
1762         // Retrieve RFCOMM channel for default uuids
1763         for (ParcelUuid uuid : RFCOMM_UUIDS) {
1764             if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1765                 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1766                         uuid.toString(), 0x0004);
1767                 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
1768                 value.put(uuid, channel);
1769             }
1770         }
1771         // Retrieve RFCOMM channel for application requested uuids
1772         for (ParcelUuid uuid : applicationUuids) {
1773             if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1774                 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1775                         uuid.toString(), 0x0004);
1776                 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
1777                 value.put(uuid, channel);
1778             }
1779         }
1780
1781         synchronized (this) {
1782             // Make application callbacks
1783             for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1784                     iter.hasNext();) {
1785                 RemoteService service = iter.next();
1786                 if (service.address.equals(address)) {
1787                     channel = -1;
1788                     if (value.get(service.uuid) != null) {
1789                         channel = value.get(service.uuid);
1790                     }
1791                     if (channel != -1) {
1792                         if (DBG) log("Making callback for " + service.uuid + " with result " +
1793                                 channel);
1794                         IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1795                         if (callback != null) {
1796                             try {
1797                                 callback.onRfcommChannelFound(channel);
1798                             } catch (RemoteException e) {Log.e(TAG, "", e);}
1799                         }
1800
1801                         iter.remove();
1802                     }
1803                 }
1804             }
1805
1806             // Update cache
1807             mDeviceServiceChannelCache.put(address, value);
1808         }
1809     }
1810
1811     /**
1812      * b is a handle to a Binder instance, so that this service can be notified
1813      * for Applications that terminate unexpectedly, to clean there service
1814      * records
1815      */
1816     public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1817             int channel, IBinder b) {
1818         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1819         if (!isEnabledInternal()) return -1;
1820
1821         if (serviceName == null || uuid == null || channel < 1 ||
1822                 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1823             return -1;
1824         }
1825         if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1826             Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1827             return -1;
1828         }
1829         int handle = addRfcommServiceRecordNative(serviceName,
1830                 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1831                 (short)channel);
1832         if (DBG) log("new handle " + Integer.toHexString(handle));
1833         if (handle == -1) {
1834             return -1;
1835         }
1836
1837         int pid = Binder.getCallingPid();
1838         mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1839         try {
1840             b.linkToDeath(new Reaper(handle, pid), 0);
1841         } catch (RemoteException e) {}
1842         return handle;
1843     }
1844
1845     public void removeServiceRecord(int handle) {
1846         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1847                                                 "Need BLUETOOTH permission");
1848         checkAndRemoveRecord(handle, Binder.getCallingPid());
1849     }
1850
1851     private synchronized void checkAndRemoveRecord(int handle, int pid) {
1852         Integer handleInt = new Integer(handle);
1853         Integer owner = mServiceRecordToPid.get(handleInt);
1854         if (owner != null && pid == owner.intValue()) {
1855             if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
1856                     pid);
1857             mServiceRecordToPid.remove(handleInt);
1858             removeServiceRecordNative(handle);
1859         }
1860     }
1861
1862     private class Reaper implements IBinder.DeathRecipient {
1863         int pid;
1864         int handle;
1865         Reaper(int handle, int pid) {
1866             this.pid = pid;
1867             this.handle = handle;
1868         }
1869         public void binderDied() {
1870             synchronized (BluetoothService.this) {
1871                 if (DBG) log("Tracked app " + pid + " died");
1872                 checkAndRemoveRecord(handle, pid);
1873             }
1874         }
1875     }
1876
1877     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1878         @Override
1879         public void onReceive(Context context, Intent intent) {
1880             if (intent == null) return;
1881
1882             String action = intent.getAction();
1883             if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1884                 ContentResolver resolver = context.getContentResolver();
1885                 // Query the airplane mode from Settings.System just to make sure that
1886                 // some random app is not sending this intent and disabling bluetooth
1887                 boolean enabled = !isAirplaneModeOn();
1888                 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1889                 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1890                     if (enabled) {
1891                         enable(false);
1892                     } else {
1893                         disable(false);
1894                     }
1895                 }
1896             } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1897                 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1898                         Intent.EXTRA_DOCK_STATE_UNDOCKED);
1899                 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1900                 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1901                     mDockAddress = null;
1902                     mDockPin = null;
1903                 } else {
1904                     SharedPreferences.Editor editor =
1905                         mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1906                                 mContext.MODE_PRIVATE).edit();
1907                     editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
1908                     editor.apply();
1909                 }
1910             }
1911         }
1912     };
1913
1914     private void registerForAirplaneMode(IntentFilter filter) {
1915         final ContentResolver resolver = mContext.getContentResolver();
1916         final String airplaneModeRadios = Settings.System.getString(resolver,
1917                 Settings.System.AIRPLANE_MODE_RADIOS);
1918         final String toggleableRadios = Settings.System.getString(resolver,
1919                 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1920
1921         mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1922                 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1923         mIsAirplaneToggleable = toggleableRadios == null ? false :
1924                 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1925
1926         if (mIsAirplaneSensitive) {
1927             filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1928         }
1929     }
1930
1931     /* Returns true if airplane mode is currently on */
1932     private final boolean isAirplaneModeOn() {
1933         return Settings.System.getInt(mContext.getContentResolver(),
1934                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1935     }
1936
1937     /* Broadcast the Uuid intent */
1938     /*package*/ synchronized void sendUuidIntent(String address) {
1939         ParcelUuid[] uuid = getUuidFromCache(address);
1940         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
1941         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
1942         intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1943         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
1944
1945         if (mUuidIntentTracker.contains(address))
1946             mUuidIntentTracker.remove(address);
1947
1948     }
1949
1950     /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1951         for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1952                 iter.hasNext();) {
1953             RemoteService service = iter.next();
1954             if (service.address.equals(address)) {
1955                 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
1956                         " " + service.uuid);
1957                 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1958                 if (callback != null) {
1959                     try {
1960                         callback.onRfcommChannelFound(-1);
1961                     } catch (RemoteException e) {Log.e(TAG, "", e);}
1962                 }
1963
1964                 iter.remove();
1965             }
1966         }
1967     }
1968
1969     @Override
1970     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1971         switch(mBluetoothState) {
1972         case BluetoothAdapter.STATE_OFF:
1973             pw.println("Bluetooth OFF\n");
1974             return;
1975         case BluetoothAdapter.STATE_TURNING_ON:
1976             pw.println("Bluetooth TURNING ON\n");
1977             return;
1978         case BluetoothAdapter.STATE_TURNING_OFF:
1979             pw.println("Bluetooth TURNING OFF\n");
1980             return;
1981         case BluetoothAdapter.STATE_ON:
1982             pw.println("Bluetooth ON\n");
1983         }
1984
1985         pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
1986         pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
1987
1988         pw.println("Local address = " + getAddress());
1989         pw.println("Local name = " + getName());
1990         pw.println("isDiscovering() = " + isDiscovering());
1991
1992         BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1993
1994         pw.println("\n--Known devices--");
1995         for (String address : mDeviceProperties.keySet()) {
1996             int bondState = mBondState.getBondState(address);
1997             pw.printf("%s %10s (%d) %s\n", address,
1998                        toBondStateString(bondState),
1999                        mBondState.getAttempt(address),
2000                        getRemoteName(address));
2001
2002             Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
2003             if (uuidChannels == null) {
2004                 pw.println("\tuuids = null");
2005             } else {
2006                 for (ParcelUuid uuid : uuidChannels.keySet()) {
2007                     Integer channel = uuidChannels.get(uuid);
2008                     if (channel == null) {
2009                         pw.println("\t" + uuid);
2010                     } else {
2011                         pw.println("\t" + uuid + " RFCOMM channel = " + channel);
2012                     }
2013                 }
2014             }
2015             for (RemoteService service : mUuidCallbackTracker.keySet()) {
2016                 if (service.address.equals(address)) {
2017                     pw.println("\tPENDING CALLBACK: " + service.uuid);
2018                 }
2019             }
2020         }
2021
2022         String value = getProperty("Devices");
2023         String[] devicesObjectPath = null;
2024         if (value != null) {
2025             devicesObjectPath = value.split(",");
2026         }
2027         pw.println("\n--ACL connected devices--");
2028         if (devicesObjectPath != null) {
2029             for (String device : devicesObjectPath) {
2030                 pw.println(getAddressFromObjectPath(device));
2031             }
2032         }
2033
2034         // Rather not do this from here, but no-where else and I need this
2035         // dump
2036         pw.println("\n--Headset Service--");
2037         switch (headset.getState(headset.getCurrentHeadset())) {
2038         case BluetoothHeadset.STATE_DISCONNECTED:
2039             pw.println("getState() = STATE_DISCONNECTED");
2040             break;
2041         case BluetoothHeadset.STATE_CONNECTING:
2042             pw.println("getState() = STATE_CONNECTING");
2043             break;
2044         case BluetoothHeadset.STATE_CONNECTED:
2045             pw.println("getState() = STATE_CONNECTED");
2046             break;
2047         case BluetoothHeadset.STATE_ERROR:
2048             pw.println("getState() = STATE_ERROR");
2049             break;
2050         }
2051
2052         pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
2053         pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
2054         headset.close();
2055         pw.println("\n--Application Service Records--");
2056         for (Integer handle : mServiceRecordToPid.keySet()) {
2057             Integer pid = mServiceRecordToPid.get(handle);
2058             pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
2059         }
2060     }
2061
2062     /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2063         if (pairable && discoverable)
2064             return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
2065         else if (pairable && !discoverable)
2066             return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
2067         else
2068             return BluetoothAdapter.SCAN_MODE_NONE;
2069     }
2070
2071     /* package */ static String scanModeToBluezString(int mode) {
2072         switch (mode) {
2073         case BluetoothAdapter.SCAN_MODE_NONE:
2074             return "off";
2075         case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
2076             return "connectable";
2077         case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
2078             return "discoverable";
2079         }
2080         return null;
2081     }
2082
2083     /*package*/ String getAddressFromObjectPath(String objectPath) {
2084         String adapterObjectPath = getPropertyInternal("ObjectPath");
2085         if (adapterObjectPath == null || objectPath == null) {
2086             Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
2087                     "  or deviceObjectPath:" + objectPath + " is null");
2088             return null;
2089         }
2090         if (!objectPath.startsWith(adapterObjectPath)) {
2091             Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
2092                     "  is not a prefix of deviceObjectPath:" + objectPath +
2093                     "bluetoothd crashed ?");
2094             return null;
2095         }
2096         String address = objectPath.substring(adapterObjectPath.length());
2097         if (address != null) return address.replace('_', ':');
2098
2099         Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2100         return null;
2101     }
2102
2103     /*package*/ String getObjectPathFromAddress(String address) {
2104         String path = getPropertyInternal("ObjectPath");
2105         if (path == null) {
2106             Log.e(TAG, "Error: Object Path is null");
2107             return null;
2108         }
2109         path = path + address.replace(":", "_");
2110         return path;
2111     }
2112
2113     /*package */ void setLinkTimeout(String address, int num_slots) {
2114         String path = getObjectPathFromAddress(address);
2115         boolean result = setLinkTimeoutNative(path, num_slots);
2116
2117         if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
2118     }
2119
2120     public boolean connectHeadset(String address) {
2121         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2122         if (state != null) {
2123             Message msg = new Message();
2124             msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2125             msg.obj = state;
2126             mHfpProfileState.sendMessage(msg);
2127             return true;
2128         }
2129         return false;
2130     }
2131
2132     public boolean disconnectHeadset(String address) {
2133         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2134         if (state != null) {
2135             Message msg = new Message();
2136             msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2137             msg.obj = state;
2138             mHfpProfileState.sendMessage(msg);
2139             return true;
2140         }
2141         return false;
2142     }
2143
2144     public boolean connectSink(String address) {
2145         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2146         if (state != null) {
2147             Message msg = new Message();
2148             msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2149             msg.obj = state;
2150             mA2dpProfileState.sendMessage(msg);
2151             return true;
2152         }
2153         return false;
2154     }
2155
2156     public boolean disconnectSink(String address) {
2157         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2158         if (state != null) {
2159             Message msg = new Message();
2160             msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2161             msg.obj = state;
2162             mA2dpProfileState.sendMessage(msg);
2163             return true;
2164         }
2165         return false;
2166     }
2167
2168     private BluetoothDeviceProfileState addProfileState(String address) {
2169         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2170         if (state != null) return state;
2171
2172         state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2173         mDeviceProfileState.put(address, state);
2174         state.start();
2175         return state;
2176     }
2177
2178     private void removeProfileState(String address) {
2179         mDeviceProfileState.remove(address);
2180     }
2181
2182     private void initProfileState() {
2183         String []bonds = null;
2184         String val = getPropertyInternal("Devices");
2185         if (val != null) {
2186             bonds = val.split(",");
2187         }
2188         if (bonds == null) {
2189             return;
2190         }
2191
2192         for (String path : bonds) {
2193             String address = getAddressFromObjectPath(path);
2194             BluetoothDeviceProfileState state = addProfileState(address);
2195             // Allow 8 secs for SDP records to get registered.
2196             Message msg = new Message();
2197             msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
2198             state.sendMessageDelayed(msg, 8000);
2199         }
2200     }
2201
2202     public boolean notifyIncomingConnection(String address) {
2203         BluetoothDeviceProfileState state =
2204              mDeviceProfileState.get(address);
2205         if (state != null) {
2206             Message msg = new Message();
2207             msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
2208             state.sendMessage(msg);
2209             return true;
2210         }
2211         return false;
2212     }
2213
2214     /*package*/ boolean notifyIncomingA2dpConnection(String address) {
2215        BluetoothDeviceProfileState state =
2216             mDeviceProfileState.get(address);
2217        if (state != null) {
2218            Message msg = new Message();
2219            msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
2220            state.sendMessage(msg);
2221            return true;
2222        }
2223        return false;
2224     }
2225
2226     /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2227         mA2dpService = a2dpService;
2228     }
2229
2230     public void sendProfileStateMessage(int profile, int cmd) {
2231         Message msg = new Message();
2232         msg.what = cmd;
2233         if (profile == BluetoothProfileState.HFP) {
2234             mHfpProfileState.sendMessage(msg);
2235         } else if (profile == BluetoothProfileState.A2DP) {
2236             mA2dpProfileState.sendMessage(msg);
2237         }
2238     }
2239
2240     private static void log(String msg) {
2241         Log.d(TAG, msg);
2242     }
2243
2244     private native static void classInitNative();
2245     private native void initializeNativeDataNative();
2246     private native boolean setupNativeDataNative();
2247     private native boolean tearDownNativeDataNative();
2248     private native void cleanupNativeDataNative();
2249     private native String getAdapterPathNative();
2250
2251     private native int isEnabledNative();
2252     private native int enableNative();
2253     private native int disableNative();
2254
2255     private native Object[] getAdapterPropertiesNative();
2256     private native Object[] getDevicePropertiesNative(String objectPath);
2257     private native boolean setAdapterPropertyStringNative(String key, String value);
2258     private native boolean setAdapterPropertyIntegerNative(String key, int value);
2259     private native boolean setAdapterPropertyBooleanNative(String key, int value);
2260
2261     private native boolean startDiscoveryNative();
2262     private native boolean stopDiscoveryNative();
2263
2264     private native boolean createPairedDeviceNative(String address, int timeout_ms);
2265     private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2266     private native byte[] readAdapterOutOfBandDataNative();
2267
2268     private native boolean cancelDeviceCreationNative(String address);
2269     private native boolean removeDeviceNative(String objectPath);
2270     private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2271             int attributeId);
2272
2273     private native boolean cancelPairingUserInputNative(String address, int nativeData);
2274     private native boolean setPinNative(String address, String pin, int nativeData);
2275     private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2276     private native boolean setPairingConfirmationNative(String address, boolean confirm,
2277             int nativeData);
2278     private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2279                                                         byte[] randomizer, int nativeData);
2280
2281     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2282             int value);
2283     private native boolean createDeviceNative(String address);
2284     /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
2285
2286     private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2287             short channel);
2288     private native boolean removeServiceRecordNative(int handle);
2289     private native boolean setLinkTimeoutNative(String path, int num_slots);
2290 }