CachedBluetoothDevice.java revision e6dd1fa1851302710ac7845d25d8ad8a5b6ee438
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 com.android.settings.R;
20afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
21afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
22afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.AlertDialog;
23afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.bluetooth.BluetoothClass;
24e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport android.bluetooth.BluetoothDevice;
25afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Context;
26afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.DialogInterface;
27afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Intent;
28afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.res.Resources;
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
35e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.text.DateFormat;
36afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.ArrayList;
37e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.util.Date;
38e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.util.Iterator;
39e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport java.util.LinkedList;
40afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport java.util.List;
41afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
42afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/**
43afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * LocalBluetoothDevice represents a remote Bluetooth device. It contains
44afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * attributes of the device (such as the address, name, RSSI, etc.) and
45afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * functionality that can be performed on the device (connect, pair, disconnect,
46afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * etc.).
47afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */
48afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectpublic class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> {
49afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final String TAG = "LocalBluetoothDevice";
50afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
51afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_CONNECT = Menu.FIRST + 1;
52afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_DISCONNECT = Menu.FIRST + 2;
53afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3;
54afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4;
55afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
56afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private final String mAddress;
57afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private String mName;
58afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private short mRssi;
59afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private int mBtClass = BluetoothClass.ERROR;
60afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
61afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private List<Profile> mProfiles = new ArrayList<Profile>();
62afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
63afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mVisible;
64afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
65afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private final LocalBluetoothManager mLocalManager;
66afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
67afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private List<Callback> mCallbacks = new ArrayList<Callback>();
68afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
69afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
70afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * When we connect to multiple profiles, we only want to display a single
71afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * error even if they all fail. This tracks that state.
72afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
73afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean mIsConnectingErrorPossible;
74afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
75e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    // Max time to hold the work queue if we don't get or missed a response
76e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    // from the bt framework.
77e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000;
78e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
79e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private enum BluetoothCommand {
80e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        CONNECT, DISCONNECT,
81e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
82e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
83e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    class BluetoothJob {
84e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final BluetoothCommand command; // CONNECT, DISCONNECT
85e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final LocalBluetoothDevice device;
86e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        final Profile profile; // HEADSET, A2DP, etc
87e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        // 0 means this command was not been sent to the bt framework.
88e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        long timeSent;
89e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
90e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        public BluetoothJob(BluetoothCommand command,
91e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                LocalBluetoothDevice device, Profile profile) {
92e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.command = command;
93e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.device = device;
94e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.profile = profile;
95e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            this.timeSent = 0;
96e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
97e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
98e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        @Override
99e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        public String toString() {
100e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            StringBuilder sb = new StringBuilder();
101e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(command.name());
102e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(" Address:").append(device.mAddress);
103e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(" Profile:").append(profile.name());
104e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            sb.append(" TimeSent:");
105e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (timeSent == 0) {
106e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                sb.append("not yet");
107e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
108e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                sb.append(DateFormat.getTimeInstance().format(new Date(timeSent)));
109e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
110e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            return sb.toString();
111e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
112e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
113e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
114e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /**
115e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * We want to serialize connect and disconnect calls. http://b/170538
116e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This are some headsets that may have L2CAP resource limitation. We want
117e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * to limit the bt bandwidth usage.
118e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *
119e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * A queue to keep track of asynchronous calls to the bt framework.  The
120e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * first item, if exist, should be in progress i.e. went to the bt framework
121e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * already, waiting for a notification to come back. The second item and
122e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * beyond have not been sent to the bt framework yet.
123e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
124e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>();
125e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
126e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void queueCommand(BluetoothJob job) {
127e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Log.d(TAG, workQueue.toString());
128e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        synchronized (workQueue) {
129e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            boolean processNow = false;
130e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            long now = System.currentTimeMillis();
131e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
132e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Iterator<BluetoothJob> it = workQueue.iterator();
133e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            while (it.hasNext()) {
134e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                BluetoothJob existingJob = it.next();
135e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
136e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // Remove any pending CONNECTS when we receive a DISCONNECT
137e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                if (job.command == BluetoothCommand.DISCONNECT) {
138e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    if (existingJob.timeSent == 0
139e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                            && existingJob.command == BluetoothCommand.CONNECT
140e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                            && existingJob.device.mAddress.equals(job.device.mAddress)
141e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                            && existingJob.profile == job.profile) {
142e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                        it.remove();
143e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                        continue;
144e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    }
145e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                }
146e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
147e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // Defensive Code: Remove any job that older than a preset time.
148e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // We never got a call back. It is better to have overlapping
149e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // calls than to get stuck.
150e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "Age:" + (now - existingJob.timeSent));
151e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                if (existingJob.timeSent != 0
152e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                        && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) {
153e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString());
154e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    it.remove();
155e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    processNow = true;
156e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    continue;
157e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                }
158e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
159e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
160e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Add job to queue
161e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "Adding: " + job.toString());
162e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            workQueue.add(job);
163e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
164e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // if there's nothing pending from before, send the command to bt
165e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // framework immediately.
166e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (workQueue.size() == 1 || processNow) {
167e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "workQueue.size() == 1 || TimeOut -> process command now");
168e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // If the failed to process, just drop it from the queue.
169e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // There will be no callback to remove this from the queue.
170e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
171e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
172e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
173e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
174e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
175e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean processCommand(BluetoothJob job) {
176e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        boolean successful = false;
177e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (job.timeSent == 0) {
178e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            job.timeSent = System.currentTimeMillis();
179e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            switch (job.command) {
180e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case CONNECT:
181e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                successful = connectInt(job.device, job.profile);
182e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
183e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case DISCONNECT:
184e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                successful = disconnectInt(job.device, job.profile);
185e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
186e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
187e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
188e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (successful) {
189e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "Command sent successfully:" + job.toString());
190e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
191e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "Framework rejected command immediately:" + job.toString());
192e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
193e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
194e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        } else {
195e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "Job already has a sent time. Skip. " + job.toString());
196e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
197e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
198e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return successful;
199e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
200e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
201e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    public void onProfileStateChanged() {
202e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        // Remove the first item and process the next one
203e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        BluetoothJob job = workQueue.poll();
204e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (job == null) {
205e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.w(TAG, "Yikes, onProfileStateChanged called but job queue is empty");
206e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        } else if (job.device.mAddress != mAddress) {
207e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.w(TAG, "Yikes, onProfileStateChanged called but the address differ. this.mAddress="
208e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    + mAddress + " workQueue.head=" + job.toString());
209e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        } else {
210e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "LocalBluetoothDevice.onProfileStateChanged() called. MAC addr matched");
211e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
212e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
213e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        processCommands();
214e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
215e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
216e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /*
217e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This method is called in 2 places:
218e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 1) queryCommand() - when someone or something want to connect or
219e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    disconnect
220e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 2) onProfileStateChanged() - when the framework sends an intent
221e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    notification when it finishes processing a command
222e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
223e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void processCommands() {
224e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Iterator<BluetoothJob> it = workQueue.iterator();
225e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        while (it.hasNext()) {
226e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            BluetoothJob job = it.next();
227e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processCommand(job)) {
228e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // Sent to bt framework. Done for now. Will remove this job
229e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // from queue when we get an event
230e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return;
231e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
232e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                /*
233e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * If the command failed immediately, there will be no event
234e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * callbacks. So delete the job immediately and move on to the
235e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * next one
236e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 */
237e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                it.remove();
238e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
239e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
240e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
241e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
242afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    LocalBluetoothDevice(Context context, String address) {
243afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mLocalManager = LocalBluetoothManager.getInstance(context);
244afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mLocalManager == null) {
245afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new IllegalStateException(
246afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    "Cannot use LocalBluetoothDevice without Bluetooth hardware");
247afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
248afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
249afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mAddress = address;
250afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
251afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fillData();
252afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
253afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
254afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onClicked() {
255afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
256afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
257afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnected()) {
258afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            askDisconnect();
259afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (bondState == BluetoothDevice.BOND_BONDED) {
260afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            connect();
261afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (bondState == BluetoothDevice.BOND_NOT_BONDED) {
262afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
263afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
264afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
265afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
266afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect() {
267afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
268afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect(profile);
269afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
270afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
271afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
272afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect(Profile profile) {
273e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile));
274e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
275e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
276e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean disconnectInt(LocalBluetoothDevice device, Profile profile) {
277afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
278afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
279e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        int status = profileManager.getConnectionStatus(device.mAddress);
280afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (SettingsBtStatus.isConnectionStatusConnected(status)) {
281e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (profileManager.disconnect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) {
282e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
283e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
284afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
285e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
286afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
287afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
288afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void askDisconnect() {
289afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getForegroundActivity();
290afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (context == null) {
291afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Cannot ask, since we need an activity context
292afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect();
293afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return;
294afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
295afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
296afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Resources res = context.getResources();
297afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
298afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String name = getName();
299afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(name)) {
300afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            name = res.getString(R.string.bluetooth_device);
301afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
302afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String message = res.getString(R.string.bluetooth_disconnect_blank, name);
303afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
304afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
305afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            public void onClick(DialogInterface dialog, int which) {
306afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
307afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
308afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        };
309afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
310afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        AlertDialog ad = new AlertDialog.Builder(context)
311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setTitle(getName())
312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setMessage(message)
313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setPositiveButton(android.R.string.ok, disconnectListener)
314afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setNegativeButton(android.R.string.cancel, null)
315afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .show();
316afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
317afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
318afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect() {
319afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
320afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
321afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
322afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
323afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
324afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getContext();
325afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean hasAtLeastOnePreferredProfile = false;
326afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
327afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager =
328afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
329afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (profileManager.isPreferred(mAddress)) {
330afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                hasAtLeastOnePreferredProfile = true;
331e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
332afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
333afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
334afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!hasAtLeastOnePreferredProfile) {
336afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            connectAndPreferAllProfiles();
337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
338afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
339afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
340afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void connectAndPreferAllProfiles() {
341afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
342afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
343afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
344afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
345afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getContext();
347afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager =
349afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
350afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            profileManager.setPreferred(mAddress, true);
351e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
352afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
353afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
354afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
355afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect(Profile profile) {
356afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
357afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
358e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
361e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean connectInt(LocalBluetoothDevice device, Profile profile) {
362e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (!device.ensurePaired()) return false;
363afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
364afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
365afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
366e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        int status = profileManager.getConnectionStatus(device.mAddress);
367afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!SettingsBtStatus.isConnectionStatusConnected(status)) {
368e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (profileManager.connect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) {
369e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
371e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.i(TAG, "Failed to connect " + profile.toString() + " to " + device.mName);
372afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
373e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Log.i(TAG, "Not connected");
374e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
375afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void showConnectingError() {
378afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!mIsConnectingErrorPossible) return;
379afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = false;
380afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
381afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mLocalManager.showError(mAddress, R.string.bluetooth_error_title,
382afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                R.string.bluetooth_connecting_error_message);
383afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
384afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
385afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean ensurePaired() {
386afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (getBondState() == BluetoothDevice.BOND_NOT_BONDED) {
387afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
388afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return false;
389afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
390afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
391afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
392afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
393afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
394afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void pair() {
395afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        BluetoothDevice manager = mLocalManager.getBluetoothManager();
396afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
397afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Pairing is unreliable while scanning, so cancel discovery
398afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (manager.isDiscovering()) {
399afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            manager.cancelDiscovery();
400afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
401afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
402afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!mLocalManager.getBluetoothManager().createBond(mAddress)) {
403afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mLocalManager.showError(mAddress, R.string.bluetooth_error_title,
404afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    R.string.bluetooth_pairing_error_message);
405afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
406afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
407afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
408afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unpair() {
409e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        synchronized (workQueue) {
410e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Remove any pending commands for this device
411e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            boolean processNow = false;
412e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Iterator<BluetoothJob> it = workQueue.iterator();
413e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            while (it.hasNext()) {
414e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                BluetoothJob job = it.next();
415e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                if (job.device.mAddress.equals(this.mAddress)) {
416e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    it.remove();
417e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    if (job.timeSent != 0) {
418e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                        processNow = true;
419e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    }
420e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                }
421e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
422e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processNow) {
423e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
424e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
425e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
426e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
427afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        BluetoothDevice manager = mLocalManager.getBluetoothManager();
428afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
429afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (getBondState()) {
430afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothDevice.BOND_BONDED:
431afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            manager.removeBond(mAddress);
432afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            break;
433afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
434afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothDevice.BOND_BONDING:
435afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            manager.cancelBondProcess(mAddress);
436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            break;
437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
438afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
439afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
440afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
441afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        BluetoothDevice manager = mLocalManager.getBluetoothManager();
442afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
443afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
444afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
445afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
446afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
447afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
448afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
449afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
450afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
451afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String getAddress() {
452afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress;
453afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
454afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
455afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String getName() {
456afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
457afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
458afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
459afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshName() {
460afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
461afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
462afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
463afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
464afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
465afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mName = mLocalManager.getBluetoothManager().getRemoteName(mAddress);
466afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
467afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
468afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mName = mAddress;
469afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
470afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
471afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
472afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refresh() {
473afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
474afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
475afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
476afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isVisible() {
477afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
478afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
479afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
480afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
481afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
482afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
483afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
484afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
485afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
486afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
487afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBondState() {
488afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mLocalManager.getBluetoothManager().getBondState(mAddress);
489afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
490afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
491afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
492afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
493afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
494afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
495afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
496afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
497afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
498afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
499afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
500afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
501afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
502afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
503afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isConnected() {
504afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
505afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
506afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getConnectionStatus(mAddress);
507afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(status)) {
508afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
509afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
510afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
511afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
514afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
515afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isBusy() {
516afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
517afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getConnectionStatus(mAddress);
519afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusBusy(status)) {
520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
521afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
522afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
523afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
524afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (getBondState() == BluetoothDevice.BOND_BONDING) {
525afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
526afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
527afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
528afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
529afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
530afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
531afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBtClassDrawable() {
532afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // First try looking at profiles
534afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.A2DP)) {
535afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_headphones_a2dp;
536afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (mProfiles.contains(Profile.HEADSET)) {
537afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_headset_hfp;
538afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
539afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
540afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on class
541afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (BluetoothClass.Device.Major.getDeviceMajor(mBtClass)) {
542afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothClass.Device.Major.COMPUTER:
543afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_laptop;
544afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
545afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothClass.Device.Major.PHONE:
546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_cellphone;
547afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
548afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        default:
549afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return 0;
550afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
551afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
552afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
553afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
554afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
555afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
556afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
557afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mBtClass = mLocalManager.getBluetoothManager().getRemoteClass(mAddress);
558afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mProfiles.clear();
559afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager.fill(mBtClass, mProfiles);
560afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
561afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
562afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshBtClass() {
567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
569afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getSummary() {
572afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // TODO: clean up
573afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int oneOffSummary = getOneOffSummary();
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (oneOffSummary != 0) {
575afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return oneOffSummary;
576afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
577afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
578afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
579afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
580afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, profile);
581afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int connectionStatus = profileManager.getConnectionStatus(mAddress);
582afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
583afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) ||
584afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING ||
585afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) {
586afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
587afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
588afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
589afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
590afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return SettingsBtStatus.getPairingStatusSummary(getBondState());
591afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
592afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
593afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
594afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * We have special summaries when particular profiles are connected. This
595afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * checks for those states and returns an applicable summary.
596afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
597afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return A one-off summary that is applicable for the current state, or 0.
598afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
599afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private int getOneOffSummary() {
600afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false;
601afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
602afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.A2DP)) {
603afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
604afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.A2DP);
605afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isConnecting = profileManager.getConnectionStatus(mAddress) ==
606afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
607afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isA2dpConnected = profileManager.isConnected(mAddress);
608afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
609afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
610afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.HEADSET)) {
611afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
612afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.HEADSET);
613afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isConnecting |= profileManager.getConnectionStatus(mAddress) ==
614afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
615afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isHeadsetConnected = profileManager.isConnected(mAddress);
616afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
617afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
618afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnecting) {
619afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // If any of these important profiles is connecting, prefer that
620afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return SettingsBtStatus.getConnectionStatusSummary(
621afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING);
622afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected && isHeadsetConnected) {
623afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp_headset;
624afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected) {
625afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp;
626afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isHeadsetConnected) {
627afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_headset;
628afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
629afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return 0;
630afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
631afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
632afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
633afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public List<Profile> getProfiles() {
634afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return new ArrayList<Profile>(mProfiles);
635afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
636afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
637afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu) {
638afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // No context menu if it is busy (none of these items are applicable if busy)
639afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isBusy()) return;
640afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
641afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
642afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isConnected = isConnected();
643afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean hasProfiles = mProfiles.size() > 0;
644afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
645afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        menu.setHeaderTitle(getName());
646afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
647afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnected) {
648afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_DISCONNECT, 0, R.string.bluetooth_device_context_disconnect);
649afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (hasProfiles) {
650afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // For connection action, show either "Connect" or "Pair & connect"
651afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int connectString = (bondState == BluetoothDevice.BOND_NOT_BONDED)
652afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    ? R.string.bluetooth_device_context_pair_connect
653afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    : R.string.bluetooth_device_context_connect;
654afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_CONNECT, 0, connectString);
655afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
656afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
657afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (bondState == BluetoothDevice.BOND_BONDED) {
658afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // For unpair action, show either "Unpair" or "Disconnect & unpair"
659afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int unpairString = isConnected
660afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    ? R.string.bluetooth_device_context_disconnect_unpair
661afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    : R.string.bluetooth_device_context_unpair;
662afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_UNPAIR, 0, unpairString);
663afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
664afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Show the connection options item
665afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_CONNECT_ADVANCED, 0,
666afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    R.string.bluetooth_device_context_connect_advanced);
667afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
668afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
669afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
670afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
671afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Called when a context menu item is clicked.
672afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
673afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @param item The item that was clicked.
674afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
675afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onContextItemSelected(MenuItem item) {
676afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (item.getItemId()) {
677afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_DISCONNECT:
678afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
679afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
680afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
681afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT:
682afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                connect();
683afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
684afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
685afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_UNPAIR:
686afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                mLocalManager.getBluetoothManager().disconnectRemoteDeviceAcl(mAddress);
687afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                unpair();
688afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
689afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
690afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT_ADVANCED:
691afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Intent intent = new Intent();
692afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                // Need an activity context to open this in our task
693afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Context context = mLocalManager.getForegroundActivity();
694afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                if (context == null) {
695afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    // Fallback on application context, and open in a new task
696afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    context = mLocalManager.getContext();
697afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
698afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                }
699afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                intent.setClass(context, ConnectSpecificProfilesActivity.class);
700afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_ADDRESS, mAddress);
701afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                context.startActivity(intent);
702afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
703afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
704afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
705afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
706afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void registerCallback(Callback callback) {
707afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
708afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
709afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
710afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
711afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
712afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unregisterCallback(Callback callback) {
713afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
714afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
715afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
716afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
717afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
718afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
719afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
720afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
721afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                callback.onDeviceAttributesChanged(this);
722afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
723afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
724afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
725afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
726afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
727afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
728afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress;
729afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
730afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
731afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
732afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
733afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if ((o == null) || !(o instanceof LocalBluetoothDevice)) {
734afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new ClassCastException();
735afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
736afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
737afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress.equals(((LocalBluetoothDevice) o).mAddress);
738afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
739afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
740afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
741afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
742afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress.hashCode();
743afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
744afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
745afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int compareTo(LocalBluetoothDevice another) {
746afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int comparison;
747afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
748afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
749afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
750afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
751afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
752afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
753afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
754afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
755afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
756afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
757afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
758afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
759afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
760afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
761afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
762afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
763afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
764afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
765afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
766afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return getName().compareTo(another.getName());
767afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
768afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
769afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
770afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        void onDeviceAttributesChanged(LocalBluetoothDevice device);
771afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
772afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
773