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