CachedBluetoothDevice.java revision 2036ebd8896bbabbbe04db34c9e7d8a1be6fe32a
1afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/*
2afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
3afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *
4afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * you may not use this file except in compliance with the License.
6afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * You may obtain a copy of the License at
7afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *
8afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project *
10afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * See the License for the specific language governing permissions and
14afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * limitations under the License.
15afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
16afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
17afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectpackage com.android.settings.bluetooth;
18afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
192aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chanimport android.bluetooth.BluetoothClass;
20e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport android.bluetooth.BluetoothDevice;
21436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.bluetooth.BluetoothProfile;
22e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chanimport android.os.ParcelUuid;
232aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chanimport android.os.SystemClock;
24afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.text.TextUtils;
25afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.Log;
26afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
27afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.ArrayList;
28436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Collection;
29436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Collections;
3050e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganeshimport java.util.HashMap;
31afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.List;
32afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
33afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/**
34d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly * CachedBluetoothDevice represents a remote Bluetooth device. It contains
35afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * attributes of the device (such as the address, name, RSSI, etc.) and
36afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * functionality that can be performed on the device (connect, pair, disconnect,
37afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * etc.).
38afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
39436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyfinal class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
40d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private static final String TAG = "CachedBluetoothDevice";
41436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static final boolean DEBUG = Utils.V;
42afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
43436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final LocalBluetoothAdapter mLocalAdapter;
44436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final LocalBluetoothProfileManager mProfileManager;
45d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private final BluetoothDevice mDevice;
46afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private String mName;
47afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private short mRssi;
4816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly    private BluetoothClass mBtClass;
4950e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState;
50afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
51436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final List<LocalBluetoothProfile> mProfiles =
52436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            new ArrayList<LocalBluetoothProfile>();
53afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
54c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    // List of profiles that were previously in mProfiles, but have been removed
55c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    private final List<LocalBluetoothProfile> mRemovedProfiles =
56c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            new ArrayList<LocalBluetoothProfile>();
57c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
58c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
59c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    private boolean mLocalNapRoleConnected;
60c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
61afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mVisible;
62afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
63436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
64afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
65afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
66afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * When we connect to multiple profiles, we only want to display a single
67afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * error even if they all fail. This tracks that state.
68afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
69afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mIsConnectingErrorPossible;
70afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
712aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
72eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * Last time a bt profile auto-connect was attempted.
73eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * If an ACTION_UUID intent comes in within
742aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect
752aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * again with the new UUIDs
762aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
77eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private long mConnectAttempted;
782aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
79eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    // See mConnectAttempted
802aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
812aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
82dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby    /** Auto-connect after pairing only if locally initiated. */
83dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby    private boolean mConnectAfterPairing;
8484905edb6315ac0af53141cf2f91591443d7422aJaikumar Ganesh
85e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /**
86f907e0bb891eca4d498871a1cc862117a462a076Adam Powell     * Describes the current device and profile for logging.
8784905edb6315ac0af53141cf2f91591443d7422aJaikumar Ganesh     *
88f907e0bb891eca4d498871a1cc862117a462a076Adam Powell     * @param profile Profile to describe
89f907e0bb891eca4d498871a1cc862117a462a076Adam Powell     * @return Description of the device and profile
90e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
91436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private String describe(LocalBluetoothProfile profile) {
92f907e0bb891eca4d498871a1cc862117a462a076Adam Powell        StringBuilder sb = new StringBuilder();
93e79f990490de49b39097433707c2bae6d8238330Jake Hamby        sb.append("Address:").append(mDevice);
94f907e0bb891eca4d498871a1cc862117a462a076Adam Powell        if (profile != null) {
95436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            sb.append(" Profile:").append(profile);
96097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        }
97e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
98f907e0bb891eca4d498871a1cc862117a462a076Adam Powell        return sb.toString();
99f907e0bb891eca4d498871a1cc862117a462a076Adam Powell    }
10084905edb6315ac0af53141cf2f91591443d7422aJaikumar Ganesh
101436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
102436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (Utils.D) {
103436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            Log.d(TAG, "onProfileStateChanged: profile " + profile +
104f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    " newProfileState " + newProfileState);
1056c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        }
106e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
10750e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        mProfileConnectionState.put(profile, newProfileState);
108436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
109f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            if (!mProfiles.contains(profile)) {
110c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                mRemovedProfiles.remove(profile);
111f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                mProfiles.add(profile);
112c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                if (profile instanceof PanProfile &&
113c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                        ((PanProfile) profile).isLocalRoleNap(mDevice)) {
114c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                    // Device doesn't support NAP, so remove PanProfile on disconnect
115c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                    mLocalNapRoleConnected = true;
116c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                }
117e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
118c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
119c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                ((PanProfile) profile).isLocalRoleNap(mDevice) &&
120c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
121c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
122c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            mProfiles.remove(profile);
123c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            mRemovedProfiles.add(profile);
124c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            mLocalNapRoleConnected = false;
125e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
126e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
127e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
128436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    CachedBluetoothDevice(LocalBluetoothAdapter adapter,
129436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            LocalBluetoothProfileManager profileManager,
130436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            BluetoothDevice device) {
131436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mLocalAdapter = adapter;
132436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mProfileManager = profileManager;
133d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mDevice = device;
13450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
135afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fillData();
136afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
137afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
138436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void disconnect() {
139436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
140afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect(profile);
141afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
142afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
143afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
144436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void disconnect(LocalBluetoothProfile profile) {
145436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (profile.disconnect(mDevice)) {
146436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (Utils.D) {
147d3a460cce7b6a4f1e81f3c15b5f7949d28fdc929Jaikumar Ganesh                Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
148f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            }
149afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
150afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
151afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
152436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void connect(boolean connectAllProfiles) {
153436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!ensurePaired()) {
154afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return;
155afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
156afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
157eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
1584bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby        connectWithoutResettingTimer(connectAllProfiles);
159eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
160eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
161436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onBondingDockConnect() {
162582f1f84bb0ad4315ce17826672298c68f1c3b38Jake Hamby        // Attempt to connect if UUIDs are available. Otherwise,
163582f1f84bb0ad4315ce17826672298c68f1c3b38Jake Hamby        // we will connect when the ACTION_UUID intent arrives.
1644bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby        connect(false);
165b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh    }
166b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh
16739ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby    private void connectWithoutResettingTimer(boolean connectAllProfiles) {
168a0d39a39c2f53ebe858685c75c756b51dfac49daJake Hamby        // Try to initialize the profiles if they were not.
169436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (mProfiles.isEmpty()) {
1702aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (!updateProfiles()) {
1712aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                // If UUIDs are not available yet, connect will be happen
1722aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                // upon arrival of the ACTION_UUID intent.
173eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                if (DEBUG) Log.d(TAG, "No profiles. Maybe we will connect later");
1742aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                return;
1752aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
1762aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
1772aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
178afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
179afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
180afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
181eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        int preferredProfiles = 0;
182436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
18339ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
184436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (profile.isPreferred(mDevice)) {
185b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                    ++preferredProfiles;
186436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    connectInt(profile);
187b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                }
188afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
189afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
190eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
191afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
192eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (preferredProfiles == 0) {
193436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            connectAutoConnectableProfiles();
194afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
195afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
196afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
197436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private void connectAutoConnectableProfiles() {
198436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!ensurePaired()) {
199436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return;
200436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
201afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
202afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
203afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
204436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
20539ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (profile.isAutoConnectable()) {
206436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                profile.setPreferred(mDevice, true);
207436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                connectInt(profile);
208b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            }
209afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
210afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
211afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
212436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    /**
213436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     * Connect this device to the specified profile.
214436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     *
215436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     * @param profile the profile to use with the remote device
216436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     */
217436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void connectProfile(LocalBluetoothProfile profile) {
218eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
219afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
220afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
221436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        connectInt(profile);
222afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
223afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
224436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private void connectInt(LocalBluetoothProfile profile) {
225436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!ensurePaired()) {
226436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return;
227436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
228436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (profile.connect(mDevice)) {
229436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (Utils.D) {
230d3a460cce7b6a4f1e81f3c15b5f7949d28fdc929Jaikumar Ganesh                Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
231afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
232436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return;
233afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
234436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
235afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
236afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
237afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean ensurePaired() {
23816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (getBondState() == BluetoothDevice.BOND_NONE) {
239436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            startPairing();
240afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return false;
241afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
242afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
243afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
244afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
245afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
246436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean startPairing() {
247afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Pairing is unreliable while scanning, so cancel discovery
248436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (mLocalAdapter.isDiscovering()) {
249436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mLocalAdapter.cancelDiscovery();
250afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
251afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
252d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (!mDevice.createBond()) {
253436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return false;
254afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
255dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby
256dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        mConnectAfterPairing = true;  // auto-connect after pairing
257436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return true;
258afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
259afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
260ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby    /**
261ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby     * Return true if user initiated pairing on this device. The message text is
262ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby     * slightly different for local vs. remote initiated pairing dialogs.
263ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby     */
264ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby    boolean isUserInitiatedPairing() {
265ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby        return mConnectAfterPairing;
266ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby    }
267ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby
268436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unpair() {
26996b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        disconnect();
270e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
27196b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        int state = getBondState();
272afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
27396b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state == BluetoothDevice.BOND_BONDING) {
274d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mDevice.cancelBondProcess();
27596b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        }
27696b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan
27796b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state != BluetoothDevice.BOND_NONE) {
278436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            final BluetoothDevice dev = mDevice;
279f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            if (dev != null) {
280f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                final boolean successful = dev.removeBond();
281f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                if (successful) {
282436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    if (Utils.D) {
283f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                        Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
284f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    }
285436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                } else if (Utils.V) {
286f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
287f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                            describe(null));
288f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                }
289f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            }
290afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
291afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
292afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
29350e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    int getProfileConnectionState(LocalBluetoothProfile profile) {
29450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        if (mProfileConnectionState == null ||
29550e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh                mProfileConnectionState.get(profile) == null) {
29650e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            // If cache is empty make the binder call to get the state
29750e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int state = profile.getConnectionStatus(mDevice);
29850e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            mProfileConnectionState.put(profile, state);
29950e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        }
30050e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        return mProfileConnectionState.get(profile);
30150e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    }
30250e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh
303436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // TODO: do any of these need to run async on a background thread?
304afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
305afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
306afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
3072aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
308afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
309afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
310afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
314436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothDevice getDevice() {
315d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice;
316afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
317afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
318436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    String getName() {
319afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
320afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
321afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
322436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void setName(String name) {
323d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        if (!mName.equals(name)) {
324d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            if (TextUtils.isEmpty(name)) {
325c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby                // TODO: use friendly name for unknown device (bug 1181856)
326d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                mName = mDevice.getAddress();
327d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            } else {
328d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh                mName = name;
3292036ebd8896bbabbbe04db34c9e7d8a1be6fe32aMatthew Xie                mDevice.setAlias(name);
330d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            }
331d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
332d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
333d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
334d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
335436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refreshName() {
336afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
338afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
339afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
340afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
3412036ebd8896bbabbbe04db34c9e7d8a1be6fe32aMatthew Xie        mName = mDevice.getAliasName();
342afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
343afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
344d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mName = mDevice.getAddress();
345436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (DEBUG) Log.d(TAG, "Device has no name (yet), use address: " + mName);
346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
347afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
349436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refresh() {
350afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
351afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
352afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
353436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isVisible() {
354afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
355afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
356afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
357afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
358afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
361afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
362afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
363afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
364436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    int getBondState() {
365d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getBondState();
366afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
367afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
371afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
372afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
373afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
374afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
375afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
378afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
379afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
380436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isConnected() {
381436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
38250e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int status = getProfileConnectionState(profile);
383436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (status == BluetoothProfile.STATE_CONNECTED) {
384afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
385afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
386afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
387afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
388afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
389afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
390afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
391436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isConnectedProfile(LocalBluetoothProfile profile) {
39250e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        int status = getProfileConnectionState(profile);
393436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return status == BluetoothProfile.STATE_CONNECTED;
394c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby
395c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby    }
396c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby
397436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isBusy() {
398436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
39950e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int status = getProfileConnectionState(profile);
400436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (status == BluetoothProfile.STATE_CONNECTING
401436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    || status == BluetoothProfile.STATE_DISCONNECTING) {
402afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
403afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
404afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
405436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return getBondState() == BluetoothDevice.BOND_BONDING;
406cde015b3d424b0d83030f11432ad0a0589014bd5Jake Hamby    }
407cde015b3d424b0d83030f11432ad0a0589014bd5Jake Hamby
408afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
409afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
410afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
411afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
412d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mBtClass = mDevice.getBluetoothClass();
4132aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
4142aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4152aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private boolean updateProfiles() {
4162aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        ParcelUuid[] uuids = mDevice.getUuids();
4172aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (uuids == null) return false;
4182aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
419436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        ParcelUuid[] localUuids = mLocalAdapter.getUuids();
420498d12bac0df509a4f74a4df8a8c69ec22583a1aJaikumar Ganesh        if (localUuids == null) return false;
421498d12bac0df509a4f74a4df8a8c69ec22583a1aJaikumar Ganesh
422c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles);
4232aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4242aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (DEBUG) {
4252036ebd8896bbabbbe04db34c9e7d8a1be6fe32aMatthew Xie            Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
4262aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
4272aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4288c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4Jaikumar Ganesh            if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
4298c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4Jaikumar Ganesh            Log.v(TAG, "UUID:");
430436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            for (ParcelUuid uuid : uuids) {
431436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.v(TAG, "  " + uuid);
4322aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
433b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly        }
4342aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        return true;
435afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
438afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
439afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
440afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
441436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refreshBtClass() {
442afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
443afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
444afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
445afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
4462aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
4472aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * Refreshes the UI when framework alerts us of a UUID change.
4482aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
449436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onUuidChanged() {
4502aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
4512aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
452eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) {
453eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.e(TAG, "onUuidChanged: Time since last connect"
454eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                    + (SystemClock.elapsedRealtime() - mConnectAttempted));
455eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
4562aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4572aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        /*
4582aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * If a connect was attempted earlier without any UUID, we will do the
4592aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * connect now.
4602aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         */
461436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!mProfiles.isEmpty()
462eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
4632aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                        .elapsedRealtime()) {
46439ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            connectWithoutResettingTimer(false);
4652aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
4662aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        dispatchAttributesChanged();
4672aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
4682aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
469436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onBondingStateChanged(int bondState) {
470eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (bondState == BluetoothDevice.BOND_NONE) {
471eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            mProfiles.clear();
472dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            mConnectAfterPairing = false;  // cancel auto-connect
473eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
4741308453b16c2ca85a456bd4bb7f3c71c916bd83eMichael Chan
475eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        refresh();
476dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby
477dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        if (bondState == BluetoothDevice.BOND_BONDED) {
478dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            if (mDevice.isBluetoothDock()) {
479dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby                onBondingDockConnect();
480dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            } else if (mConnectAfterPairing) {
4814bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby                connect(false);
482dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            }
483dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            mConnectAfterPairing = false;
484dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        }
485eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
486eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
487436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void setBtClass(BluetoothClass btClass) {
48816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (btClass != null && mBtClass != btClass) {
489d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            mBtClass = btClass;
490d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
491d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
492d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
493d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
494436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothClass getBtClass() {
495436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return mBtClass;
496afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
497afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
498436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    List<LocalBluetoothProfile> getProfiles() {
499436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return Collections.unmodifiableList(mProfiles);
50048e90002839e662eb1667471aebeb0483e9fb7dbAmith Yamasani    }
501afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
502436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    List<LocalBluetoothProfile> getConnectableProfiles() {
503436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        List<LocalBluetoothProfile> connectableProfiles =
504436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                new ArrayList<LocalBluetoothProfile>();
505436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
50639ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (profile.isConnectable()) {
507e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                connectableProfiles.add(profile);
508e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
509e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
510e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return connectableProfiles;
511e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    }
512e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
513c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    List<LocalBluetoothProfile> getRemovedProfiles() {
514c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        return mRemovedProfiles;
515c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    }
516c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
517436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void registerCallback(Callback callback) {
518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
519afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
521afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
522afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
523436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unregisterCallback(Callback callback) {
524afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
525afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
526afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
527afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
528afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
529afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
530afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
531afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
532c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby                callback.onDeviceAttributesChanged();
533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
534afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
535afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
536afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
537afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
538afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
539d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.toString();
540afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
541afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
542afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
543afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
544d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
545436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return false;
546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
547d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
548afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
549afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
550afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
551afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
552d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getAddress().hashCode();
553afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
554afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
555436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // This comparison uses non-final fields so the sort order may change
556436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // when device attributes change (such as bonding state). Settings
557436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // will completely refresh the device list when this happens.
558d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public int compareTo(CachedBluetoothDevice another) {
559afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
560436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        int comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
561afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
562afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
569afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
572afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
573afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
575afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
576afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
577436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return mName.compareTo(another.mName);
578afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
579afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
580afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
581c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby        void onDeviceAttributesChanged();
582afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
583afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
584