CachedBluetoothDevice.java revision 097ed6c671ed733c0967acc32e017020ede4575b
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) {
129097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            boolean processNow = pruneQueue(job);
130e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
131e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Add job to queue
132e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "Adding: " + job.toString());
133e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            workQueue.add(job);
134e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
135e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // if there's nothing pending from before, send the command to bt
136e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // framework immediately.
137e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (workQueue.size() == 1 || processNow) {
138e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "workQueue.size() == 1 || TimeOut -> process command now");
139e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // If the failed to process, just drop it from the queue.
140e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // There will be no callback to remove this from the queue.
141e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
142e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
143e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
144e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
145097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
146097ed6c671ed733c0967acc32e017020ede4575bMichael Chan    private boolean pruneQueue(BluetoothJob job) {
147097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        boolean removedStaleItems = false;
148097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        long now = System.currentTimeMillis();
149097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        Iterator<BluetoothJob> it = workQueue.iterator();
150097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        while (it.hasNext()) {
151097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            BluetoothJob existingJob = it.next();
152097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
153097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Remove any pending CONNECTS when we receive a DISCONNECT
154097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (job != null && job.command == BluetoothCommand.DISCONNECT) {
155097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                if (existingJob.timeSent == 0
156097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.command == BluetoothCommand.CONNECT
157097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.device.mAddress.equals(job.device.mAddress)
158097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                        && existingJob.profile == job.profile) {
159097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    Log.d(TAG, "Removed because of a pending disconnect. " + existingJob);
160097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    it.remove();
161097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    continue;
162097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                }
163097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
164097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
165097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Defensive Code: Remove any job that older than a preset time.
166097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // We never got a call back. It is better to have overlapping
167097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // calls than to get stuck.
168097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            Log.d(TAG, "Age:" + (now - existingJob.timeSent));
169097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (existingJob.timeSent != 0
170097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) {
171097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString());
172097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                it.remove();
173097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                removedStaleItems = true;
174097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                continue;
175097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
176097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        }
177097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        return removedStaleItems;
178097ed6c671ed733c0967acc32e017020ede4575bMichael Chan    }
179e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
180e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean processCommand(BluetoothJob job) {
181e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        boolean successful = false;
182e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (job.timeSent == 0) {
183e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            job.timeSent = System.currentTimeMillis();
184e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            switch (job.command) {
185e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case CONNECT:
186e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                successful = connectInt(job.device, job.profile);
187e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
188e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            case DISCONNECT:
189e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                successful = disconnectInt(job.device, job.profile);
190e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                break;
191e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
192e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
193e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (successful) {
194e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "Command sent successfully:" + job.toString());
195e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
196e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                Log.d(TAG, "Framework rejected command immediately:" + job.toString());
197e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
198e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
199e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        } else {
200e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "Job already has a sent time. Skip. " + job.toString());
201e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
202e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
203e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return successful;
204e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
205e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
206e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    public void onProfileStateChanged() {
207097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        Log.d(TAG, "onProfileStateChanged:" + workQueue.toString());
208097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        BluetoothJob job = workQueue.peek();
209e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (job == null) {
210097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            Log.v(TAG, "Yikes, onProfileStateChanged called but job queue is empty. "
211097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    + "(Okay for device initiated actions and BluetoothA2dpService initiated "
212097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                    + "Auto-connections)");
213097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            return;
214e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        } else if (job.device.mAddress != mAddress) {
215097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // This can happen in 2 cases: 1) BT device initiated pairing and
216097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // 2) disconnects of one headset that's triggered by connects of
217097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // another.
218097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            Log.v(TAG, "onProfileStateChanged called. The addresses differ. this.mAddress="
219e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    + mAddress + " workQueue.head=" + job.toString());
220097ed6c671ed733c0967acc32e017020ede4575bMichael Chan
221097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Check to see if we need to remove the stale items from the queue
222097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            if (!pruneQueue(null)) {
223097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                // nothing in the queue was modify. Just ignore the notification and return.
224097ed6c671ed733c0967acc32e017020ede4575bMichael Chan                return;
225097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            }
226e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        } else {
227097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            // Remove the first item and process the next one
228e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.d(TAG, "LocalBluetoothDevice.onProfileStateChanged() called. MAC addr matched");
229097ed6c671ed733c0967acc32e017020ede4575bMichael Chan            workQueue.poll();
230e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
231e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
232e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        processCommands();
233e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
234e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
235e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    /*
236e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * This method is called in 2 places:
237e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 1) queryCommand() - when someone or something want to connect or
238e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    disconnect
239e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     * 2) onProfileStateChanged() - when the framework sends an intent
240e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     *    notification when it finishes processing a command
241e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project     */
242e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private void processCommands() {
243097ed6c671ed733c0967acc32e017020ede4575bMichael Chan        Log.d(TAG, "processCommands:" + workQueue.toString());
244e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Iterator<BluetoothJob> it = workQueue.iterator();
245e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        while (it.hasNext()) {
246e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            BluetoothJob job = it.next();
247e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processCommand(job)) {
248e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // Sent to bt framework. Done for now. Will remove this job
249e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                // from queue when we get an event
250e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return;
251e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            } else {
252e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                /*
253e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * If the command failed immediately, there will be no event
254e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * callbacks. So delete the job immediately and move on to the
255e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 * next one
256e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                 */
257e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                it.remove();
258e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
259e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
260e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
261e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
262afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    LocalBluetoothDevice(Context context, String address) {
263afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mLocalManager = LocalBluetoothManager.getInstance(context);
264afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mLocalManager == null) {
265afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new IllegalStateException(
266afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    "Cannot use LocalBluetoothDevice without Bluetooth hardware");
267afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
268afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
269afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mAddress = address;
270afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
271afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fillData();
272afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
273afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
274afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onClicked() {
275afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
276afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
277afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnected()) {
278afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            askDisconnect();
279afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (bondState == BluetoothDevice.BOND_BONDED) {
280afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            connect();
281afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (bondState == BluetoothDevice.BOND_NOT_BONDED) {
282afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
283afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
284afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
285afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
286afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect() {
287afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
288afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect(profile);
289afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
290afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
291afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
292afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void disconnect(Profile profile) {
293e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile));
294e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    }
295e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
296e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean disconnectInt(LocalBluetoothDevice device, Profile profile) {
297afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
298afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
299e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        int status = profileManager.getConnectionStatus(device.mAddress);
300afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (SettingsBtStatus.isConnectionStatusConnected(status)) {
301e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (profileManager.disconnect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) {
302e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
303e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
304afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
305e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
306afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
307afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
308afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void askDisconnect() {
309afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getForegroundActivity();
310afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (context == null) {
311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Cannot ask, since we need an activity context
312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            disconnect();
313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return;
314afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
315afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
316afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Resources res = context.getResources();
317afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
318afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String name = getName();
319afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(name)) {
320afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            name = res.getString(R.string.bluetooth_device);
321afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
322afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        String message = res.getString(R.string.bluetooth_disconnect_blank, name);
323afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
324afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
325afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            public void onClick(DialogInterface dialog, int which) {
326afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
327afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
328afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        };
329afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
330afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        AlertDialog ad = new AlertDialog.Builder(context)
331afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setTitle(getName())
332afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setMessage(message)
333afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setPositiveButton(android.R.string.ok, disconnectListener)
334afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .setNegativeButton(android.R.string.cancel, null)
335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                .show();
336afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
338afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect() {
339afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
340afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
341afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
342afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
343afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
344afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getContext();
345afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean hasAtLeastOnePreferredProfile = false;
346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
347afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager =
348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
349afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (profileManager.isPreferred(mAddress)) {
350afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                hasAtLeastOnePreferredProfile = 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        if (!hasAtLeastOnePreferredProfile) {
356afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            connectAndPreferAllProfiles();
357afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
358afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void connectAndPreferAllProfiles() {
361afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!ensurePaired()) return;
362afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
363afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
364afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
365afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
366afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        Context context = mLocalManager.getContext();
367afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager =
369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            profileManager.setPreferred(mAddress, true);
371e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
372afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
373afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
374afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
375afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void connect(Profile profile) {
376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Reset the only-show-one-error-dialog tracking variable
377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = true;
378e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
379afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
380afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
381e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project    private boolean connectInt(LocalBluetoothDevice device, Profile profile) {
382e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        if (!device.ensurePaired()) return false;
383afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
384afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager profileManager =
385afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
386e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        int status = profileManager.getConnectionStatus(device.mAddress);
387afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!SettingsBtStatus.isConnectionStatusConnected(status)) {
388e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (profileManager.connect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) {
389e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                return true;
390afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
391e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Log.i(TAG, "Failed to connect " + profile.toString() + " to " + device.mName);
392afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
393e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        Log.i(TAG, "Not connected");
394e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        return false;
395afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
396afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
397afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void showConnectingError() {
398afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!mIsConnectingErrorPossible) return;
399afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mIsConnectingErrorPossible = false;
400afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
401afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mLocalManager.showError(mAddress, R.string.bluetooth_error_title,
402afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                R.string.bluetooth_connecting_error_message);
403afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
404afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
405afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private boolean ensurePaired() {
406afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (getBondState() == BluetoothDevice.BOND_NOT_BONDED) {
407afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            pair();
408afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return false;
409afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
410afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
411afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
412afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
413afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
414afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void pair() {
415afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        BluetoothDevice manager = mLocalManager.getBluetoothManager();
416afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
417afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Pairing is unreliable while scanning, so cancel discovery
418afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (manager.isDiscovering()) {
419afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            manager.cancelDiscovery();
420afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
421afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
422afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (!mLocalManager.getBluetoothManager().createBond(mAddress)) {
423afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mLocalManager.showError(mAddress, R.string.bluetooth_error_title,
424afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    R.string.bluetooth_pairing_error_message);
425afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
426afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
427afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
428afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unpair() {
429e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        synchronized (workQueue) {
430e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            // Remove any pending commands for this device
431e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            boolean processNow = false;
432e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            Iterator<BluetoothJob> it = workQueue.iterator();
433e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            while (it.hasNext()) {
434e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                BluetoothJob job = it.next();
435e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                if (job.device.mAddress.equals(this.mAddress)) {
436e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    it.remove();
437e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    if (job.timeSent != 0) {
438e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                        processNow = true;
439e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                    }
440e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                }
441e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
442e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            if (processNow) {
443e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project                processCommands();
444e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project            }
445e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project        }
446e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project
447afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        BluetoothDevice manager = mLocalManager.getBluetoothManager();
448afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
449afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (getBondState()) {
450afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothDevice.BOND_BONDED:
451afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            manager.removeBond(mAddress);
452afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            break;
453afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
454afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothDevice.BOND_BONDING:
455afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            manager.cancelBondProcess(mAddress);
456afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            break;
457afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
458afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
459afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
460afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fillData() {
461afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        BluetoothDevice manager = mLocalManager.getBluetoothManager();
462afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
463afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
464afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
465afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
466afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mVisible = false;
467afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
468afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
469afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
470afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
471afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String getAddress() {
472afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress;
473afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
474afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
475afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String getName() {
476afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mName;
477afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
478afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
479afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshName() {
480afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchName();
481afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
482afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
483afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
484afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchName() {
485afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mName = mLocalManager.getBluetoothManager().getRemoteName(mAddress);
486afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
487afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (TextUtils.isEmpty(mName)) {
488afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mName = mAddress;
489afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
490afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
491afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
492afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refresh() {
493afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
494afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
495afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
496afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isVisible() {
497afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mVisible;
498afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
499afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
500afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setVisible(boolean visible) {
501afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mVisible != visible) {
502afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mVisible = visible;
503afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
504afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
505afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
506afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
507afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBondState() {
508afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mLocalManager.getBluetoothManager().getBondState(mAddress);
509afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
510afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
511afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    void setRssi(short rssi) {
512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mRssi != rssi) {
513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mRssi = rssi;
514afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            dispatchAttributesChanged();
515afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
516afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
517afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
519afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Checks whether we are connected to this device (any profile counts).
520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
521afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return Whether it is connected.
522afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
523afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isConnected() {
524afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
525afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
526afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getConnectionStatus(mAddress);
527afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(status)) {
528afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
529afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
530afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
531afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
532afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
534afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
535afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean isBusy() {
536afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
537afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
538afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getConnectionStatus(mAddress);
539afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusBusy(status)) {
540afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return true;
541afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
542afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
543afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
544afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (getBondState() == BluetoothDevice.BOND_BONDING) {
545afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return true;
546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
547afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
548afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return false;
549afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
550afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
551afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getBtClassDrawable() {
552afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
553afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // First try looking at profiles
554afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.A2DP)) {
555afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_headphones_a2dp;
556afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (mProfiles.contains(Profile.HEADSET)) {
557afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_headset_hfp;
558afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
559afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
560afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on class
561afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (BluetoothClass.Device.Major.getDeviceMajor(mBtClass)) {
562afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothClass.Device.Major.COMPUTER:
563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_laptop;
564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        case BluetoothClass.Device.Major.PHONE:
566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.drawable.ic_bt_cellphone;
567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        default:
569afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return 0;
570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
572afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
573afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Fetches a new value for the cached BT class.
575afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
576afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void fetchBtClass() {
577afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mBtClass = mLocalManager.getBluetoothManager().getRemoteClass(mAddress);
578afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        mProfiles.clear();
579afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        LocalBluetoothProfileManager.fill(mBtClass, mProfiles);
580afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
581afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
582afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
583afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Refreshes the UI for the BT class, including fetching the latest value
584afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * for the class.
585afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
586afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void refreshBtClass() {
587afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        fetchBtClass();
588afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        dispatchAttributesChanged();
589afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
590afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
591afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int getSummary() {
592afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // TODO: clean up
593afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int oneOffSummary = getOneOffSummary();
594afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (oneOffSummary != 0) {
595afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return oneOffSummary;
596afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
597afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
598afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        for (Profile profile : mProfiles) {
599afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
600afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, profile);
601afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int connectionStatus = profileManager.getConnectionStatus(mAddress);
602afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
603afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) ||
604afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING ||
605afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) {
606afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
607afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
608afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
609afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
610afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return SettingsBtStatus.getPairingStatusSummary(getBondState());
611afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
612afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
613afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
614afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * We have special summaries when particular profiles are connected. This
615afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * checks for those states and returns an applicable summary.
616afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
617afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @return A one-off summary that is applicable for the current state, or 0.
618afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
619afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private int getOneOffSummary() {
620afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false;
621afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
622afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.A2DP)) {
623afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
624afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.A2DP);
625afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isConnecting = profileManager.getConnectionStatus(mAddress) ==
626afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
627afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isA2dpConnected = profileManager.isConnected(mAddress);
628afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
629afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
630afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (mProfiles.contains(Profile.HEADSET)) {
631afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
632afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    .getProfileManager(mLocalManager, Profile.HEADSET);
633afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isConnecting |= profileManager.getConnectionStatus(mAddress) ==
634afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
635afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            isHeadsetConnected = profileManager.isConnected(mAddress);
636afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
637afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
638afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnecting) {
639afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // If any of these important profiles is connecting, prefer that
640afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return SettingsBtStatus.getConnectionStatusSummary(
641afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING);
642afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected && isHeadsetConnected) {
643afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp_headset;
644afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isA2dpConnected) {
645afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_a2dp;
646afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (isHeadsetConnected) {
647afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return R.string.bluetooth_summary_connected_to_headset;
648afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else {
649afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            return 0;
650afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
651afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
652afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
653afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public List<Profile> getProfiles() {
654afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return new ArrayList<Profile>(mProfiles);
655afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
656afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
657afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu) {
658afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // No context menu if it is busy (none of these items are applicable if busy)
659afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isBusy()) return;
660afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
661afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int bondState = getBondState();
662afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean isConnected = isConnected();
663afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        boolean hasProfiles = mProfiles.size() > 0;
664afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
665afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        menu.setHeaderTitle(getName());
666afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
667afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (isConnected) {
668afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_DISCONNECT, 0, R.string.bluetooth_device_context_disconnect);
669afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        } else if (hasProfiles) {
670afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // For connection action, show either "Connect" or "Pair & connect"
671afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int connectString = (bondState == BluetoothDevice.BOND_NOT_BONDED)
672afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    ? R.string.bluetooth_device_context_pair_connect
673afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    : R.string.bluetooth_device_context_connect;
674afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_CONNECT, 0, connectString);
675afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
676afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
677afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (bondState == BluetoothDevice.BOND_BONDED) {
678afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // For unpair action, show either "Unpair" or "Disconnect & unpair"
679afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            int unpairString = isConnected
680afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    ? R.string.bluetooth_device_context_disconnect_unpair
681afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    : R.string.bluetooth_device_context_unpair;
682afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_UNPAIR, 0, unpairString);
683afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
684afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            // Show the connection options item
685afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            menu.add(0, CONTEXT_ITEM_CONNECT_ADVANCED, 0,
686afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    R.string.bluetooth_device_context_connect_advanced);
687afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
688afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
689afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
690afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    /**
691afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * Called when a context menu item is clicked.
692afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     *
693afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     * @param item The item that was clicked.
694afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project     */
695afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void onContextItemSelected(MenuItem item) {
696afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        switch (item.getItemId()) {
697afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_DISCONNECT:
698afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                disconnect();
699afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
700afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
701afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT:
702afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                connect();
703afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
704afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
705afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_UNPAIR:
706afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                mLocalManager.getBluetoothManager().disconnectRemoteDeviceAcl(mAddress);
707afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                unpair();
708afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
709afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
710afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            case CONTEXT_ITEM_CONNECT_ADVANCED:
711afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Intent intent = new Intent();
712afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                // Need an activity context to open this in our task
713afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                Context context = mLocalManager.getForegroundActivity();
714afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                if (context == null) {
715afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    // Fallback on application context, and open in a new task
716afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    context = mLocalManager.getContext();
717afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
718afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                }
719afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                intent.setClass(context, ConnectSpecificProfilesActivity.class);
720afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_ADDRESS, mAddress);
721afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                context.startActivity(intent);
722afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                break;
723afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
724afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
725afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
726afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void registerCallback(Callback callback) {
727afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
728afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.add(callback);
729afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
730afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
731afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
732afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public void unregisterCallback(Callback callback) {
733afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
734afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            mCallbacks.remove(callback);
735afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
736afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
737afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
738afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    private void dispatchAttributesChanged() {
739afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        synchronized (mCallbacks) {
740afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            for (Callback callback : mCallbacks) {
741afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project                callback.onDeviceAttributesChanged(this);
742afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            }
743afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
744afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
745afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
746afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
747afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public String toString() {
748afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress;
749afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
750afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
751afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
752afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public boolean equals(Object o) {
753afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if ((o == null) || !(o instanceof LocalBluetoothDevice)) {
754afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            throw new ClassCastException();
755afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        }
756afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
757afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress.equals(((LocalBluetoothDevice) o).mAddress);
758afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
759afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
760afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    @Override
761afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int hashCode() {
762afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return mAddress.hashCode();
763afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
764afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
765afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public int compareTo(LocalBluetoothDevice another) {
766afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        int comparison;
767afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
768afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Connected above not connected
769afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
770afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
771afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
772afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Paired above not paired
773afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
774afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project            (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
775afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
776afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
777afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Visible above not visible
778afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
779afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
780afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
781afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Stronger signal above weaker signal
782afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        comparison = another.mRssi - mRssi;
783afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        if (comparison != 0) return comparison;
784afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
785afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        // Fallback on name
786afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        return getName().compareTo(another.getName());
787afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
788afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project
789afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    public interface Callback {
790afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project        void onDeviceAttributesChanged(LocalBluetoothDevice device);
791afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project    }
792afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project}
793