BluetoothService.java revision a224f70b1efc29d9698da5b5c143251a43838f2b
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
25package android.server;
26
27import android.bluetooth.BluetoothAdapter;
28import android.bluetooth.BluetoothClass;
29import android.bluetooth.BluetoothDevice;
30import android.bluetooth.BluetoothHeadset;
31import android.bluetooth.BluetoothDeviceProfileState;
32import android.bluetooth.BluetoothProfileState;
33import android.bluetooth.BluetoothSocket;
34import android.bluetooth.BluetoothUuid;
35import android.bluetooth.IBluetooth;
36import android.bluetooth.IBluetoothCallback;
37import android.content.BroadcastReceiver;
38import android.content.ContentResolver;
39import android.content.Context;
40import android.content.Intent;
41import android.content.IntentFilter;
42import android.content.SharedPreferences;
43import android.os.Binder;
44import android.os.Handler;
45import android.os.IBinder;
46import android.os.Message;
47import android.os.ParcelUuid;
48import android.os.RemoteException;
49import android.os.ServiceManager;
50import android.os.SystemService;
51import android.provider.Settings;
52import android.util.Log;
53import android.util.Pair;
54
55import com.android.internal.app.IBatteryStats;
56
57import java.io.BufferedInputStream;
58import java.io.BufferedReader;
59import java.io.BufferedWriter;
60import java.io.DataInputStream;
61import java.io.File;
62import java.io.FileDescriptor;
63import java.io.FileInputStream;
64import java.io.FileNotFoundException;
65import java.io.FileOutputStream;
66import java.io.FileWriter;
67import java.io.IOException;
68import java.io.InputStreamReader;
69import java.io.PrintWriter;
70import java.io.UnsupportedEncodingException;
71import java.util.ArrayList;
72import java.util.Arrays;
73import java.util.HashMap;
74import java.util.Iterator;
75import java.util.Map;
76
77public 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 regsiter 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 addreses 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 blacklis
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_s 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: AdpaterObjectPath:" + adapterObjectPath +
2087                    "  or deviceObjectPath:" + objectPath + " is null");
2088            return null;
2089        }
2090        if (!objectPath.startsWith(adapterObjectPath)) {
2091            Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + 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    private static void log(String msg) {
2231        Log.d(TAG, msg);
2232    }
2233
2234    private native static void classInitNative();
2235    private native void initializeNativeDataNative();
2236    private native boolean setupNativeDataNative();
2237    private native boolean tearDownNativeDataNative();
2238    private native void cleanupNativeDataNative();
2239    private native String getAdapterPathNative();
2240
2241    private native int isEnabledNative();
2242    private native int enableNative();
2243    private native int disableNative();
2244
2245    private native Object[] getAdapterPropertiesNative();
2246    private native Object[] getDevicePropertiesNative(String objectPath);
2247    private native boolean setAdapterPropertyStringNative(String key, String value);
2248    private native boolean setAdapterPropertyIntegerNative(String key, int value);
2249    private native boolean setAdapterPropertyBooleanNative(String key, int value);
2250
2251    private native boolean startDiscoveryNative();
2252    private native boolean stopDiscoveryNative();
2253
2254    private native boolean createPairedDeviceNative(String address, int timeout_ms);
2255    private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2256    private native byte[] readAdapterOutOfBandDataNative();
2257
2258    private native boolean cancelDeviceCreationNative(String address);
2259    private native boolean removeDeviceNative(String objectPath);
2260    private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2261            int attributeId);
2262
2263    private native boolean cancelPairingUserInputNative(String address, int nativeData);
2264    private native boolean setPinNative(String address, String pin, int nativeData);
2265    private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2266    private native boolean setPairingConfirmationNative(String address, boolean confirm,
2267            int nativeData);
2268    private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2269                                                        byte[] randomizer, int nativeData);
2270
2271    private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2272            int value);
2273    private native boolean createDeviceNative(String address);
2274    /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
2275
2276    private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2277            short channel);
2278    private native boolean removeServiceRecordNative(int handle);
2279    private native boolean setLinkTimeoutNative(String path, int num_slots);
2280}
2281