CachedBluetoothDevice.java revision c777ee29c856e1d1a2a61ccd799b6e18b50febdc
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
260436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unpair() {
26196b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        disconnect();
262e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
26396b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        int state = getBondState();
264afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
26596b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state == BluetoothDevice.BOND_BONDING) {
266d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mDevice.cancelBondProcess();
26796b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        }
26896b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan
26996b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state != BluetoothDevice.BOND_NONE) {
270436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            final BluetoothDevice dev = mDevice;
271f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            if (dev != null) {
272f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                final boolean successful = dev.removeBond();
273f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                if (successful) {
274436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    if (Utils.D) {
275f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                        Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
276f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    }
277436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                } else if (Utils.V) {
278f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
279f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                            describe(null));
280f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                }
281f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            }
282afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
283afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
284afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
28550e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    int getProfileConnectionState(LocalBluetoothProfile profile) {
28650e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        if (mProfileConnectionState == null ||
28750e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh                mProfileConnectionState.get(profile) == null) {
28850e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            // If cache is empty make the binder call to get the state
28950e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int state = profile.getConnectionStatus(mDevice);
29050e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            mProfileConnectionState.put(profile, state);
29150e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        }
29250e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        return mProfileConnectionState.get(profile);
29350e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    }
29450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh
295436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // TODO: do any of these need to run async on a background thread?
296afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
297afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
298afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
2992aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
300afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
301afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
302afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
303afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
304afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
305afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
306436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothDevice getDevice() {
307d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice;
308afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
309afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
310436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    String getName() {
311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
314436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void setName(String name) {
315d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        if (!mName.equals(name)) {
316d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            if (TextUtils.isEmpty(name)) {
317c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby                // TODO: use friendly name for unknown device (bug 1181856)
318d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                mName = mDevice.getAddress();
319d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            } else {
320d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh                mName = name;
321d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            }
322c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby            // TODO: save custom device name in preferences
323d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
324d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
325d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
326d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
327436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refreshName() {
328afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
329afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
330afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
331afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
332afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
333d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mName = mDevice.getName();
334afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
336d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mName = mDevice.getAddress();
337436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (DEBUG) Log.d(TAG, "Device has no name (yet), use address: " + mName);
338afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
339afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
340afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
341436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refresh() {
342afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
343afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
344afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
345436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isVisible() {
346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
347afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
349afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
350afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
351afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
352afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
353afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
354afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
355afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
356436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    int getBondState() {
357d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getBondState();
358afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
361afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
362afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
363afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
364afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
365afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
366afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
367afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
371afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
372436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isConnected() {
373436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
37450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int status = getProfileConnectionState(profile);
375436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (status == BluetoothProfile.STATE_CONNECTED) {
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
378afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
379afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
380afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
381afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
382afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
383436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isConnectedProfile(LocalBluetoothProfile profile) {
38450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        int status = getProfileConnectionState(profile);
385436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return status == BluetoothProfile.STATE_CONNECTED;
386c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby
387c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby    }
388c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby
389436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isBusy() {
390436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
39150e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int status = getProfileConnectionState(profile);
392436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (status == BluetoothProfile.STATE_CONNECTING
393436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    || status == BluetoothProfile.STATE_DISCONNECTING) {
394afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
395afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
396afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
397436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return getBondState() == BluetoothDevice.BOND_BONDING;
398cde015b3d424b0d83030f11432ad0a0589014bd5Jake Hamby    }
399cde015b3d424b0d83030f11432ad0a0589014bd5Jake Hamby
400afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
401afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
402afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
403afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
404d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mBtClass = mDevice.getBluetoothClass();
4052aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
4062aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4072aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private boolean updateProfiles() {
4082aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        ParcelUuid[] uuids = mDevice.getUuids();
4092aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (uuids == null) return false;
4102aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
411436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        ParcelUuid[] localUuids = mLocalAdapter.getUuids();
412498d12bac0df509a4f74a4df8a8c69ec22583a1aJaikumar Ganesh        if (localUuids == null) return false;
413498d12bac0df509a4f74a4df8a8c69ec22583a1aJaikumar Ganesh
414c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles);
4152aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4162aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (DEBUG) {
4172aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            Log.e(TAG, "updating profiles for " + mDevice.getName());
4182aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
4192aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4208c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4Jaikumar Ganesh            if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
4218c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4Jaikumar Ganesh            Log.v(TAG, "UUID:");
422436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            for (ParcelUuid uuid : uuids) {
423436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.v(TAG, "  " + uuid);
4242aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
425b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly        }
4262aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        return true;
427afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
428afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
429afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
430afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
431afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
432afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
433436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refreshBtClass() {
434afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
435afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
4382aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
4392aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * Refreshes the UI when framework alerts us of a UUID change.
4402aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
441436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onUuidChanged() {
4422aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
4432aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
444eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) {
445eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.e(TAG, "onUuidChanged: Time since last connect"
446eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                    + (SystemClock.elapsedRealtime() - mConnectAttempted));
447eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
4482aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4492aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        /*
4502aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * If a connect was attempted earlier without any UUID, we will do the
4512aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * connect now.
4522aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         */
453436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!mProfiles.isEmpty()
454eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
4552aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                        .elapsedRealtime()) {
45639ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            connectWithoutResettingTimer(false);
4572aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
4582aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        dispatchAttributesChanged();
4592aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
4602aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
461436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onBondingStateChanged(int bondState) {
462eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (bondState == BluetoothDevice.BOND_NONE) {
463eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            mProfiles.clear();
464dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            mConnectAfterPairing = false;  // cancel auto-connect
465eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
4661308453b16c2ca85a456bd4bb7f3c71c916bd83eMichael Chan
467eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        refresh();
468dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby
469dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        if (bondState == BluetoothDevice.BOND_BONDED) {
470dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            if (mDevice.isBluetoothDock()) {
471dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby                onBondingDockConnect();
472dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            } else if (mConnectAfterPairing) {
4734bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby                connect(false);
474dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            }
475dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            mConnectAfterPairing = false;
476dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        }
477eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
478eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
479436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void setBtClass(BluetoothClass btClass) {
48016cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (btClass != null && mBtClass != btClass) {
481d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            mBtClass = btClass;
482d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
483d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
484d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
485d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
486436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothClass getBtClass() {
487436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return mBtClass;
488afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
489afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
490436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    List<LocalBluetoothProfile> getProfiles() {
491436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return Collections.unmodifiableList(mProfiles);
49248e90002839e662eb1667471aebeb0483e9fb7dbAmith Yamasani    }
493afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
494436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    List<LocalBluetoothProfile> getConnectableProfiles() {
495436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        List<LocalBluetoothProfile> connectableProfiles =
496436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                new ArrayList<LocalBluetoothProfile>();
497436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
49839ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (profile.isConnectable()) {
499e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                connectableProfiles.add(profile);
500e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
501e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
502e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return connectableProfiles;
503e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    }
504e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
505c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    List<LocalBluetoothProfile> getRemovedProfiles() {
506c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        return mRemovedProfiles;
507c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    }
508c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
509436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void registerCallback(Callback callback) {
510afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
511afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
514afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
515436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unregisterCallback(Callback callback) {
516afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
517afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
519afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
521afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
522afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
523afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
524c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby                callback.onDeviceAttributesChanged();
525afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
526afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
527afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
528afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
529afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
530afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
531d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.toString();
532afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
534afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
535afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
536d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
537436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return false;
538afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
539d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
540afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
541afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
542afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
543afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
544d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getAddress().hashCode();
545afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
547436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // This comparison uses non-final fields so the sort order may change
548436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // when device attributes change (such as bonding state). Settings
549436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // will completely refresh the device list when this happens.
550d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public int compareTo(CachedBluetoothDevice another) {
551afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
552436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        int comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
553afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
554afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
555afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
556afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
557afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
558afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
559afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
560afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
561afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
562afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
569436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return mName.compareTo(another.mName);
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
572afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
573c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby        void onDeviceAttributesChanged();
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
575afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
576