CachedBluetoothDevice.java revision 4d004e910732565ddd449c7c525f7d6cbd353392
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;
44afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
45afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/**
46d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly * CachedBluetoothDevice represents a remote Bluetooth device. It contains
47afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * attributes of the device (such as the address, name, RSSI, etc.) and
48afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * functionality that can be performed on the device (connect, pair, disconnect,
49afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * etc.).
50afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
51d63c0112251ab4e4e977545368dd703d875012a4Nick Pellypublic class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
52d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private static final String TAG = "CachedBluetoothDevice";
53f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan    private static final boolean D = LocalBluetoothManager.D;
54f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan    private static final boolean V = LocalBluetoothManager.V;
5598d76cc9b882466a97fb55913f5a507a642b5018Jaikumar Ganesh    private static final boolean DEBUG = false;
56afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
57afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_CONNECT = Menu.FIRST + 1;
58afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_DISCONNECT = Menu.FIRST + 2;
59afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3;
60afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4;
61afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
62d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private final BluetoothDevice mDevice;
63afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private String mName;
64afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private short mRssi;
6516cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly    private BluetoothClass mBtClass;
66afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
67afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private List<Profile> mProfiles = new ArrayList<Profile>();
68afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
69afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mVisible;
70afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
71afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private final LocalBluetoothManager mLocalManager;
72afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
73afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private List<Callback> mCallbacks = new ArrayList<Callback>();
74afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
75afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
76afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * When we connect to multiple profiles, we only want to display a single
77afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * error even if they all fail. This tracks that state.
78afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
79afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mIsConnectingErrorPossible;
80afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
812aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
82eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * Last time a bt profile auto-connect was attempted.
83eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan     * If an ACTION_UUID intent comes in within
842aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect
852aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * again with the new UUIDs
862aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
87eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private long mConnectAttempted;
882aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
89eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    // See mConnectAttempted
902aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
912aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
92e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    // Max time to hold the work queue if we don't get or missed a response
93e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    // from the bt framework.
94e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000;
95e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
96e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private enum BluetoothCommand {
97e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        CONNECT, DISCONNECT,
98e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
99e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
1008762093e0094e09493fa6cbac8e8fbf265f18165Michael Chan    static class BluetoothJob {
101e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final BluetoothCommand command; // CONNECT, DISCONNECT
102d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        final CachedBluetoothDevice cachedDevice;
103e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final Profile profile; // HEADSET, A2DP, etc
104e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        // 0 means this command was not been sent to the bt framework.
105e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        long timeSent;
106e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
107e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        public BluetoothJob(BluetoothCommand command,
108d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                CachedBluetoothDevice cachedDevice, Profile profile) {
109e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.command = command;
110d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            this.cachedDevice = cachedDevice;
111e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.profile = profile;
112e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.timeSent = 0;
113e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
114d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
115e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        @Override
116e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        public String toString() {
117e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            StringBuilder sb = new StringBuilder();
118e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(command.name());
119d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            sb.append(" Address:").append(cachedDevice.mDevice);
120e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(" Profile:").append(profile.name());
121e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(" TimeSent:");
122e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (timeSent == 0) {
123e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                sb.append("not yet");
124e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
125e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                sb.append(DateFormat.getTimeInstance().format(new Date(timeSent)));
126e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
127e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            return sb.toString();
128e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
129e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
130e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
131e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /**
132e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * We want to serialize connect and disconnect calls. http://b/170538
133e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This are some headsets that may have L2CAP resource limitation. We want
134e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * to limit the bt bandwidth usage.
135e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *
136e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * A queue to keep track of asynchronous calls to the bt framework.  The
137e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * first item, if exist, should be in progress i.e. went to the bt framework
138e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * already, waiting for a notification to come back. The second item and
139e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * beyond have not been sent to the bt framework yet.
140e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
141e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>();
142e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
143e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void queueCommand(BluetoothJob job) {
144f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        if (D) {
145f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            Log.d(TAG, workQueue.toString());
146f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        }
147e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        synchronized (workQueue) {
148097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            boolean processNow = pruneQueue(job);
149e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
150e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Add job to queue
151f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            if (D) {
152f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                Log.d(TAG, "Adding: " + job.toString());
153f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            }
154e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            workQueue.add(job);
155e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
156e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // if there's nothing pending from before, send the command to bt
157e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // framework immediately.
158e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (workQueue.size() == 1 || processNow) {
159e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // If the failed to process, just drop it from the queue.
160e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // There will be no callback to remove this from the queue.
161e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
162e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
163e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
164e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
165d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
166097ed6c671ed733c0967acc32e017020ede4575bMichael Chan    private boolean pruneQueue(BluetoothJob job) {
167097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        boolean removedStaleItems = false;
168097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        long now = System.currentTimeMillis();
169097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        Iterator<BluetoothJob> it = workQueue.iterator();
170097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        while (it.hasNext()) {
171097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            BluetoothJob existingJob = it.next();
172097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
173097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Remove any pending CONNECTS when we receive a DISCONNECT
174097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (job != null && job.command == BluetoothCommand.DISCONNECT) {
175097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                if (existingJob.timeSent == 0
176097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.command == BluetoothCommand.CONNECT
177d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                        && existingJob.cachedDevice.mDevice.equals(job.cachedDevice.mDevice)
178097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.profile == job.profile) {
179f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                    if (D) {
180f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                        Log.d(TAG, "Removed because of a pending disconnect. " + existingJob);
181f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                    }
182097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    it.remove();
183097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    continue;
184097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                }
185097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
186097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
187097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Defensive Code: Remove any job that older than a preset time.
188097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // We never got a call back. It is better to have overlapping
189097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // calls than to get stuck.
190097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (existingJob.timeSent != 0
191097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) {
192097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString());
193097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                it.remove();
194097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                removedStaleItems = true;
195097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                continue;
196097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
197097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        }
198097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        return removedStaleItems;
199097ed6c671ed733c0967acc32e017020ede4575bMichael Chan    }
200e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
201e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean processCommand(BluetoothJob job) {
202e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        boolean successful = false;
203e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (job.timeSent == 0) {
204d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            job.timeSent = System.currentTimeMillis();
205e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            switch (job.command) {
206e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case CONNECT:
207d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                successful = connectInt(job.cachedDevice, job.profile);
208e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
209e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case DISCONNECT:
210d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                successful = disconnectInt(job.cachedDevice, job.profile);
211e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
212e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
213e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
214e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (successful) {
215f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                if (D) {
216f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                    Log.d(TAG, "Command sent successfully:" + job.toString());
217f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                }
218f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            } else if (V) {
219f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan                Log.v(TAG, "Framework rejected command immediately:" + job.toString());
220e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
221f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        } else if (D) {
222e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "Job already has a sent time. Skip. " + job.toString());
223e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
224e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
225e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return successful;
226e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
227e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
2286c97742c88303c65e68fe08257a7ccf841f03866Michael Chan    public void onProfileStateChanged(Profile profile, int newProfileState) {
229f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        if (D) {
230f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            Log.d(TAG, "onProfileStateChanged:" + workQueue.toString());
231f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        }
2326c97742c88303c65e68fe08257a7ccf841f03866Michael Chan
2336c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager,
2346c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                profile).convertState(newProfileState);
2356c97742c88303c65e68fe08257a7ccf841f03866Michael Chan
2366c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) {
2376c97742c88303c65e68fe08257a7ccf841f03866Michael Chan            if (!mProfiles.contains(profile)) {
2386c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                mProfiles.add(profile);
239f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            }
2406c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        }
241097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
2426c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        /* Ignore the transient states e.g. connecting, disconnecting */
2436c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED ||
2446c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                newState == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
2456c97742c88303c65e68fe08257a7ccf841f03866Michael Chan            BluetoothJob job = workQueue.peek();
2466c97742c88303c65e68fe08257a7ccf841f03866Michael Chan            if (job == null) {
247097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                return;
24853121cf110f358c30ea39224bc3ff0f4c1b56e3fJaikumar Ganesh            } else if (!job.cachedDevice.mDevice.equals(mDevice)) {
2496c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                // This can happen in 2 cases: 1) BT device initiated pairing and
2506c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                // 2) disconnects of one headset that's triggered by connects of
2516c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                // another.
2526c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                if (D) {
253d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    Log.d(TAG, "mDevice:" + mDevice + " != head:" + job.toString());
2546c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                }
2556c97742c88303c65e68fe08257a7ccf841f03866Michael Chan
2566c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                // Check to see if we need to remove the stale items from the queue
2576c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                if (!pruneQueue(null)) {
2586c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                    // nothing in the queue was modify. Just ignore the notification and return.
2596c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                    return;
2606c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                }
2616c97742c88303c65e68fe08257a7ccf841f03866Michael Chan            } else {
2626c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                // Remove the first item and process the next one
2636c97742c88303c65e68fe08257a7ccf841f03866Michael Chan                workQueue.poll();
264097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
265e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
2666c97742c88303c65e68fe08257a7ccf841f03866Michael Chan            processCommands();
2676c97742c88303c65e68fe08257a7ccf841f03866Michael Chan        }
268e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
269e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
270e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /*
271e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This method is called in 2 places:
272e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 1) queryCommand() - when someone or something want to connect or
273e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    disconnect
274e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 2) onProfileStateChanged() - when the framework sends an intent
275e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    notification when it finishes processing a command
276e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
277e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void processCommands() {
278f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        if (D) {
279f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan            Log.d(TAG, "processCommands:" + workQueue.toString());
280f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan        }
281e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Iterator<BluetoothJob> it = workQueue.iterator();
282e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        while (it.hasNext()) {
283e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            BluetoothJob job = it.next();
284e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processCommand(job)) {
285e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // Sent to bt framework. Done for now. Will remove this job
286e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // from queue when we get an event
287e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return;
288e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
289e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                /*
290e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * If the command failed immediately, there will be no event
291e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * callbacks. So delete the job immediately and move on to the
292e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * next one
293e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 */
294e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                it.remove();
295e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
296e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
297e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
298e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
299d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    CachedBluetoothDevice(Context context, BluetoothDevice device) {
300afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mLocalManager = LocalBluetoothManager.getInstance(context);
301afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mLocalManager == null) {
302afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new IllegalStateException(
303d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    "Cannot use CachedBluetoothDevice without Bluetooth hardware");
304afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
305afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
306d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mDevice = device;
307afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
308afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fillData();
309afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
310afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onClicked() {
312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
314afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnected()) {
315afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            askDisconnect();
316afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (bondState == BluetoothDevice.BOND_BONDED) {
317afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            connect();
31816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        } else if (bondState == BluetoothDevice.BOND_NONE) {
319afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
320afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
321afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
322afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
323afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect() {
324afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
325afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect(profile);
326afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
327afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
328afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
329afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect(Profile profile) {
330e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile));
331e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
332e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
333d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private boolean disconnectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
334afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
336d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        int status = profileManager.getConnectionStatus(cachedDevice.mDevice);
337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (SettingsBtStatus.isConnectionStatusConnected(status)) {
338f4779354b39353a48d0bec6cc5c71fd73e3f369aNick Pelly            if (profileManager.disconnect(cachedDevice.mDevice)) {
339e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
340e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
341afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
342e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
343afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
344afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
345afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void askDisconnect() {
346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getForegroundActivity();
347afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (context == null) {
348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Cannot ask, since we need an activity context
349afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect();
350afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return;
351afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
352afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
353afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Resources res = context.getResources();
354afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
355afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String name = getName();
356afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(name)) {
357afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            name = res.getString(R.string.bluetooth_device);
358afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String message = res.getString(R.string.bluetooth_disconnect_blank, name);
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
361afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
362afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            public void onClick(DialogInterface dialog, int which) {
363afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
364afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
365afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        };
366afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
3678762093e0094e09493fa6cbac8e8fbf265f18165Michael Chan        new AlertDialog.Builder(context)
368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setTitle(getName())
369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setMessage(message)
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setPositiveButton(android.R.string.ok, disconnectListener)
371afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setNegativeButton(android.R.string.cancel, null)
372afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .show();
373afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
374afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
375afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect() {
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
378eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
379eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
380eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        connectWithoutResettingTimer();
381eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
382eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
383eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private void connectWithoutResettingTimer() {
3842aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        // Try to initialize the profiles if there were not.
3852aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (mProfiles.size() == 0) {
3862aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (!updateProfiles()) {
3872aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                // If UUIDs are not available yet, connect will be happen
3882aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                // upon arrival of the ACTION_UUID intent.
389eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                if (DEBUG) Log.d(TAG, "No profiles. Maybe we will connect later");
3902aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                return;
3912aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
3922aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
3932aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
394afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
395afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
396afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
397eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        int preferredProfiles = 0;
398afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
399b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            if (isConnectableProfile(profile)) {
400b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
401b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                        .getProfileManager(mLocalManager, profile);
402b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                if (profileManager.isPreferred(mDevice)) {
403b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                    ++preferredProfiles;
404b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                    queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
405b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                }
406afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
407afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
408eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
409afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
410eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (preferredProfiles == 0) {
411eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            connectAllProfiles();
412afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
413afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
414afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
415eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    private void connectAllProfiles() {
416afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
417afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
418afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
419afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
420afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
421afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
422b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            if (isConnectableProfile(profile)) {
423b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
424b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                        .getProfileManager(mLocalManager, profile);
425b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                profileManager.setPreferred(mDevice, false);
426b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan                queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
427b547dda338f1b135076c8be59c50a12d843cebf8Michael Chan            }
428afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
429afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
430afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
431afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect(Profile profile) {
432eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        mConnectAttempted = SystemClock.elapsedRealtime();
433afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
434afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
435e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
438d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    private boolean connectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
439d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (!cachedDevice.ensurePaired()) return false;
440afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
441afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
442afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
443d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        int status = profileManager.getConnectionStatus(cachedDevice.mDevice);
444afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!SettingsBtStatus.isConnectionStatusConnected(status)) {
445f4779354b39353a48d0bec6cc5c71fd73e3f369aNick Pelly            if (profileManager.connect(cachedDevice.mDevice)) {
446e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
447afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
448d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            Log.i(TAG, "Failed to connect " + profile.toString() + " to " + cachedDevice.mName);
449eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        } else {
450eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.i(TAG, "Already connected");
451afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
452e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
453afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
454afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
455afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void showConnectingError() {
456afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!mIsConnectingErrorPossible) return;
457afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = false;
458afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
459d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mLocalManager.showError(mDevice, R.string.bluetooth_error_title,
460afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                R.string.bluetooth_connecting_error_message);
461afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
462afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
463afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean ensurePaired() {
46416cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (getBondState() == BluetoothDevice.BOND_NONE) {
465afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
466afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return false;
467afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
468afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
469afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
470afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
471afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
472afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void pair() {
473d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
474afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
475afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Pairing is unreliable while scanning, so cancel discovery
476d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (adapter.isDiscovering()) {
477d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            adapter.cancelDiscovery();
478afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
479afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
480d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if (!mDevice.createBond()) {
481d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mLocalManager.showError(mDevice, R.string.bluetooth_error_title,
482afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    R.string.bluetooth_pairing_error_message);
483afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
484afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
485afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
486afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unpair() {
487e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        synchronized (workQueue) {
488e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Remove any pending commands for this device
489d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            boolean processNow = false;
490e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Iterator<BluetoothJob> it = workQueue.iterator();
491e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            while (it.hasNext()) {
492e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                BluetoothJob job = it.next();
493d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                if (job.cachedDevice.mDevice.equals(this.mDevice)) {
494e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    it.remove();
495e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    if (job.timeSent != 0) {
496e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                        processNow = true;
497e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    }
498e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                }
499e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
500e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processNow) {
501e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
502e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
503e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
504e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
505afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (getBondState()) {
506afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothDevice.BOND_BONDED:
507d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mDevice.removeBond();
508afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            break;
509afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
510afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothDevice.BOND_BONDING:
511d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mDevice.cancelBondProcess();
512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            break;
513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
514afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
515afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
516afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
517afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
5192aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
521afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
522afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
523afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
524afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
525afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
526d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public BluetoothDevice getDevice() {
527d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice;
528afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
529afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
530afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String getName() {
531afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
532afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
534d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    public void setName(String name) {
535d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        if (!mName.equals(name)) {
536d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            if (TextUtils.isEmpty(name)) {
537d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                mName = mDevice.getAddress();
538d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            } else {
539d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh                mName = name;
540d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            }
541d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
542d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
543d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
544d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
545afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshName() {
546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
547afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
548afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
549afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
550afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
551d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mName = mDevice.getName();
552afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
553afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
554d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            mName = mDevice.getAddress();
555eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            if (DEBUG) Log.d(TAG, "Default to address. Device has no name (yet) " + mName);
556afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
557afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
558afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
559afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refresh() {
560afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
561afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
562afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isVisible() {
564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
569afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
572afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
573afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBondState() {
575d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getBondState();
576afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
577afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
578afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
579afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
580afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
581afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
582afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
583afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
584afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
585afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
586afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
587afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
588afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
589afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
590afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isConnected() {
591afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
592afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
593d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    .getConnectionStatus(mDevice);
594afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(status)) {
595afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
596afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
597afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
598afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
599afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
600afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
601afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
602afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isBusy() {
603afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
604afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
605d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                    .getConnectionStatus(mDevice);
606afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusBusy(status)) {
607afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
608afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
609afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
610afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
611afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (getBondState() == BluetoothDevice.BOND_BONDING) {
612afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
613afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
614afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
615afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
616afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
617afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
618afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBtClassDrawable() {
6194d004e910732565ddd449c7c525f7d6cbd353392Michael Chan        if (mBtClass != null) {
6204d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            switch (mBtClass.getMajorDeviceClass()) {
6214d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            case BluetoothClass.Device.Major.COMPUTER:
6224d004e910732565ddd449c7c525f7d6cbd353392Michael Chan                return R.drawable.ic_bt_laptop;
623afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
6244d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            case BluetoothClass.Device.Major.PHONE:
6254d004e910732565ddd449c7c525f7d6cbd353392Michael Chan                return R.drawable.ic_bt_cellphone;
6264d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            }
6274d004e910732565ddd449c7c525f7d6cbd353392Michael Chan        } else {
6284d004e910732565ddd449c7c525f7d6cbd353392Michael Chan            Log.w(TAG, "mBtClass is null");
6293e1c1d19201e625888cdd07e06ea9502d7c4dafbMichael Chan        }
630afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
631cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan        if (mProfiles.size() > 0) {
632cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            if (mProfiles.contains(Profile.A2DP)) {
633cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headphones_a2dp;
634cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            } else if (mProfiles.contains(Profile.HEADSET)) {
635cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headset_hfp;
636cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            }
6374d004e910732565ddd449c7c525f7d6cbd353392Michael Chan        } else if (mBtClass != null) {
638cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            if (mBtClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
639cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headphones_a2dp;
640cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan
641cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            }
642cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            if (mBtClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
643cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan                return R.drawable.ic_bt_headset_hfp;
644cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan            }
645afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
646cb844e2eab4efa03513c0e0a2de7bc18c79dd128Michael Chan        return 0;
647afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
648afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
649afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
650afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
651afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
652afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
653d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        mBtClass = mDevice.getBluetoothClass();
6542aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
6552aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6562aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    private boolean updateProfiles() {
6572aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        ParcelUuid[] uuids = mDevice.getUuids();
6582aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (uuids == null) return false;
6592aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6602aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        LocalBluetoothProfileManager.updateProfiles(uuids, mProfiles);
6612aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6622aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (DEBUG) {
6632aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            Log.e(TAG, "updating profiles for " + mDevice.getName());
6642aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6652aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            boolean printUuids = true;
6662aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
6672aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6682aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET) !=
6692aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                mProfiles.contains(Profile.HEADSET)) {
6702aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                Log.v(TAG, "headset classbits != uuid");
6712aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                printUuids = true;
6722aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
6732aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6742aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP) !=
6752aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                mProfiles.contains(Profile.A2DP)) {
6762aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                Log.v(TAG, "a2dp classbits != uuid");
6772aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                printUuids = true;
6782aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
6792aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6802aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_OPP) !=
6812aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                mProfiles.contains(Profile.OPP)) {
6822aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                Log.v(TAG, "opp classbits != uuid");
6832aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                printUuids = true;
6842aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
6852aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
6862aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            if (printUuids) {
6872aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                Log.v(TAG, "Class: " + bluetoothClass.toString());
6882aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                Log.v(TAG, "UUID:");
6892aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                for (int i = 0; i < uuids.length; i++) {
6902aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                    Log.v(TAG, "  " + uuids[i]);
6912aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                }
6922aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan            }
693b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly        }
6942aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        return true;
695afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
696afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
697afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
698afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
699afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
700afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
701afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshBtClass() {
702afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
703afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
704afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
705afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
7062aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    /**
7072aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     * Refreshes the UI when framework alerts us of a UUID change.
7082aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan     */
7092aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    public void onUuidChanged() {
7102aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        updateProfiles();
7112aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
712eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (DEBUG) {
713eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            Log.e(TAG, "onUuidChanged: Time since last connect"
714eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                    + (SystemClock.elapsedRealtime() - mConnectAttempted));
715eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
7162aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
7172aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        /*
7182aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * If a connect was attempted earlier without any UUID, we will do the
7192aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         * connect now.
7202aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan         */
7212aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        if (mProfiles.size() > 0
722eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan                && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
7232aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan                        .elapsedRealtime()) {
724eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            connectWithoutResettingTimer();
7252aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        }
7262aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan        dispatchAttributesChanged();
7272aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan    }
7282aef1f3c814b1f8aa00aeefff35caf293c738702Michael Chan
729eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    public void onBondingStateChanged(int bondState) {
730eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        if (bondState == BluetoothDevice.BOND_NONE) {
731eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan            mProfiles.clear();
732eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        }
733eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan        refresh();
734eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan    }
735eaf13a2c154a0c7bf7559b45d33fb589b5bcf3d4Michael Chan
73616cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly    public void setBtClass(BluetoothClass btClass) {
73716cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (btClass != null && mBtClass != btClass) {
738d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            mBtClass = btClass;
739d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh            dispatchAttributesChanged();
740d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh        }
741d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh    }
742d97daa064b35ee0c2b73547d46f97fa88d2da4e9Jaikumar Ganesh
743afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getSummary() {
744afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // TODO: clean up
745afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int oneOffSummary = getOneOffSummary();
746afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (oneOffSummary != 0) {
747afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return oneOffSummary;
748afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
749afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
750afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
751afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
752afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, profile);
753d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            int connectionStatus = profileManager.getConnectionStatus(mDevice);
754afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
755afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) ||
756afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING ||
757afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) {
758afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
759afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
760afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
761afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
762afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return SettingsBtStatus.getPairingStatusSummary(getBondState());
763afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
764afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
765afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
766afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * We have special summaries when particular profiles are connected. This
767afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * checks for those states and returns an applicable summary.
768afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
769afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return A one-off summary that is applicable for the current state, or 0.
770afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
771afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private int getOneOffSummary() {
772afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false;
773afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
774afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.A2DP)) {
775afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
776afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.A2DP);
777d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isConnecting = profileManager.getConnectionStatus(mDevice) ==
778afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
779d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isA2dpConnected = profileManager.isConnected(mDevice);
780afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
781afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
782afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.HEADSET)) {
783afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
784afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.HEADSET);
785d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isConnecting |= profileManager.getConnectionStatus(mDevice) ==
786afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
787d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly            isHeadsetConnected = profileManager.isConnected(mDevice);
788afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
789afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
790afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnecting) {
791afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // If any of these important profiles is connecting, prefer that
792afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return SettingsBtStatus.getConnectionStatusSummary(
793afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING);
794afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected && isHeadsetConnected) {
795afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp_headset;
796afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected) {
797afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp;
798afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isHeadsetConnected) {
799afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_headset;
800afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
801afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return 0;
802afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
803afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
804afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
805e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    public List<Profile> getConnectableProfiles() {
806e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        ArrayList<Profile> connectableProfiles = new ArrayList<Profile>();
807e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        for (Profile profile : mProfiles) {
808e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            if (isConnectableProfile(profile)) {
809e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                connectableProfiles.add(profile);
810e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
811e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
812e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return connectableProfiles;
813e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    }
814e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
815e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan    private boolean isConnectableProfile(Profile profile) {
816e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        return profile.equals(Profile.HEADSET) || profile.equals(Profile.A2DP);
817afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
818afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
819afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu) {
820afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // No context menu if it is busy (none of these items are applicable if busy)
821ca564d6bdaa56fba60f499e092e1024fff5d1abaNick Pelly        if (mLocalManager.getBluetoothState() != BluetoothAdapter.STATE_ON || isBusy()) {
822487843886dc1c822ee568bb0d77e3f78356dfb87Michael Chan            return;
823487843886dc1c822ee568bb0d77e3f78356dfb87Michael Chan        }
824afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
825afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
826afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isConnected = isConnected();
827e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        boolean hasConnectableProfiles = false;
828e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan
829e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        for (Profile profile : mProfiles) {
830e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            if (isConnectableProfile(profile)) {
831e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                hasConnectableProfiles = true;
832e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                break;
833e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            }
834e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan        }
835afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
836afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        menu.setHeaderTitle(getName());
837afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
83816cc86315d7a8e1f6a0f3083d0a810a7cb097832Nick Pelly        if (bondState == BluetoothDevice.BOND_NONE) { // Not paired and not connected
839fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            menu.add(0, CONTEXT_ITEM_CONNECT, 0, R.string.bluetooth_device_context_pair_connect);
840fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan        } else { // Paired
841fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            if (isConnected) { // Paired and connected
842fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_DISCONNECT, 0,
843fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                        R.string.bluetooth_device_context_disconnect);
844fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_UNPAIR, 0,
845fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                        R.string.bluetooth_device_context_disconnect_unpair);
846fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            } else { // Paired but not connected
847e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan                if (hasConnectableProfiles) {
848fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                    menu.add(0, CONTEXT_ITEM_CONNECT, 0, R.string.bluetooth_device_context_connect);
849fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                }
850fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_UNPAIR, 0, R.string.bluetooth_device_context_unpair);
851fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            }
852afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
853afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Show the connection options item
854e6531e253bf646324d3a68de0e9cc612c5e1c8acMichael Chan            if (hasConnectableProfiles) {
855fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                menu.add(0, CONTEXT_ITEM_CONNECT_ADVANCED, 0,
856fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan                        R.string.bluetooth_device_context_connect_advanced);
857fdfb90f9e8207702700093888d0de6dea9a198ebMichael Chan            }
858afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
859afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
860afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
861afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
862afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Called when a context menu item is clicked.
863afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
864afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @param item The item that was clicked.
865afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
866afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onContextItemSelected(MenuItem item) {
867afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (item.getItemId()) {
868afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_DISCONNECT:
869afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
870afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
871afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
872afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT:
873afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                connect();
874afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
875afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
876afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_UNPAIR:
877afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                unpair();
878afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
879afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
880afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT_ADVANCED:
881afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Intent intent = new Intent();
882afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                // Need an activity context to open this in our task
883afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Context context = mLocalManager.getForegroundActivity();
884afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                if (context == null) {
885afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    // Fallback on application context, and open in a new task
886afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    context = mLocalManager.getContext();
887afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
888afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                }
889afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                intent.setClass(context, ConnectSpecificProfilesActivity.class);
890d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly                intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_DEVICE, mDevice);
891afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                context.startActivity(intent);
892afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
893afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
894afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
895afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
896afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void registerCallback(Callback callback) {
897afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
898afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
899afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
900afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
901afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
902afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unregisterCallback(Callback callback) {
903afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
904afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
905afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
906afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
907afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
908afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
909afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
910afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
911afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                callback.onDeviceAttributesChanged(this);
912afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
913afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
914afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
915afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
916afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
917afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
918d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.toString();
919afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
920afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
921afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
922afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
923d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
924afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new ClassCastException();
925afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
926afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
927d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
928afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
929afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
930afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
931afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
932d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        return mDevice.getAddress().hashCode();
933afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
934afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
935d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly    public int compareTo(CachedBluetoothDevice another) {
936afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int comparison;
937afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
938afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
939afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
940afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
941afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
942afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
943afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
944afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
945afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
946afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
947afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
948afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
949afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
950afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
951afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
952afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
953afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
954afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
955afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
956afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return getName().compareTo(another.getName());
957afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
958afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
959afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
960d63c0112251ab4e4e977545368dd703d875012a4Nick Pelly        void onDeviceAttributesChanged(CachedBluetoothDevice cachedDevice);
961afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
962afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
963