CachedBluetoothDevice.java revision 96b0c1dc0813f7e8c957ec17dc7751693926c6ae
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
19afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.AlertDialog;
20d63c0112251ab4e4e977545368dd703d875012a4Nick Pellyimport android.bluetooth.BluetoothAdapter;
212aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chanimport android.bluetooth.BluetoothClass;
22e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport android.bluetooth.BluetoothDevice;
23afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Context;
24afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.DialogInterface;
25afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Intent;
26afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.res.Resources;
27e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chanimport android.os.ParcelUuid;
282aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chanimport android.os.SystemClock;
29afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.text.TextUtils;
30afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.Log;
31afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.ContextMenu;
32afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.Menu;
33afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.MenuItem;
34afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
356c97742c88303c65e68fe08257a7ccf841f03866Michael Chanimport com.android.settings.R;
366c97742c88303c65e68fe08257a7ccf841f03866Michael Chanimport com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
376c97742c88303c65e68fe08257a7ccf841f03866Michael Chan
38e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.text.DateFormat;
39afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.ArrayList;
40e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.util.Date;
41e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.util.Iterator;
42e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.util.LinkedList;
43afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.List;
443327f7887328c55ef2d837a8e2a07cea2070c744Michael Chanimport java.util.Set;
45afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
46afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/**
47d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly * CachedBluetoothDevice represents a remote Bluetooth device. It contains
48afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * attributes of the device (such as the address, name, RSSI, etc.) and
49afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * functionality that can be performed on the device (connect, pair, disconnect,
50afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * etc.).
51afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
52d63c0112251ab4e4e977545368dd703d875012a4Nick Pellypublic class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
53d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private static final String TAG = "CachedBluetoothDevice";
54f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan    private static final boolean D = LocalBluetoothManager.D;
55f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan    private static final boolean V = LocalBluetoothManager.V;
56293867bd109383a1a26b35bf6accb92c0bad264cMichael Chan    private static final boolean DEBUG = false;
57afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
58afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_CONNECT = Menu.FIRST + 1;
59afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_DISCONNECT = Menu.FIRST + 2;
60afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3;
61afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4;
62afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
63d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private final BluetoothDevice mDevice;
64afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private String mName;
65afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private short mRssi;
6616cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly    private BluetoothClass mBtClass;
67afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
68afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private List<Profile> mProfiles = new ArrayList<Profile>();
69afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
70afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mVisible;
71afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
72afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private final LocalBluetoothManager mLocalManager;
73afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
74afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private List<Callback> mCallbacks = new ArrayList<Callback>();
75afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
76afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
77afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * When we connect to multiple profiles, we only want to display a single
78afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * error even if they all fail. This tracks that state.
79afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
80afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mIsConnectingErrorPossible;
81afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
822aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
83eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * Last time a bt profile auto-connect was attempted.
84eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * If an ACTION_UUID intent comes in within
852aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect
862aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * again with the new UUIDs
872aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
88eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private long mConnectAttempted;
892aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
90eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    // See mConnectAttempted
912aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
922aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
93e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    // Max time to hold the work queue if we don't get or missed a response
94e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    // from the bt framework.
95e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000;
96e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
97e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private enum BluetoothCommand {
9896b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        CONNECT, DISCONNECT, REMOVE_BOND,
99e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
100e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
1018762093e0094e09493fa6cbac8e8fbf265f18165Michael Chan    static class BluetoothJob {
102e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final BluetoothCommand command; // CONNECT, DISCONNECT
103d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        final CachedBluetoothDevice cachedDevice;
104e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final Profile profile; // HEADSET, A2DP, etc
105e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        // 0 means this command was not been sent to the bt framework.
106e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        long timeSent;
107e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
108e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        public BluetoothJob(BluetoothCommand command,
109d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                CachedBluetoothDevice cachedDevice, Profile profile) {
110e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.command = command;
111d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            this.cachedDevice = cachedDevice;
112e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.profile = profile;
113e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.timeSent = 0;
114e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
115d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
116e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        @Override
117e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        public String toString() {
118e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            StringBuilder sb = new StringBuilder();
119e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(command.name());
120d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            sb.append(" Address:").append(cachedDevice.mDevice);
12196b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan            if (profile != null) {
12296b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan                sb.append(" Profile:").append(profile.name());
12396b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan            }
124e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(" TimeSent:");
125e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (timeSent == 0) {
126e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                sb.append("not yet");
127e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
128e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                sb.append(DateFormat.getTimeInstance().format(new Date(timeSent)));
129e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
130e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            return sb.toString();
131e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
132e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
133e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
134e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /**
135e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * We want to serialize connect and disconnect calls. http://b/170538
136e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This are some headsets that may have L2CAP resource limitation. We want
137e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * to limit the bt bandwidth usage.
138e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *
139e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * A queue to keep track of asynchronous calls to the bt framework.  The
140e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * first item, if exist, should be in progress i.e. went to the bt framework
141e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * already, waiting for a notification to come back. The second item and
142e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * beyond have not been sent to the bt framework yet.
143e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
144e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>();
145e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
146e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void queueCommand(BluetoothJob job) {
147e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        synchronized (workQueue) {
148792edfcf12f669378f218c13f8fd4e54d587d87aMichael Chan            if (D) {
149792edfcf12f669378f218c13f8fd4e54d587d87aMichael Chan                Log.d(TAG, workQueue.toString());
150792edfcf12f669378f218c13f8fd4e54d587d87aMichael Chan            }
151097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            boolean processNow = pruneQueue(job);
152e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
153e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Add job to queue
154f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            if (D) {
155f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                Log.d(TAG, "Adding: " + job.toString());
156f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            }
157e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            workQueue.add(job);
158e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
159e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // if there's nothing pending from before, send the command to bt
160e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // framework immediately.
161e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (workQueue.size() == 1 || processNow) {
162e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // If the failed to process, just drop it from the queue.
163e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // There will be no callback to remove this from the queue.
164e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
165e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
166e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
167e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
168d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
169097ed6c671ed733c0967acc32e017020ede4575bMichael Chan    private boolean pruneQueue(BluetoothJob job) {
170097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        boolean removedStaleItems = false;
171097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        long now = System.currentTimeMillis();
172097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        Iterator<BluetoothJob> it = workQueue.iterator();
173097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        while (it.hasNext()) {
174097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            BluetoothJob existingJob = it.next();
175097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
176097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Remove any pending CONNECTS when we receive a DISCONNECT
177097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (job != null && job.command == BluetoothCommand.DISCONNECT) {
178097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                if (existingJob.timeSent == 0
179097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.command == BluetoothCommand.CONNECT
180d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                        && existingJob.cachedDevice.mDevice.equals(job.cachedDevice.mDevice)
181097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.profile == job.profile) {
182f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                    if (D) {
183f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                        Log.d(TAG, "Removed because of a pending disconnect. " + existingJob);
184f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                    }
185097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    it.remove();
186097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    continue;
187097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                }
188097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
189097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
190097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Defensive Code: Remove any job that older than a preset time.
191097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // We never got a call back. It is better to have overlapping
192097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // calls than to get stuck.
193097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (existingJob.timeSent != 0
194097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) {
195097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString());
196097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                it.remove();
197097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                removedStaleItems = true;
198097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                continue;
199097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
200097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        }
201097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        return removedStaleItems;
202097ed6c671ed733c0967acc32e017020ede4575bMichael Chan    }
203e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
204e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean processCommand(BluetoothJob job) {
205e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        boolean successful = false;
206e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (job.timeSent == 0) {
207d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            job.timeSent = System.currentTimeMillis();
208e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            switch (job.command) {
209e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case CONNECT:
210d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                successful = connectInt(job.cachedDevice, job.profile);
211e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
212e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case DISCONNECT:
213d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                successful = disconnectInt(job.cachedDevice, job.profile);
214e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
21596b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan            case REMOVE_BOND:
21696b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan                BluetoothDevice dev = job.cachedDevice.getDevice();
21796b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan                if (dev != null) {
21896b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan                    successful = dev.removeBond();
21996b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan                }
22096b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan                break;
221e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
222e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
223e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (successful) {
224f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                if (D) {
225f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                    Log.d(TAG, "Command sent successfully:" + job.toString());
226f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                }
227f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            } else if (V) {
228f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                Log.v(TAG, "Framework rejected command immediately:" + job.toString());
229e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
230f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        } else if (D) {
231e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "Job already has a sent time. Skip. " + job.toString());
232e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
233e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
234e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return successful;
235e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
236e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
2376c97742c88303c65e68fe08257a7ccf841f03866Michael Chan    public void onProfileStateChanged(Profile profile, int newProfileState) {
238df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        synchronized (workQueue) {
239df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            if (D) {
240df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                Log.d(TAG, "onProfileStateChanged:" + workQueue.toString());
241f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            }
242097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
243df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager,
244df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    profile).convertState(newProfileState);
245df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
246df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) {
247df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                if (!mProfiles.contains(profile)) {
248df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    mProfiles.add(profile);
2496c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                }
250df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
2516c97742c88303c65e68fe08257a7ccf841f03866Michael Chan
252df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            /* Ignore the transient states e.g. connecting, disconnecting */
253df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED ||
254df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    newState == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
255df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                BluetoothJob job = workQueue.peek();
256df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                if (job == null) {
2576c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                    return;
258df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                } else if (!job.cachedDevice.mDevice.equals(mDevice)) {
259df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    // This can happen in 2 cases: 1) BT device initiated pairing and
260df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    // 2) disconnects of one headset that's triggered by connects of
261df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    // another.
262df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    if (D) {
263df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                        Log.d(TAG, "mDevice:" + mDevice + " != head:" + job.toString());
264df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    }
265df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
266df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    // Check to see if we need to remove the stale items from the queue
267df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    if (!pruneQueue(null)) {
268df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                        // nothing in the queue was modify. Just ignore the notification and return.
269df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                        return;
270df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    }
271df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                } else {
272df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    // Remove the first item and process the next one
273df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    workQueue.poll();
2746c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                }
275e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
276df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                processCommands();
277df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
2786c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        }
279e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
280e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
281e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /*
282e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This method is called in 2 places:
283e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 1) queryCommand() - when someone or something want to connect or
284e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    disconnect
285e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 2) onProfileStateChanged() - when the framework sends an intent
286e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    notification when it finishes processing a command
287e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
288e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void processCommands() {
289f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        if (D) {
290f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            Log.d(TAG, "processCommands:" + workQueue.toString());
291f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        }
292e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Iterator<BluetoothJob> it = workQueue.iterator();
293e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        while (it.hasNext()) {
294e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            BluetoothJob job = it.next();
295e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processCommand(job)) {
296e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // Sent to bt framework. Done for now. Will remove this job
297e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // from queue when we get an event
298e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return;
299e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
300e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                /*
301e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * If the command failed immediately, there will be no event
302e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * callbacks. So delete the job immediately and move on to the
303e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * next one
304e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 */
305e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                it.remove();
306e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
307e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
308e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
309e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
310d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    CachedBluetoothDevice(Context context, BluetoothDevice device) {
311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mLocalManager = LocalBluetoothManager.getInstance(context);
312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mLocalManager == null) {
313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new IllegalStateException(
314d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    "Cannot use CachedBluetoothDevice without Bluetooth hardware");
315afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
316afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
317d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mDevice = device;
318afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
319afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fillData();
320afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
321afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
322afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onClicked() {
323afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
324afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
325afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnected()) {
326afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            askDisconnect();
327afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (bondState == BluetoothDevice.BOND_BONDED) {
328afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            connect();
32916cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        } else if (bondState == BluetoothDevice.BOND_NONE) {
330afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
331afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
332afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
333afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
334afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect() {
335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
336afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect(profile);
337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
338afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
339afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
340afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect(Profile profile) {
341e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile));
342e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
343e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
344d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private boolean disconnectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
345afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
347d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        int status = profileManager.getConnectionStatus(cachedDevice.mDevice);
348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (SettingsBtStatus.isConnectionStatusConnected(status)) {
349f4779354b39353a48d0bec6cc5c71fd73e3f369aNick Pelly            if (profileManager.disconnect(cachedDevice.mDevice)) {
350e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
351e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
352afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
353e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
354afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
355afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
356afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void askDisconnect() {
357afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getForegroundActivity();
358afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (context == null) {
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Cannot ask, since we need an activity context
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect();
361afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return;
362afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
363afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
364afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Resources res = context.getResources();
365afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
366afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String name = getName();
367afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(name)) {
368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            name = res.getString(R.string.bluetooth_device);
369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String message = res.getString(R.string.bluetooth_disconnect_blank, name);
371afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
372afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
373afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            public void onClick(DialogInterface dialog, int which) {
374afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
375afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        };
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
3788762093e0094e09493fa6cbac8e8fbf265f18165Michael Chan        new AlertDialog.Builder(context)
379afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setTitle(getName())
380afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setMessage(message)
381afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setPositiveButton(android.R.string.ok, disconnectListener)
382afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setNegativeButton(android.R.string.cancel, null)
383afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .show();
384afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
385afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
386afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect() {
387afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
388afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
389eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
390eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
391eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        connectWithoutResettingTimer();
392eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
393eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
394b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh    /*package*/ void onBondingDockConnect() {
395b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh        // Don't connect just set the timer.
396b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh        // TODO(): Fix the actual problem
397b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh        mConnectAttempted = SystemClock.elapsedRealtime();
398b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh    }
399b172cd5d232bd650d6efbb7d8b5e4cf9c213952fJaikumar Ganesh
400eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private void connectWithoutResettingTimer() {
4012aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        // Try to initialize the profiles if there were not.
4022aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (mProfiles.size() == 0) {
4032aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (!updateProfiles()) {
4042aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                // If UUIDs are not available yet, connect will be happen
4052aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                // upon arrival of the ACTION_UUID intent.
406eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                if (DEBUG) Log.d(TAG, "No profiles. Maybe we will connect later");
4072aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                return;
4082aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
4092aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
4102aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
411afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
412afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
413afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
414eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        int preferredProfiles = 0;
415afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
416b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            if (isConnectableProfile(profile)) {
417b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
418b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                        .getProfileManager(mLocalManager, profile);
419b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                if (profileManager.isPreferred(mDevice)) {
420b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                    ++preferredProfiles;
4213327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan                    disconnectConnected(profile);
422b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                    queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
423b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                }
424afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
425afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
426eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
427afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
428eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (preferredProfiles == 0) {
429eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            connectAllProfiles();
430afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
431afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
432afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
433eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private void connectAllProfiles() {
434afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
435afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
438afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
439afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
440b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            if (isConnectableProfile(profile)) {
441b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
442b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                        .getProfileManager(mLocalManager, profile);
443b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                profileManager.setPreferred(mDevice, false);
4443327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan                disconnectConnected(profile);
445b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
446b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            }
447afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
448afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
449afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
450afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect(Profile profile) {
451eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
452afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
453afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
4543327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        disconnectConnected(profile);
455e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
456afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
457afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
4583327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan    private void disconnectConnected(Profile profile) {
4593327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        LocalBluetoothProfileManager profileManager =
4603327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan            LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
4613327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        CachedBluetoothDeviceManager cachedDeviceManager = mLocalManager.getCachedDeviceManager();
4623327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        Set<BluetoothDevice> devices = profileManager.getConnectedDevices();
4633327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        if (devices == null) return;
4643327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        for (BluetoothDevice device : devices) {
4653327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan            CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device);
4663327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan            if (cachedDevice != null) {
4673327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan                queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, cachedDevice, profile));
4683327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan            }
4693327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan        }
4703327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan    }
4713327f7887328c55ef2d837a8e2a07cea2070c744Michael Chan
472d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private boolean connectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
473d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (!cachedDevice.ensurePaired()) return false;
474afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
475afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
476afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
477d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        int status = profileManager.getConnectionStatus(cachedDevice.mDevice);
478afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!SettingsBtStatus.isConnectionStatusConnected(status)) {
479f4779354b39353a48d0bec6cc5c71fd73e3f369aNick Pelly            if (profileManager.connect(cachedDevice.mDevice)) {
480e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
481afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
482d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            Log.i(TAG, "Failed to connect " + profile.toString() + " to " + cachedDevice.mName);
483eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        } else {
484eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.i(TAG, "Already connected");
485afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
486e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
487afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
488afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
489afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void showConnectingError() {
490afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!mIsConnectingErrorPossible) return;
491afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = false;
492afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
493d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mLocalManager.showError(mDevice, R.string.bluetooth_error_title,
494afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                R.string.bluetooth_connecting_error_message);
495afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
496afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
497afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean ensurePaired() {
49816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (getBondState() == BluetoothDevice.BOND_NONE) {
499afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
500afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return false;
501afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
502afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
503afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
504afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
505afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
506afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void pair() {
507d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
508afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
509afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Pairing is unreliable while scanning, so cancel discovery
510d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (adapter.isDiscovering()) {
511d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            adapter.cancelDiscovery();
512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
514d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (!mDevice.createBond()) {
515d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mLocalManager.showError(mDevice, R.string.bluetooth_error_title,
516afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    R.string.bluetooth_pairing_error_message);
517afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
519afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unpair() {
52196b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        disconnect();
522e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
52396b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        int state = getBondState();
524afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
52596b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state == BluetoothDevice.BOND_BONDING) {
526d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mDevice.cancelBondProcess();
52796b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        }
52896b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan
52996b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan        if (state != BluetoothDevice.BOND_NONE) {
53096b0c1dc0813f7e8c957ec17dc7751693926c6aeMichael Chan            queueCommand(new BluetoothJob(BluetoothCommand.REMOVE_BOND, this, null));
531afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
532afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
534afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
535afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
536afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
5372aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
538afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
539afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
540afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
541afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
542afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
543afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
544d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public BluetoothDevice getDevice() {
545d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice;
546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
547afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
548afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String getName() {
549afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
550afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
551afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
552d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    public void setName(String name) {
553d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        if (!mName.equals(name)) {
554d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            if (TextUtils.isEmpty(name)) {
555d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                mName = mDevice.getAddress();
556d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            } else {
557d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh                mName = name;
558d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            }
559d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
560d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
561d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
562d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshName() {
564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
569d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mName = mDevice.getName();
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
572d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mName = mDevice.getAddress();
573eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            if (DEBUG) Log.d(TAG, "Default to address. Device has no name (yet) " + mName);
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
575afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
576afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
577afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refresh() {
578afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
579afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
580afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
581afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isVisible() {
582afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
583afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
584afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
585afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
586afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
587afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
588afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
589afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
590afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
591afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
592afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBondState() {
593d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getBondState();
594afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
595afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
596afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
597afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
598afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
599afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
600afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
601afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
602afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
603afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
604afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
605afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
606afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
607afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
608afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isConnected() {
609afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
610afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
611d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    .getConnectionStatus(mDevice);
612afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(status)) {
613afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
614afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
615afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
616afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
617afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
618afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
619afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
620afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isBusy() {
621afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
622afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
623d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    .getConnectionStatus(mDevice);
624afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusBusy(status)) {
625afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
626afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
627afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
628afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
629afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (getBondState() == BluetoothDevice.BOND_BONDING) {
630afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
631afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
632afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
633afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
634afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
635afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
636afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBtClassDrawable() {
6374d004e910732565ddd449c7c525f7d6cbd353392Michael Chan        if (mBtClass != null) {
6384d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            switch (mBtClass.getMajorDeviceClass()) {
6394d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            case BluetoothClass.Device.Major.COMPUTER:
6404d004e910732565ddd449c7c525f7d6cbd353392Michael Chan                return R.drawable.ic_bt_laptop;
641afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
6424d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            case BluetoothClass.Device.Major.PHONE:
6434d004e910732565ddd449c7c525f7d6cbd353392Michael Chan                return R.drawable.ic_bt_cellphone;
6444d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            }
6454d004e910732565ddd449c7c525f7d6cbd353392Michael Chan        } else {
6464d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            Log.w(TAG, "mBtClass is null");
6473e1c1d19201e625888cdd07e06ea9502d7c4dafbMichael Chan        }
648afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
649cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan        if (mProfiles.size() > 0) {
650cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            if (mProfiles.contains(Profile.A2DP)) {
651cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headphones_a2dp;
652cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            } else if (mProfiles.contains(Profile.HEADSET)) {
653cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headset_hfp;
654cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            }
6554d004e910732565ddd449c7c525f7d6cbd353392Michael Chan        } else if (mBtClass != null) {
656cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            if (mBtClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
657cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headphones_a2dp;
658cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan
659cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            }
660cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            if (mBtClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
661cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headset_hfp;
662cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            }
663afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
664cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan        return 0;
665afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
666afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
667afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
668afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
669afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
670afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
671d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mBtClass = mDevice.getBluetoothClass();
6722aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
6732aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6742aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private boolean updateProfiles() {
6752aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        ParcelUuid[] uuids = mDevice.getUuids();
6762aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (uuids == null) return false;
6772aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6782aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        LocalBluetoothProfileManager.updateProfiles(uuids, mProfiles);
6792aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6802aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (DEBUG) {
6812aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            Log.e(TAG, "updating profiles for " + mDevice.getName());
6822aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6832aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            boolean printUuids = true;
6842aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
6852aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
686a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan            if (bluetoothClass != null) {
687a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET) !=
688a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    mProfiles.contains(Profile.HEADSET)) {
689a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    Log.v(TAG, "headset classbits != uuid");
690a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    printUuids = true;
691a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                }
6922aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
693a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP) !=
694a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    mProfiles.contains(Profile.A2DP)) {
695a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    Log.v(TAG, "a2dp classbits != uuid");
696a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    printUuids = true;
697a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                }
6982aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
699a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_OPP) !=
700a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    mProfiles.contains(Profile.OPP)) {
701a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    Log.v(TAG, "opp classbits != uuid");
702a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                    printUuids = true;
703a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                }
7042aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
7052aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
7062aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (printUuids) {
707a87f2fd7d82e5055e0f9ea2c93704ed0b58f27efMichael Chan                if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
7082aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                Log.v(TAG, "UUID:");
7092aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                for (int i = 0; i < uuids.length; i++) {
7102aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                    Log.v(TAG, "  " + uuids[i]);
7112aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                }
7122aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
713b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly        }
7142aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        return true;
715afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
716afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
717afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
718afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
719afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
720afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
721afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshBtClass() {
722afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
723afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
724afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
725afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
7262aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
7272aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * Refreshes the UI when framework alerts us of a UUID change.
7282aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
7292aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    public void onUuidChanged() {
7302aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
7312aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
732eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) {
733eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.e(TAG, "onUuidChanged: Time since last connect"
734eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                    + (SystemClock.elapsedRealtime() - mConnectAttempted));
735eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
7362aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
7372aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        /*
7382aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * If a connect was attempted earlier without any UUID, we will do the
7392aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * connect now.
7402aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         */
7412aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (mProfiles.size() > 0
742eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
7432aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                        .elapsedRealtime()) {
744eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            connectWithoutResettingTimer();
7452aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
7462aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        dispatchAttributesChanged();
7472aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
7482aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
749eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    public void onBondingStateChanged(int bondState) {
750eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (bondState == BluetoothDevice.BOND_NONE) {
751eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            mProfiles.clear();
752eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
753eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        refresh();
754eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
755eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
75616cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly    public void setBtClass(BluetoothClass btClass) {
75716cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (btClass != null && mBtClass != btClass) {
758d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            mBtClass = btClass;
759d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
760d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
761d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
762d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
763afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getSummary() {
764afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // TODO: clean up
765afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int oneOffSummary = getOneOffSummary();
766afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (oneOffSummary != 0) {
767afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return oneOffSummary;
768afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
769afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
770afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
771afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
772afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, profile);
773d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            int connectionStatus = profileManager.getConnectionStatus(mDevice);
774afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
775afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) ||
776afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING ||
777afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) {
778afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
779afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
780afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
781afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
782afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return SettingsBtStatus.getPairingStatusSummary(getBondState());
783afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
784afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
785afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
786afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * We have special summaries when particular profiles are connected. This
787afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * checks for those states and returns an applicable summary.
788afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
789afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return A one-off summary that is applicable for the current state, or 0.
790afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
791afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private int getOneOffSummary() {
792afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false;
793afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
794afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.A2DP)) {
795afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
796afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.A2DP);
797d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isConnecting = profileManager.getConnectionStatus(mDevice) ==
798afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
799d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isA2dpConnected = profileManager.isConnected(mDevice);
800afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
801afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
802afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.HEADSET)) {
803afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
804afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.HEADSET);
805d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isConnecting |= profileManager.getConnectionStatus(mDevice) ==
806afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
807d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isHeadsetConnected = profileManager.isConnected(mDevice);
808afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
809afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
810afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnecting) {
811afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // If any of these important profiles is connecting, prefer that
812afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return SettingsBtStatus.getConnectionStatusSummary(
813afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING);
814afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected && isHeadsetConnected) {
815afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp_headset;
816afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected) {
817afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp;
818afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isHeadsetConnected) {
819afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_headset;
820afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
821afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return 0;
822afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
823afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
824afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
825e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    public List<Profile> getConnectableProfiles() {
826e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        ArrayList<Profile> connectableProfiles = new ArrayList<Profile>();
827e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        for (Profile profile : mProfiles) {
828e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            if (isConnectableProfile(profile)) {
829e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                connectableProfiles.add(profile);
830e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
831e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
832e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return connectableProfiles;
833e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    }
834e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
835e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    private boolean isConnectableProfile(Profile profile) {
836e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return profile.equals(Profile.HEADSET) || profile.equals(Profile.A2DP);
837afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
838afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
839afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu) {
840afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // No context menu if it is busy (none of these items are applicable if busy)
841ca564d6bdaa56fba60f499e092e1024fff5d1abaNick Pelly        if (mLocalManager.getBluetoothState() != BluetoothAdapter.STATE_ON || isBusy()) {
842487843886dc1c822ee568bb0d77e3f78356dfb87Michael Chan            return;
843487843886dc1c822ee568bb0d77e3f78356dfb87Michael Chan        }
844afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
845afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
846afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isConnected = isConnected();
847e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        boolean hasConnectableProfiles = false;
848e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
849e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        for (Profile profile : mProfiles) {
850e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            if (isConnectableProfile(profile)) {
851e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                hasConnectableProfiles = true;
852e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                break;
853e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
854e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
855afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
856afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        menu.setHeaderTitle(getName());
857afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
85816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (bondState == BluetoothDevice.BOND_NONE) { // Not paired and not connected
859fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            menu.add(0, CONTEXT_ITEM_CONNECT, 0, R.string.bluetooth_device_context_pair_connect);
860fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan        } else { // Paired
861fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            if (isConnected) { // Paired and connected
862fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_DISCONNECT, 0,
863fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                        R.string.bluetooth_device_context_disconnect);
864fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_UNPAIR, 0,
865fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                        R.string.bluetooth_device_context_disconnect_unpair);
866fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            } else { // Paired but not connected
867e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                if (hasConnectableProfiles) {
868fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                    menu.add(0, CONTEXT_ITEM_CONNECT, 0, R.string.bluetooth_device_context_connect);
869fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                }
870fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_UNPAIR, 0, R.string.bluetooth_device_context_unpair);
871fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            }
872afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
873afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Show the connection options item
874e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            if (hasConnectableProfiles) {
875fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_CONNECT_ADVANCED, 0,
876fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                        R.string.bluetooth_device_context_connect_advanced);
877fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            }
878afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
879afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
880afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
881afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
882afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Called when a context menu item is clicked.
883afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
884afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @param item The item that was clicked.
885afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
886afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onContextItemSelected(MenuItem item) {
887afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (item.getItemId()) {
888afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_DISCONNECT:
889afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
890afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
891afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
892afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT:
893afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                connect();
894afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
895afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
896afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_UNPAIR:
897afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                unpair();
898afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
899afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
900afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT_ADVANCED:
901afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Intent intent = new Intent();
902afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                // Need an activity context to open this in our task
903afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Context context = mLocalManager.getForegroundActivity();
904afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                if (context == null) {
905afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    // Fallback on application context, and open in a new task
906afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    context = mLocalManager.getContext();
907afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
908afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                }
909afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                intent.setClass(context, ConnectSpecificProfilesActivity.class);
910d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_DEVICE, mDevice);
911afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                context.startActivity(intent);
912afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
913afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
914afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
915afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
916afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void registerCallback(Callback callback) {
917afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
918afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
919afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
920afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
921afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
922afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unregisterCallback(Callback callback) {
923afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
924afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
925afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
926afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
927afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
928afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
929afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
930afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
931afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                callback.onDeviceAttributesChanged(this);
932afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
933afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
934afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
935afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
936afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
937afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
938d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.toString();
939afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
940afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
941afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
942afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
943d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
944afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new ClassCastException();
945afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
946afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
947d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
948afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
949afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
950afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
951afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
952d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getAddress().hashCode();
953afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
954afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
955d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public int compareTo(CachedBluetoothDevice another) {
956afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int comparison;
957afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
958afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
959afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
960afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
961afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
962afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
963afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
964afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
965afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
966afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
967afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
968afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
969afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
970afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
971afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
972afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
973afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
974afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
975afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
976afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return getName().compareTo(another.getName());
977afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
978afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
979afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
980d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        void onDeviceAttributesChanged(CachedBluetoothDevice cachedDevice);
981afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
982afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
983