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;
22b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xieimport android.content.Context;
23b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xieimport android.content.SharedPreferences;
24e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chanimport android.os.ParcelUuid;
252aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chanimport android.os.SystemClock;
26afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.text.TextUtils;
27afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.Log;
282ac143fff796ff1bd5b73484db17b6934c05f18eSwaminatha Balajiimport android.bluetooth.BluetoothAdapter;
29afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
30afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.ArrayList;
31436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Collection;
32436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Collections;
3350e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganeshimport java.util.HashMap;
34afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.List;
35afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
36afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/**
37d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly * CachedBluetoothDevice represents a remote Bluetooth device. It contains
38afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * attributes of the device (such as the address, name, RSSI, etc.) and
39afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * functionality that can be performed on the device (connect, pair, disconnect,
40afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * etc.).
41afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
42436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyfinal class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
43d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private static final String TAG = "CachedBluetoothDevice";
44436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static final boolean DEBUG = Utils.V;
45afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
46b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    private final Context mContext;
47436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final LocalBluetoothAdapter mLocalAdapter;
48436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final LocalBluetoothProfileManager mProfileManager;
49d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private final BluetoothDevice mDevice;
50afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private String mName;
51afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private short mRssi;
5216cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly    private BluetoothClass mBtClass;
5350e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState;
54afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
55436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final List<LocalBluetoothProfile> mProfiles =
56436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            new ArrayList<LocalBluetoothProfile>();
57afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
58c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    // List of profiles that were previously in mProfiles, but have been removed
59c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    private final List<LocalBluetoothProfile> mRemovedProfiles =
60c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            new ArrayList<LocalBluetoothProfile>();
61c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
62c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
63c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    private boolean mLocalNapRoleConnected;
64c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
65afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mVisible;
66afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
67b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    private int mPhonebookPermissionChoice;
68b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
695b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    private int mMessagePermissionChoice;
705b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie
71ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private int mMessageRejectionCount;
725b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie
73436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
74afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
755b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    // Following constants indicate the user's choices of Phone book/message access settings
76b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    // User hasn't made any choice or settings app has wiped out the memory
77361d870e5415f8642cb106b4329d194673b4dd02PauloftheWest    public final static int ACCESS_UNKNOWN = 0;
78b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    // User has accepted the connection and let Settings app remember the decision
79361d870e5415f8642cb106b4329d194673b4dd02PauloftheWest    public final static int ACCESS_ALLOWED = 1;
80b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    // User has rejected the connection and let Settings app remember the decision
81361d870e5415f8642cb106b4329d194673b4dd02PauloftheWest    public final static int ACCESS_REJECTED = 2;
825b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie
83ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    // How many times user should reject the connection to make the choice persist.
84ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
85b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
86ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
87b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
88afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
89afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * When we connect to multiple profiles, we only want to display a single
90afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * error even if they all fail. This tracks that state.
91afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
92afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mIsConnectingErrorPossible;
93afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
942aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
95eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * Last time a bt profile auto-connect was attempted.
96eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * If an ACTION_UUID intent comes in within
972aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect
982aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * again with the new UUIDs
992aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
100eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private long mConnectAttempted;
1012aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
102eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    // See mConnectAttempted
1032aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
1042aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
105dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby    /** Auto-connect after pairing only if locally initiated. */
106dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby    private boolean mConnectAfterPairing;
10784905edb6315ac0af53141cf2f91591443d7422aJaikumar Ganesh
108e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /**
109f907e0bb891eca4d498871a1cc862117a462a076Adam Powell     * Describes the current device and profile for logging.
11084905edb6315ac0af53141cf2f91591443d7422aJaikumar Ganesh     *
111f907e0bb891eca4d498871a1cc862117a462a076Adam Powell     * @param profile Profile to describe
112f907e0bb891eca4d498871a1cc862117a462a076Adam Powell     * @return Description of the device and profile
113e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
114436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private String describe(LocalBluetoothProfile profile) {
115f907e0bb891eca4d498871a1cc862117a462a076Adam Powell        StringBuilder sb = new StringBuilder();
116e79f990490de49b39097433707c2bae6d8238330Jake Hamby        sb.append("Address:").append(mDevice);
117f907e0bb891eca4d498871a1cc862117a462a076Adam Powell        if (profile != null) {
118436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            sb.append(" Profile:").append(profile);
119097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        }
120e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
121f907e0bb891eca4d498871a1cc862117a462a076Adam Powell        return sb.toString();
122f907e0bb891eca4d498871a1cc862117a462a076Adam Powell    }
12384905edb6315ac0af53141cf2f91591443d7422aJaikumar Ganesh
124436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
125436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (Utils.D) {
126436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            Log.d(TAG, "onProfileStateChanged: profile " + profile +
127f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    " newProfileState " + newProfileState);
1286c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        }
1297951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF)
1307951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        {
1317951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta            if (Utils.D) Log.d(TAG, " BT Turninig Off...Profile conn state change ignored...");
1327951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta            return;
1337951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        }
13450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        mProfileConnectionState.put(profile, newProfileState);
135436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
13600656b8e46e187e5fa63b8a80cd4f42367c1c924PauloftheWest            if (profile instanceof MapProfile) {
13700656b8e46e187e5fa63b8a80cd4f42367c1c924PauloftheWest                profile.setPreferred(mDevice, true);
13800656b8e46e187e5fa63b8a80cd4f42367c1c924PauloftheWest            } else if (!mProfiles.contains(profile)) {
139c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                mRemovedProfiles.remove(profile);
140f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                mProfiles.add(profile);
141c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                if (profile instanceof PanProfile &&
142c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                        ((PanProfile) profile).isLocalRoleNap(mDevice)) {
143c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                    // Device doesn't support NAP, so remove PanProfile on disconnect
144c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                    mLocalNapRoleConnected = true;
145c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                }
146e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
1475d2595f05182910c1dd0ed1302bf59071b55ae79Kim Schulz        } else if (profile instanceof MapProfile &&
1485d2595f05182910c1dd0ed1302bf59071b55ae79Kim Schulz                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
1495d2595f05182910c1dd0ed1302bf59071b55ae79Kim Schulz            profile.setPreferred(mDevice, false);
150c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
151c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                ((PanProfile) profile).isLocalRoleNap(mDevice) &&
152c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
153c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
154c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            mProfiles.remove(profile);
155c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            mRemovedProfiles.add(profile);
156c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby            mLocalNapRoleConnected = false;
157e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
158e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
159e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
160b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    CachedBluetoothDevice(Context context,
161b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie                          LocalBluetoothAdapter adapter,
162b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie                          LocalBluetoothProfileManager profileManager,
163b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie                          BluetoothDevice device) {
164b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie        mContext = context;
165436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mLocalAdapter = adapter;
166436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mProfileManager = profileManager;
167d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mDevice = device;
16850e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
169afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fillData();
170afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
171afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
172436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void disconnect() {
173436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
174afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect(profile);
175afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
1760654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        // Disconnect  PBAP server in case its connected
1770654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        // This is to ensure all the profiles are disconnected as some CK/Hs do not
1780654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        // disconnect  PBAP connection when HF connection is brought down
1790654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        PbapServerProfile PbapProfile = mProfileManager.getPbapProfile();
1800654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED)
1810654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        {
1820654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta            PbapProfile.disconnect(mDevice);
1830654b71534411f4d93502f6ed8d8e864ef5b88efGanesh Ganapathi Batta        }
184afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
185afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
186436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void disconnect(LocalBluetoothProfile profile) {
187436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (profile.disconnect(mDevice)) {
188436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (Utils.D) {
189d3a460cce7b6a4f1e81f3c15b5f7949d28fdc929Jaikumar Ganesh                Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
190f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            }
191afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
192afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
193afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
194436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void connect(boolean connectAllProfiles) {
195436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!ensurePaired()) {
196afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return;
197afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
198afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
199eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
2004bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby        connectWithoutResettingTimer(connectAllProfiles);
201eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
202eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
203436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onBondingDockConnect() {
204582f1f84bb0ad4315ce17826672298c68f1c3b38Jake Hamby        // Attempt to connect if UUIDs are available. Otherwise,
205582f1f84bb0ad4315ce17826672298c68f1c3b38Jake Hamby        // we will connect when the ACTION_UUID intent arrives.
2064bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby        connect(false);
207b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh    }
208b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh
20939ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby    private void connectWithoutResettingTimer(boolean connectAllProfiles) {
210a0d39a39c2f53ebe858685c75c756b51dfac49daJake Hamby        // Try to initialize the profiles if they were not.
211436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (mProfiles.isEmpty()) {
2126e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
2136e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
2146e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // from bluetooth stack but ACTION.uuid is not sent yet.
2156e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // Eventually ACTION.uuid will be received which shall trigger the connection of the
2166e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // various profiles
2176e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // If UUIDs are not available yet, connect will be happen
2186e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            // upon arrival of the ACTION_UUID intent.
2196e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            Log.d(TAG, "No profiles. Maybe we will connect later");
2206e4060212e537bdcb69b21c2ba814c23cd393d42Kausik Sinnaswamy            return;
2212aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
2222aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
223afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
224afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
225afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
226eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        int preferredProfiles = 0;
227436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
22839ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
229436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (profile.isPreferred(mDevice)) {
230b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                    ++preferredProfiles;
231436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    connectInt(profile);
232b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                }
233afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
234afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
235eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
236afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
237eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (preferredProfiles == 0) {
238436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            connectAutoConnectableProfiles();
239afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
240afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
241afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
242436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private void connectAutoConnectableProfiles() {
243436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!ensurePaired()) {
244436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return;
245436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
246afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
247afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
248afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
249436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
25039ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (profile.isAutoConnectable()) {
251436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                profile.setPreferred(mDevice, true);
252436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                connectInt(profile);
253b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            }
254afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
255afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
256afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
257436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    /**
258436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     * Connect this device to the specified profile.
259436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     *
260436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     * @param profile the profile to use with the remote device
261436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby     */
262436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void connectProfile(LocalBluetoothProfile profile) {
263eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
264afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
265afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
266436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        connectInt(profile);
267f457e613d5d91322e0c967fb801899d55e3db9e5Syed Ibrahim M        // Refresh the UI based on profile.connect() call
268f457e613d5d91322e0c967fb801899d55e3db9e5Syed Ibrahim M        refresh();
269afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
270afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
2712ac143fff796ff1bd5b73484db17b6934c05f18eSwaminatha Balaji    synchronized void connectInt(LocalBluetoothProfile profile) {
272436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!ensurePaired()) {
273436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return;
274436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
275436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (profile.connect(mDevice)) {
276436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (Utils.D) {
277d3a460cce7b6a4f1e81f3c15b5f7949d28fdc929Jaikumar Ganesh                Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
278afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
279436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return;
280afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
281436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
282afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
283afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
284afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean ensurePaired() {
28516cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (getBondState() == BluetoothDevice.BOND_NONE) {
286436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            startPairing();
287afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return false;
288afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
289afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
290afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
291afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
292afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
293436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean startPairing() {
294afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Pairing is unreliable while scanning, so cancel discovery
295436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (mLocalAdapter.isDiscovering()) {
296436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mLocalAdapter.cancelDiscovery();
297afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
298afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
299d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (!mDevice.createBond()) {
300436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return false;
301afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
302dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby
303dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        mConnectAfterPairing = true;  // auto-connect after pairing
304436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return true;
305afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
306afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
307ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby    /**
308ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby     * Return true if user initiated pairing on this device. The message text is
309ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby     * slightly different for local vs. remote initiated pairing dialogs.
310ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby     */
311ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby    boolean isUserInitiatedPairing() {
312ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby        return mConnectAfterPairing;
313ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby    }
314ca9812a8521fcc483e821fd5a88ec421de0b8f66Jake Hamby
315436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unpair() {
31696b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        int state = getBondState();
317afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
31896b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state == BluetoothDevice.BOND_BONDING) {
319d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mDevice.cancelBondProcess();
32096b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        }
32196b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan
32296b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state != BluetoothDevice.BOND_NONE) {
323436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            final BluetoothDevice dev = mDevice;
324f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            if (dev != null) {
325f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                final boolean successful = dev.removeBond();
326f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                if (successful) {
327436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    if (Utils.D) {
328f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                        Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
329f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    }
330436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                } else if (Utils.V) {
331f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                    Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
332f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                            describe(null));
333f907e0bb891eca4d498871a1cc862117a462a076Adam Powell                }
334f907e0bb891eca4d498871a1cc862117a462a076Adam Powell            }
335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
336afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
33850e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    int getProfileConnectionState(LocalBluetoothProfile profile) {
33950e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        if (mProfileConnectionState == null ||
34050e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh                mProfileConnectionState.get(profile) == null) {
34150e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            // If cache is empty make the binder call to get the state
34250e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int state = profile.getConnectionStatus(mDevice);
34350e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            mProfileConnectionState.put(profile, state);
34450e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        }
34550e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        return mProfileConnectionState.get(profile);
34650e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh    }
34750e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh
3487951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta    public void clearProfileConnectionState ()
3497951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta    {
3507951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        if (Utils.D) {
3517951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta            Log.d(TAG," Clearing all connection state for dev:" + mDevice.getName());
3527951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        }
3537951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        for (LocalBluetoothProfile profile :getProfiles()) {
3547951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta            mProfileConnectionState.put(profile, BluetoothProfile.STATE_DISCONNECTED);
3557951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta        }
3567951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta    }
3577951b43bd9a759b01ea994b341c102b03011ca3cGanesh Ganapathi Batta
358436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // TODO: do any of these need to run async on a background thread?
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
361afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
3622aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
363ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        migratePhonebookPermissionChoice();
364ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        migrateMessagePermissionChoice();
365ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        fetchMessageRejectionCount();
366afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
367afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
371436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothDevice getDevice() {
372d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice;
373afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
374afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
375436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    String getName() {
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
378afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
3792f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie    /**
3802f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie     * Populate name from BluetoothDevice.ACTION_FOUND intent
3812f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie     */
3822f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie    void setNewName(String name) {
3832f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie        if (mName == null) {
3842f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie            mName = name;
3852f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie            if (mName == null || TextUtils.isEmpty(mName)) {
386d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                mName = mDevice.getAddress();
387d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            }
388d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
389d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
390d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
391d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
3922f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie    /**
3932f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie     * user changes the device name
3942f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie     */
3952f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie    void setName(String name) {
3962f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie        if (!mName.equals(name)) {
3972f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie            mName = name;
3982f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie            mDevice.setAlias(name);
3992f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie            dispatchAttributesChanged();
4002f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie        }
4012f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie    }
4022f837509dd3b9341339d30e64c43e7aa857212abMatthew Xie
403436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refreshName() {
404afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
405afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
406afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
407afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
408afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
4092036ebd8896bbabbbe04db34c9e7d8a1be6fe32aMatthew Xie        mName = mDevice.getAliasName();
410afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
411afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
412d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mName = mDevice.getAddress();
413436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (DEBUG) Log.d(TAG, "Device has no name (yet), use address: " + mName);
414afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
415afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
416afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
417436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refresh() {
418afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
419afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
420afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
421436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isVisible() {
422afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
423afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
424afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
425afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
426afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
427afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
428afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
429afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
430afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
431afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
432436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    int getBondState() {
433d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getBondState();
434afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
435afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
438afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
439afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
440afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
441afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
442afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
443afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
444afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
445afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
446afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
447afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
448436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isConnected() {
449436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
45050e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int status = getProfileConnectionState(profile);
451436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (status == BluetoothProfile.STATE_CONNECTED) {
452afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
453afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
454afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
455afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
456afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
457afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
458afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
459436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isConnectedProfile(LocalBluetoothProfile profile) {
46050e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh        int status = getProfileConnectionState(profile);
461436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return status == BluetoothProfile.STATE_CONNECTED;
462c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby
463c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby    }
464c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby
465436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean isBusy() {
466436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
46750e0b0cf87f724d25ada10353867f14ebbf644aaJaikumar Ganesh            int status = getProfileConnectionState(profile);
468436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (status == BluetoothProfile.STATE_CONNECTING
469436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    || status == BluetoothProfile.STATE_DISCONNECTING) {
470afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
471afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
472afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
473436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return getBondState() == BluetoothDevice.BOND_BONDING;
474cde015b3d424b0d83030f11432ad0a0589014bd5Jake Hamby    }
475cde015b3d424b0d83030f11432ad0a0589014bd5Jake Hamby
476afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
477afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
478afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
479afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
480d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mBtClass = mDevice.getBluetoothClass();
4812aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
4822aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4832aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private boolean updateProfiles() {
4842aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        ParcelUuid[] uuids = mDevice.getUuids();
4852aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (uuids == null) return false;
4862aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
487436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        ParcelUuid[] localUuids = mLocalAdapter.getUuids();
488498d12bac0df509a4f74a4df8a8c69ec22583a1aJaikumar Ganesh        if (localUuids == null) return false;
489498d12bac0df509a4f74a4df8a8c69ec22583a1aJaikumar Ganesh
4901b5bd091ebe4be7d714d19d5b35d8d7233a7713aZhihai Xu        mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
4911b5bd091ebe4be7d714d19d5b35d8d7233a7713aZhihai Xu                                       mLocalNapRoleConnected, mDevice);
4922aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4932aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (DEBUG) {
4942036ebd8896bbabbbe04db34c9e7d8a1be6fe32aMatthew Xie            Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
4952aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
4962aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
4978c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4Jaikumar Ganesh            if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
4988c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4Jaikumar Ganesh            Log.v(TAG, "UUID:");
499436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            for (ParcelUuid uuid : uuids) {
500436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.v(TAG, "  " + uuid);
5012aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
502b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly        }
5032aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        return true;
504afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
505afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
506afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
507afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
508afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
509afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
510436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void refreshBtClass() {
511afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
514afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
5152aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
5162aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * Refreshes the UI when framework alerts us of a UUID change.
5172aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
518436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onUuidChanged() {
5192aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
5202aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
521eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) {
522eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.e(TAG, "onUuidChanged: Time since last connect"
523eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                    + (SystemClock.elapsedRealtime() - mConnectAttempted));
524eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
5252aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
5262aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        /*
5272aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * If a connect was attempted earlier without any UUID, we will do the
5282aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * connect now.
5292aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         */
530436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (!mProfiles.isEmpty()
531eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
5322aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                        .elapsedRealtime()) {
53339ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            connectWithoutResettingTimer(false);
5342aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
5352aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        dispatchAttributesChanged();
5362aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
5372aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
538436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void onBondingStateChanged(int bondState) {
539eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (bondState == BluetoothDevice.BOND_NONE) {
540eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            mProfiles.clear();
541dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            mConnectAfterPairing = false;  // cancel auto-connect
54258938ba0d6cddb53a8a76d2e0a2217cf49c92395PauloftheWest            setPhonebookPermissionChoice(ACCESS_UNKNOWN);
5435b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie            setMessagePermissionChoice(ACCESS_UNKNOWN);
544ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            mMessageRejectionCount = 0;
545ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            saveMessageRejectionCount();
546eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
5471308453b16c2ca85a456bd4bb7f3c71c916bd83eMichael Chan
548eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        refresh();
549dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby
550dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        if (bondState == BluetoothDevice.BOND_BONDED) {
551dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            if (mDevice.isBluetoothDock()) {
552dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby                onBondingDockConnect();
553dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            } else if (mConnectAfterPairing) {
5544bd7cb0e07a1fbc4e658810631132cebd5b1fdd6Jake Hamby                connect(false);
555dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            }
556dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby            mConnectAfterPairing = false;
557dd79a33ba53fec530094f4d2fe37f0538530d9f8Jake Hamby        }
558eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
559eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
560436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void setBtClass(BluetoothClass btClass) {
56116cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (btClass != null && mBtClass != btClass) {
562d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            mBtClass = btClass;
563d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
564d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
565d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
566d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
567436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothClass getBtClass() {
568436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return mBtClass;
569afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
571436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    List<LocalBluetoothProfile> getProfiles() {
572436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return Collections.unmodifiableList(mProfiles);
57348e90002839e662eb1667471aebeb0483e9fb7dbAmith Yamasani    }
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
575436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    List<LocalBluetoothProfile> getConnectableProfiles() {
576436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        List<LocalBluetoothProfile> connectableProfiles =
577436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                new ArrayList<LocalBluetoothProfile>();
578436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (LocalBluetoothProfile profile : mProfiles) {
57939ef225e7c44a48aa9cfdf5c56ecd4ddfb95ae89Jake Hamby            if (profile.isConnectable()) {
580e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                connectableProfiles.add(profile);
581e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
582e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
583e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return connectableProfiles;
584e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    }
585e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
586c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    List<LocalBluetoothProfile> getRemovedProfiles() {
587c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby        return mRemovedProfiles;
588c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby    }
589c777ee29c856e1d1a2a61ccd799b6e18b50febdcJake Hamby
590436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void registerCallback(Callback callback) {
591afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
592afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
593afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
594afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
595afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
596436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unregisterCallback(Callback callback) {
597afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
598afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
599afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
600afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
601afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
602afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
603afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
604afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
605c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby                callback.onDeviceAttributesChanged();
606afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
607afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
608afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
609afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
610afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
611afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
612d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.toString();
613afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
614afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
615afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
616afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
617d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
618436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return false;
619afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
620d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
621afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
622afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
623afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
624afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
625d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getAddress().hashCode();
626afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
627afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
628436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // This comparison uses non-final fields so the sort order may change
629436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // when device attributes change (such as bonding state). Settings
630436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // will completely refresh the device list when this happens.
631d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public int compareTo(CachedBluetoothDevice another) {
632afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
633436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        int comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
634afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
635afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
636afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
637afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
638afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
639afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
640afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
641afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
642afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
643afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
644afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
645afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
646afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
647afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
648afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
649afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
650436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return mName.compareTo(another.mName);
651afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
652afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
653afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
654c090feb64f674d9840993736a24f9667f8b0e0d5Jake Hamby        void onDeviceAttributesChanged();
655afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
656b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
657b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    int getPhonebookPermissionChoice() {
658ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        int permission = mDevice.getPhonebookAccessPermission();
659ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (permission == BluetoothDevice.ACCESS_ALLOWED) {
660ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            return ACCESS_ALLOWED;
661ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
662ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            return ACCESS_REJECTED;
663ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        }
664ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        return ACCESS_UNKNOWN;
665b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    }
666b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
667b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    void setPhonebookPermissionChoice(int permissionChoice) {
668ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        int permission = BluetoothDevice.ACCESS_UNKNOWN;
669ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (permissionChoice == ACCESS_ALLOWED) {
670ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            permission = BluetoothDevice.ACCESS_ALLOWED;
671ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        } else if (permissionChoice == ACCESS_REJECTED) {
672ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            permission = BluetoothDevice.ACCESS_REJECTED;
673b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie        }
674ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        mDevice.setPhonebookAccessPermission(permission);
675b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    }
676b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
677ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
678ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    // app's shared preferences).
679ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private void migratePhonebookPermissionChoice() {
680ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        SharedPreferences preferences = mContext.getSharedPreferences(
681ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                "bluetooth_phonebook_permission", Context.MODE_PRIVATE);
682ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (!preferences.contains(mDevice.getAddress())) {
683ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            return;
684ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        }
685ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee
686ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
687ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
688ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            if (oldPermission == ACCESS_ALLOWED) {
689ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
690ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            } else if (oldPermission == ACCESS_REJECTED) {
691ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
692ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            }
693ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        }
694ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee
695ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        SharedPreferences.Editor editor = preferences.edit();
696ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        editor.remove(mDevice.getAddress());
697ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        editor.commit();
6985b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    }
6995b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie
7005b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    int getMessagePermissionChoice() {
701ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        int permission = mDevice.getMessageAccessPermission();
702ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (permission == BluetoothDevice.ACCESS_ALLOWED) {
703ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            return ACCESS_ALLOWED;
704ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
705ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            return ACCESS_REJECTED;
706ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        }
707ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        return ACCESS_UNKNOWN;
7085b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    }
7095b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie
7105b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    void setMessagePermissionChoice(int permissionChoice) {
711ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        int permission = BluetoothDevice.ACCESS_UNKNOWN;
712ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (permissionChoice == ACCESS_ALLOWED) {
713ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            permission = BluetoothDevice.ACCESS_ALLOWED;
714ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        } else if (permissionChoice == ACCESS_REJECTED) {
715ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            permission = BluetoothDevice.ACCESS_REJECTED;
7163fd5e2839fba6afc18d99006d61c063c2ed4f121Zhihai Xu        }
717ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        mDevice.setMessageAccessPermission(permission);
718ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    }
7199998639a657825babd2514fd9af9726c068544c8Zhihai Xu
720ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
721ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    // app's shared preferences).
722ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private void migrateMessagePermissionChoice() {
723ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        SharedPreferences preferences = mContext.getSharedPreferences(
724ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                "bluetooth_message_permission", Context.MODE_PRIVATE);
725ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (!preferences.contains(mDevice.getAddress())) {
726ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            return;
727ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        }
7289998639a657825babd2514fd9af9726c068544c8Zhihai Xu
729ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (mDevice.getMessageAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
730ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
731ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            if (oldPermission == ACCESS_ALLOWED) {
732ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
733ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            } else if (oldPermission == ACCESS_REJECTED) {
734ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
735ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            }
7365b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie        }
737ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee
738ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        SharedPreferences.Editor editor = preferences.edit();
739ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        editor.remove(mDevice.getAddress());
7405b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie        editor.commit();
7415b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie    }
7425b5b1e39ddd28af55cf5dd4a9ca9bf29828a66a6Matthew Xie
743ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    /**
744ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee     * @return Whether this rejection should persist.
745ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee     */
746ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    boolean checkAndIncreaseMessageRejectionCount() {
747ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (mMessageRejectionCount < MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST) {
748ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            mMessageRejectionCount++;
749ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            saveMessageRejectionCount();
750ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        }
751ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        return mMessageRejectionCount >= MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST;
752b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie    }
753b707255a440e53ec2865c4b34c7ea07d16d471bdMatthew Xie
754ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private void fetchMessageRejectionCount() {
755ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        SharedPreferences preference = mContext.getSharedPreferences(
756ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE);
757ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        mMessageRejectionCount = preference.getInt(mDevice.getAddress(), 0);
7581083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu    }
7591083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu
760ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee    private void saveMessageRejectionCount() {
761ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        SharedPreferences.Editor editor = mContext.getSharedPreferences(
762ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee                MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE).edit();
763ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee        if (mMessageRejectionCount == 0) {
7641083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu            editor.remove(mDevice.getAddress());
7651083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu        } else {
766ac8b5b7fd6b6774d22d39bb9a4becca1908915c7Edward Jee            editor.putInt(mDevice.getAddress(), mMessageRejectionCount);
7671083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu        }
7681083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu        editor.commit();
7691083b4726ba915261034612ae9b757d67ed19fcbZhihai Xu    }
770afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
771