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