CachedBluetoothDevice.java revision 6c97742c88303c65e68fe08257a7ccf841f03866
1afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project/* 2afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 3afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * 4afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * you may not use this file except in compliance with the License. 6afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * You may obtain a copy of the License at 7afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * 8afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * 10afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * See the License for the specific language governing permissions and 14afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * limitations under the License. 15afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 16afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 17afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectpackage com.android.settings.bluetooth; 18afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 19afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.app.AlertDialog; 20afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.bluetooth.BluetoothClass; 21e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Projectimport android.bluetooth.BluetoothDevice; 22afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Context; 23afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.DialogInterface; 24afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.Intent; 25afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.content.res.Resources; 26afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.text.TextUtils; 27afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.util.Log; 28afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.ContextMenu; 29afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.Menu; 30afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Projectimport android.view.MenuItem; 31afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 326c97742c88303c65e68fe08257a7ccf841f03866Michael Chanimport com.android.settings.R; 336c97742c88303c65e68fe08257a7ccf841f03866Michael Chanimport com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; 346c97742c88303c65e68fe08257a7ccf841f03866Michael Chan 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"; 50f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan private static final boolean D = LocalBluetoothManager.D; 51f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan private static final boolean V = LocalBluetoothManager.V; 52afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 53afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private static final int CONTEXT_ITEM_CONNECT = Menu.FIRST + 1; 54afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private static final int CONTEXT_ITEM_DISCONNECT = Menu.FIRST + 2; 55afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3; 56afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4; 57afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 58afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private final String mAddress; 59afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private String mName; 60afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private short mRssi; 61afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private int mBtClass = BluetoothClass.ERROR; 62afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 63afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private List<Profile> mProfiles = new ArrayList<Profile>(); 64afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 65afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private boolean mVisible; 66afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 67afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private final LocalBluetoothManager mLocalManager; 68afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 69afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private List<Callback> mCallbacks = new ArrayList<Callback>(); 70afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 71afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project /** 72afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * When we connect to multiple profiles, we only want to display a single 73afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * error even if they all fail. This tracks that state. 74afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 75afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private boolean mIsConnectingErrorPossible; 76afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 77e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // Max time to hold the work queue if we don't get or missed a response 78e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // from the bt framework. 79e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000; 80e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 81e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private enum BluetoothCommand { 82e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project CONNECT, DISCONNECT, 83e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 84e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 85e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project class BluetoothJob { 86e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project final BluetoothCommand command; // CONNECT, DISCONNECT 87e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project final LocalBluetoothDevice device; 88e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project final Profile profile; // HEADSET, A2DP, etc 89e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // 0 means this command was not been sent to the bt framework. 90e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project long timeSent; 91e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 92e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project public BluetoothJob(BluetoothCommand command, 93e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project LocalBluetoothDevice device, Profile profile) { 94e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project this.command = command; 95e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project this.device = device; 96e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project this.profile = profile; 97e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project this.timeSent = 0; 98e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 99e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 100e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project @Override 101e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project public String toString() { 102e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project StringBuilder sb = new StringBuilder(); 103e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project sb.append(command.name()); 104e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project sb.append(" Address:").append(device.mAddress); 105e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project sb.append(" Profile:").append(profile.name()); 106e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project sb.append(" TimeSent:"); 107e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (timeSent == 0) { 108e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project sb.append("not yet"); 109e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } else { 110e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project sb.append(DateFormat.getTimeInstance().format(new Date(timeSent))); 111e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 112e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return sb.toString(); 113e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 114e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 115e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 116e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project /** 117e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * We want to serialize connect and disconnect calls. http://b/170538 118e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * This are some headsets that may have L2CAP resource limitation. We want 119e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * to limit the bt bandwidth usage. 120e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * 121e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * A queue to keep track of asynchronous calls to the bt framework. The 122e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * first item, if exist, should be in progress i.e. went to the bt framework 123e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * already, waiting for a notification to come back. The second item and 124e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * beyond have not been sent to the bt framework yet. 125e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project */ 126e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>(); 127e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 128e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private void queueCommand(BluetoothJob job) { 129f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan if (D) { 130f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.d(TAG, workQueue.toString()); 131f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 132e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project synchronized (workQueue) { 133097ed6c671ed733c0967acc32e017020ede4575bMichael Chan boolean processNow = pruneQueue(job); 134e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 135e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // Add job to queue 136f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan if (D) { 137f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.d(TAG, "Adding: " + job.toString()); 138f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 139e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project workQueue.add(job); 140e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 141e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // if there's nothing pending from before, send the command to bt 142e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // framework immediately. 143e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (workQueue.size() == 1 || processNow) { 144e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // If the failed to process, just drop it from the queue. 145e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // There will be no callback to remove this from the queue. 146e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project processCommands(); 147e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 148e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 149e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 150097ed6c671ed733c0967acc32e017020ede4575bMichael Chan 151097ed6c671ed733c0967acc32e017020ede4575bMichael Chan private boolean pruneQueue(BluetoothJob job) { 152097ed6c671ed733c0967acc32e017020ede4575bMichael Chan boolean removedStaleItems = false; 153097ed6c671ed733c0967acc32e017020ede4575bMichael Chan long now = System.currentTimeMillis(); 154097ed6c671ed733c0967acc32e017020ede4575bMichael Chan Iterator<BluetoothJob> it = workQueue.iterator(); 155097ed6c671ed733c0967acc32e017020ede4575bMichael Chan while (it.hasNext()) { 156097ed6c671ed733c0967acc32e017020ede4575bMichael Chan BluetoothJob existingJob = it.next(); 157097ed6c671ed733c0967acc32e017020ede4575bMichael Chan 158097ed6c671ed733c0967acc32e017020ede4575bMichael Chan // Remove any pending CONNECTS when we receive a DISCONNECT 159097ed6c671ed733c0967acc32e017020ede4575bMichael Chan if (job != null && job.command == BluetoothCommand.DISCONNECT) { 160097ed6c671ed733c0967acc32e017020ede4575bMichael Chan if (existingJob.timeSent == 0 161097ed6c671ed733c0967acc32e017020ede4575bMichael Chan && existingJob.command == BluetoothCommand.CONNECT 162097ed6c671ed733c0967acc32e017020ede4575bMichael Chan && existingJob.device.mAddress.equals(job.device.mAddress) 163097ed6c671ed733c0967acc32e017020ede4575bMichael Chan && existingJob.profile == job.profile) { 164f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan if (D) { 165f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.d(TAG, "Removed because of a pending disconnect. " + existingJob); 166f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 167097ed6c671ed733c0967acc32e017020ede4575bMichael Chan it.remove(); 168097ed6c671ed733c0967acc32e017020ede4575bMichael Chan continue; 169097ed6c671ed733c0967acc32e017020ede4575bMichael Chan } 170097ed6c671ed733c0967acc32e017020ede4575bMichael Chan } 171097ed6c671ed733c0967acc32e017020ede4575bMichael Chan 172097ed6c671ed733c0967acc32e017020ede4575bMichael Chan // Defensive Code: Remove any job that older than a preset time. 173097ed6c671ed733c0967acc32e017020ede4575bMichael Chan // We never got a call back. It is better to have overlapping 174097ed6c671ed733c0967acc32e017020ede4575bMichael Chan // calls than to get stuck. 175097ed6c671ed733c0967acc32e017020ede4575bMichael Chan if (existingJob.timeSent != 0 176097ed6c671ed733c0967acc32e017020ede4575bMichael Chan && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) { 177097ed6c671ed733c0967acc32e017020ede4575bMichael Chan Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString()); 178097ed6c671ed733c0967acc32e017020ede4575bMichael Chan it.remove(); 179097ed6c671ed733c0967acc32e017020ede4575bMichael Chan removedStaleItems = true; 180097ed6c671ed733c0967acc32e017020ede4575bMichael Chan continue; 181097ed6c671ed733c0967acc32e017020ede4575bMichael Chan } 182097ed6c671ed733c0967acc32e017020ede4575bMichael Chan } 183097ed6c671ed733c0967acc32e017020ede4575bMichael Chan return removedStaleItems; 184097ed6c671ed733c0967acc32e017020ede4575bMichael Chan } 185e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 186e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private boolean processCommand(BluetoothJob job) { 187e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project boolean successful = false; 188e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (job.timeSent == 0) { 189e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project job.timeSent = System.currentTimeMillis(); 190e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project switch (job.command) { 191e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project case CONNECT: 192e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project successful = connectInt(job.device, job.profile); 193e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project break; 194e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project case DISCONNECT: 195e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project successful = disconnectInt(job.device, job.profile); 196e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project break; 197e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 198e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 199e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (successful) { 200f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan if (D) { 201f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.d(TAG, "Command sent successfully:" + job.toString()); 202f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 203f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } else if (V) { 204f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.v(TAG, "Framework rejected command immediately:" + job.toString()); 205e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 206f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } else if (D) { 207e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project Log.d(TAG, "Job already has a sent time. Skip. " + job.toString()); 208e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 209e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 210e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return successful; 211e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 212e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 2136c97742c88303c65e68fe08257a7ccf841f03866Michael Chan public void onProfileStateChanged(Profile profile, int newProfileState) { 214f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan if (D) { 215f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.d(TAG, "onProfileStateChanged:" + workQueue.toString()); 216f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 2176c97742c88303c65e68fe08257a7ccf841f03866Michael Chan 2186c97742c88303c65e68fe08257a7ccf841f03866Michael Chan int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager, 2196c97742c88303c65e68fe08257a7ccf841f03866Michael Chan profile).convertState(newProfileState); 2206c97742c88303c65e68fe08257a7ccf841f03866Michael Chan 2216c97742c88303c65e68fe08257a7ccf841f03866Michael Chan if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) { 2226c97742c88303c65e68fe08257a7ccf841f03866Michael Chan if (!mProfiles.contains(profile)) { 2236c97742c88303c65e68fe08257a7ccf841f03866Michael Chan mProfiles.add(profile); 224f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 2256c97742c88303c65e68fe08257a7ccf841f03866Michael Chan } 226097ed6c671ed733c0967acc32e017020ede4575bMichael Chan 2276c97742c88303c65e68fe08257a7ccf841f03866Michael Chan /* Ignore the transient states e.g. connecting, disconnecting */ 2286c97742c88303c65e68fe08257a7ccf841f03866Michael Chan if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED || 2296c97742c88303c65e68fe08257a7ccf841f03866Michael Chan newState == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) { 2306c97742c88303c65e68fe08257a7ccf841f03866Michael Chan BluetoothJob job = workQueue.peek(); 2316c97742c88303c65e68fe08257a7ccf841f03866Michael Chan if (job == null) { 232097ed6c671ed733c0967acc32e017020ede4575bMichael Chan return; 2336c97742c88303c65e68fe08257a7ccf841f03866Michael Chan } else if (job.device.mAddress != mAddress) { 2346c97742c88303c65e68fe08257a7ccf841f03866Michael Chan // This can happen in 2 cases: 1) BT device initiated pairing and 2356c97742c88303c65e68fe08257a7ccf841f03866Michael Chan // 2) disconnects of one headset that's triggered by connects of 2366c97742c88303c65e68fe08257a7ccf841f03866Michael Chan // another. 2376c97742c88303c65e68fe08257a7ccf841f03866Michael Chan if (D) { 2386c97742c88303c65e68fe08257a7ccf841f03866Michael Chan Log.d(TAG, "mAddresses:" + mAddress + " != head:" + job.toString()); 2396c97742c88303c65e68fe08257a7ccf841f03866Michael Chan } 2406c97742c88303c65e68fe08257a7ccf841f03866Michael Chan 2416c97742c88303c65e68fe08257a7ccf841f03866Michael Chan // Check to see if we need to remove the stale items from the queue 2426c97742c88303c65e68fe08257a7ccf841f03866Michael Chan if (!pruneQueue(null)) { 2436c97742c88303c65e68fe08257a7ccf841f03866Michael Chan // nothing in the queue was modify. Just ignore the notification and return. 2446c97742c88303c65e68fe08257a7ccf841f03866Michael Chan return; 2456c97742c88303c65e68fe08257a7ccf841f03866Michael Chan } 2466c97742c88303c65e68fe08257a7ccf841f03866Michael Chan } else { 2476c97742c88303c65e68fe08257a7ccf841f03866Michael Chan // Remove the first item and process the next one 2486c97742c88303c65e68fe08257a7ccf841f03866Michael Chan workQueue.poll(); 249097ed6c671ed733c0967acc32e017020ede4575bMichael Chan } 250e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 2516c97742c88303c65e68fe08257a7ccf841f03866Michael Chan processCommands(); 2526c97742c88303c65e68fe08257a7ccf841f03866Michael Chan } 253e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 254e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 255e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project /* 256e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * This method is called in 2 places: 257e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * 1) queryCommand() - when someone or something want to connect or 258e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * disconnect 259e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * 2) onProfileStateChanged() - when the framework sends an intent 260e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * notification when it finishes processing a command 261e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project */ 262e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private void processCommands() { 263f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan if (D) { 264f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan Log.d(TAG, "processCommands:" + workQueue.toString()); 265f25063aee3d8eeaf3767b3a4bf43e1895f2deb2dMichael Chan } 266e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project Iterator<BluetoothJob> it = workQueue.iterator(); 267e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project while (it.hasNext()) { 268e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project BluetoothJob job = it.next(); 269e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (processCommand(job)) { 270e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // Sent to bt framework. Done for now. Will remove this job 271e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // from queue when we get an event 272e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return; 273e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } else { 274e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project /* 275e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * If the command failed immediately, there will be no event 276e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * callbacks. So delete the job immediately and move on to the 277e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project * next one 278e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project */ 279e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project it.remove(); 280e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 281e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 282e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 283e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 284afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothDevice(Context context, String address) { 285afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mLocalManager = LocalBluetoothManager.getInstance(context); 286afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (mLocalManager == null) { 287afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project throw new IllegalStateException( 288afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project "Cannot use LocalBluetoothDevice without Bluetooth hardware"); 289afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 290afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 291afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mAddress = address; 292afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 293afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project fillData(); 294afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 295afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 296afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void onClicked() { 297afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int bondState = getBondState(); 298afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 299afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (isConnected()) { 300afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project askDisconnect(); 301afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (bondState == BluetoothDevice.BOND_BONDED) { 302afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project connect(); 303afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (bondState == BluetoothDevice.BOND_NOT_BONDED) { 304afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project pair(); 305afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 306afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 307afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 308afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void disconnect() { 309afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Profile profile : mProfiles) { 310afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project disconnect(profile); 311afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 312afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 313afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 314afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void disconnect(Profile profile) { 315e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile)); 316e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 317e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 318e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private boolean disconnectInt(LocalBluetoothDevice device, Profile profile) { 319afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = 320afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); 321e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project int status = profileManager.getConnectionStatus(device.mAddress); 322afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (SettingsBtStatus.isConnectionStatusConnected(status)) { 323e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (profileManager.disconnect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) { 324e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return true; 325e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 326afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 327e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return false; 328afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 329afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 330afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void askDisconnect() { 331afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project Context context = mLocalManager.getForegroundActivity(); 332afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (context == null) { 333afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Cannot ask, since we need an activity context 334afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project disconnect(); 335afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return; 336afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 337afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 338afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project Resources res = context.getResources(); 339afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 340afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project String name = getName(); 341afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (TextUtils.isEmpty(name)) { 342afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project name = res.getString(R.string.bluetooth_device); 343afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 344afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project String message = res.getString(R.string.bluetooth_disconnect_blank, name); 345afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 346afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() { 347afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void onClick(DialogInterface dialog, int which) { 348afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project disconnect(); 349afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 350afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project }; 351afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 352afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project AlertDialog ad = new AlertDialog.Builder(context) 353afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .setTitle(getName()) 354afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .setMessage(message) 355afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .setPositiveButton(android.R.string.ok, disconnectListener) 356afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .setNegativeButton(android.R.string.cancel, null) 357afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .show(); 358afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 359afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 360afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void connect() { 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 boolean hasAtLeastOnePreferredProfile = false; 368afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Profile profile : mProfiles) { 369afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = 370afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); 371afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (profileManager.isPreferred(mAddress)) { 372afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project hasAtLeastOnePreferredProfile = true; 373e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); 374afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 375afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 376afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 377afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (!hasAtLeastOnePreferredProfile) { 378afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project connectAndPreferAllProfiles(); 379afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 380afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 381afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 382afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private void connectAndPreferAllProfiles() { 383afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (!ensurePaired()) return; 384afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 385afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Reset the only-show-one-error-dialog tracking variable 386afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mIsConnectingErrorPossible = true; 387afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 388afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project Context context = mLocalManager.getContext(); 389afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Profile profile : mProfiles) { 390afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = 391afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); 392afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project profileManager.setPreferred(mAddress, true); 393e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); 394afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 395afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 396afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 397afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void connect(Profile profile) { 398afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Reset the only-show-one-error-dialog tracking variable 399afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mIsConnectingErrorPossible = true; 400e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); 401afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 402afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 403e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project private boolean connectInt(LocalBluetoothDevice device, Profile profile) { 404e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (!device.ensurePaired()) return false; 405afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 406afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = 407afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); 408e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project int status = profileManager.getConnectionStatus(device.mAddress); 409afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (!SettingsBtStatus.isConnectionStatusConnected(status)) { 410e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (profileManager.connect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) { 411e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return true; 412afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 413e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project Log.i(TAG, "Failed to connect " + profile.toString() + " to " + device.mName); 414afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 415e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project Log.i(TAG, "Not connected"); 416e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project return false; 417afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 418afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 419afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void showConnectingError() { 420afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (!mIsConnectingErrorPossible) return; 421afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mIsConnectingErrorPossible = false; 422afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 423afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mLocalManager.showError(mAddress, R.string.bluetooth_error_title, 424afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project R.string.bluetooth_connecting_error_message); 425afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 426afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 427afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private boolean ensurePaired() { 428afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (getBondState() == BluetoothDevice.BOND_NOT_BONDED) { 429afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project pair(); 430afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return false; 431afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else { 432afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return true; 433afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 434afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 435afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 436afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void pair() { 437afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project BluetoothDevice manager = mLocalManager.getBluetoothManager(); 438afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 439afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Pairing is unreliable while scanning, so cancel discovery 440afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (manager.isDiscovering()) { 441afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project manager.cancelDiscovery(); 442afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 443afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 444afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (!mLocalManager.getBluetoothManager().createBond(mAddress)) { 445afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mLocalManager.showError(mAddress, R.string.bluetooth_error_title, 446afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project R.string.bluetooth_pairing_error_message); 447afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 448afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 449afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 450afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void unpair() { 451e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project synchronized (workQueue) { 452e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project // Remove any pending commands for this device 453e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project boolean processNow = false; 454e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project Iterator<BluetoothJob> it = workQueue.iterator(); 455e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project while (it.hasNext()) { 456e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project BluetoothJob job = it.next(); 457e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (job.device.mAddress.equals(this.mAddress)) { 458e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project it.remove(); 459e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (job.timeSent != 0) { 460e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project processNow = true; 461e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 462e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 463e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 464e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project if (processNow) { 465e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project processCommands(); 466e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 467e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project } 468e6dd1fa1851302710ac7845d25d8ad8a5b6ee438The Android Open Source Project 469afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project BluetoothDevice manager = mLocalManager.getBluetoothManager(); 470afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 471afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project switch (getBondState()) { 472afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case BluetoothDevice.BOND_BONDED: 473afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project manager.removeBond(mAddress); 474afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project break; 475afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 476afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case BluetoothDevice.BOND_BONDING: 477afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project manager.cancelBondProcess(mAddress); 478afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project break; 479afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 480afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 481afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 482afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private void fillData() { 483afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project BluetoothDevice manager = mLocalManager.getBluetoothManager(); 484afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 485afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project fetchName(); 486afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project fetchBtClass(); 487afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 488afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mVisible = false; 489afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 490afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project dispatchAttributesChanged(); 491afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 492afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 493afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public String getAddress() { 494afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mAddress; 495afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 496afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 497afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public String getName() { 498afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mName; 499afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 500afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 501afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void refreshName() { 502afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project fetchName(); 503afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project dispatchAttributesChanged(); 504afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 505afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 506afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private void fetchName() { 507afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mName = mLocalManager.getBluetoothManager().getRemoteName(mAddress); 508afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 509afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (TextUtils.isEmpty(mName)) { 510afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mName = mAddress; 511afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 512afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 513afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 514afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void refresh() { 515afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project dispatchAttributesChanged(); 516afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 517afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 518afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public boolean isVisible() { 519afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mVisible; 520afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 521afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 522afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project void setVisible(boolean visible) { 523afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (mVisible != visible) { 524afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mVisible = visible; 525afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project dispatchAttributesChanged(); 526afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 527afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 528afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 529afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public int getBondState() { 530afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mLocalManager.getBluetoothManager().getBondState(mAddress); 531afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 532afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 533afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project void setRssi(short rssi) { 534afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (mRssi != rssi) { 535afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mRssi = rssi; 536afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project dispatchAttributesChanged(); 537afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 538afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 539afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 540afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project /** 541afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Checks whether we are connected to this device (any profile counts). 542afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * 543afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * @return Whether it is connected. 544afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 545afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public boolean isConnected() { 546afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Profile profile : mProfiles) { 547afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile) 548afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .getConnectionStatus(mAddress); 549afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (SettingsBtStatus.isConnectionStatusConnected(status)) { 550afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return true; 551afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 552afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 553afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 554afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return false; 555afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 556afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 557afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public boolean isBusy() { 558afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Profile profile : mProfiles) { 559afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile) 560afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .getConnectionStatus(mAddress); 561afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (SettingsBtStatus.isConnectionStatusBusy(status)) { 562afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return true; 563afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 564afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 565afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 566afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (getBondState() == BluetoothDevice.BOND_BONDING) { 567afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return true; 568afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 569afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 570afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return false; 571afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 572afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 573afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public int getBtClassDrawable() { 574afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 575afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // First try looking at profiles 576afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (mProfiles.contains(Profile.A2DP)) { 577afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.drawable.ic_bt_headphones_a2dp; 578afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (mProfiles.contains(Profile.HEADSET)) { 579afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.drawable.ic_bt_headset_hfp; 580afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 581afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 582afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Fallback on class 583afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project switch (BluetoothClass.Device.Major.getDeviceMajor(mBtClass)) { 584afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case BluetoothClass.Device.Major.COMPUTER: 585afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.drawable.ic_bt_laptop; 586afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 587afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case BluetoothClass.Device.Major.PHONE: 588afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.drawable.ic_bt_cellphone; 589afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 590afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project default: 591afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return 0; 592afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 593afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 594afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 595afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project /** 596afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Fetches a new value for the cached BT class. 597afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 598afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private void fetchBtClass() { 599afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mBtClass = mLocalManager.getBluetoothManager().getRemoteClass(mAddress); 600b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly if (mBtClass != BluetoothClass.ERROR) { 601b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly LocalBluetoothProfileManager.fill(mBtClass, mProfiles); 602b20dd917e2d29045225985baa980a2a8e22e10fcNick Pelly } 603afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 604afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 605afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project /** 606afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Refreshes the UI for the BT class, including fetching the latest value 607afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * for the class. 608afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 609afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void refreshBtClass() { 610afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project fetchBtClass(); 611afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project dispatchAttributesChanged(); 612afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 613afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 614afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public int getSummary() { 615afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // TODO: clean up 616afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int oneOffSummary = getOneOffSummary(); 617afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (oneOffSummary != 0) { 618afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return oneOffSummary; 619afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 620afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 621afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Profile profile : mProfiles) { 622afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager 623afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .getProfileManager(mLocalManager, profile); 624afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int connectionStatus = profileManager.getConnectionStatus(mAddress); 625afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 626afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) || 627afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING || 628afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) { 629afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 630afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 631afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 632afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 633afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return SettingsBtStatus.getPairingStatusSummary(getBondState()); 634afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 635afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 636afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project /** 637afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * We have special summaries when particular profiles are connected. This 638afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * checks for those states and returns an applicable summary. 639afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * 640afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * @return A one-off summary that is applicable for the current state, or 0. 641afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 642afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private int getOneOffSummary() { 643afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false; 644afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 645afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (mProfiles.contains(Profile.A2DP)) { 646afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager 647afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .getProfileManager(mLocalManager, Profile.A2DP); 648afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project isConnecting = profileManager.getConnectionStatus(mAddress) == 649afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 650afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project isA2dpConnected = profileManager.isConnected(mAddress); 651afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 652afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 653afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (mProfiles.contains(Profile.HEADSET)) { 654afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager 655afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project .getProfileManager(mLocalManager, Profile.HEADSET); 656afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project isConnecting |= profileManager.getConnectionStatus(mAddress) == 657afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 658afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project isHeadsetConnected = profileManager.isConnected(mAddress); 659afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 660afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 661afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (isConnecting) { 662afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // If any of these important profiles is connecting, prefer that 663afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return SettingsBtStatus.getConnectionStatusSummary( 664afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project SettingsBtStatus.CONNECTION_STATUS_CONNECTING); 665afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (isA2dpConnected && isHeadsetConnected) { 666afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.string.bluetooth_summary_connected_to_a2dp_headset; 667afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (isA2dpConnected) { 668afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.string.bluetooth_summary_connected_to_a2dp; 669afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (isHeadsetConnected) { 670afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return R.string.bluetooth_summary_connected_to_headset; 671afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else { 672afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return 0; 673afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 674afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 675afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 676afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public List<Profile> getProfiles() { 677afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return new ArrayList<Profile>(mProfiles); 678afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 679afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 680afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void onCreateContextMenu(ContextMenu menu) { 681afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // No context menu if it is busy (none of these items are applicable if busy) 682afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (isBusy()) return; 683afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 684afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int bondState = getBondState(); 685afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project boolean isConnected = isConnected(); 686afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project boolean hasProfiles = mProfiles.size() > 0; 687afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 688afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project menu.setHeaderTitle(getName()); 689afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 690afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (isConnected) { 691afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project menu.add(0, CONTEXT_ITEM_DISCONNECT, 0, R.string.bluetooth_device_context_disconnect); 692afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } else if (hasProfiles) { 693afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // For connection action, show either "Connect" or "Pair & connect" 694afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int connectString = (bondState == BluetoothDevice.BOND_NOT_BONDED) 695afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project ? R.string.bluetooth_device_context_pair_connect 696afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project : R.string.bluetooth_device_context_connect; 697afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project menu.add(0, CONTEXT_ITEM_CONNECT, 0, connectString); 698afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 699afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 700afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (bondState == BluetoothDevice.BOND_BONDED) { 701afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // For unpair action, show either "Unpair" or "Disconnect & unpair" 702afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int unpairString = isConnected 703afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project ? R.string.bluetooth_device_context_disconnect_unpair 704afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project : R.string.bluetooth_device_context_unpair; 705afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project menu.add(0, CONTEXT_ITEM_UNPAIR, 0, unpairString); 706afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 707afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Show the connection options item 708afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project menu.add(0, CONTEXT_ITEM_CONNECT_ADVANCED, 0, 709afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project R.string.bluetooth_device_context_connect_advanced); 710afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 711afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 712afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 713afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project /** 714afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * Called when a context menu item is clicked. 715afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * 716afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project * @param item The item that was clicked. 717afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project */ 718afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void onContextItemSelected(MenuItem item) { 719afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project switch (item.getItemId()) { 720afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case CONTEXT_ITEM_DISCONNECT: 721afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project disconnect(); 722afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project break; 723afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 724afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case CONTEXT_ITEM_CONNECT: 725afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project connect(); 726afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project break; 727afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 728afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case CONTEXT_ITEM_UNPAIR: 729afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mLocalManager.getBluetoothManager().disconnectRemoteDeviceAcl(mAddress); 730afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project unpair(); 731afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project break; 732afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 733afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project case CONTEXT_ITEM_CONNECT_ADVANCED: 734afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project Intent intent = new Intent(); 735afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Need an activity context to open this in our task 736afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project Context context = mLocalManager.getForegroundActivity(); 737afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (context == null) { 738afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Fallback on application context, and open in a new task 739afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project context = mLocalManager.getContext(); 740afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 741afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 742afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project intent.setClass(context, ConnectSpecificProfilesActivity.class); 743afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_ADDRESS, mAddress); 744afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project context.startActivity(intent); 745afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project break; 746afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 747afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 748afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 749afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void registerCallback(Callback callback) { 750afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project synchronized (mCallbacks) { 751afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mCallbacks.add(callback); 752afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 753afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 754afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 755afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public void unregisterCallback(Callback callback) { 756afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project synchronized (mCallbacks) { 757afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project mCallbacks.remove(callback); 758afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 759afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 760afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 761afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project private void dispatchAttributesChanged() { 762afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project synchronized (mCallbacks) { 763afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project for (Callback callback : mCallbacks) { 764afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project callback.onDeviceAttributesChanged(this); 765afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 766afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 767afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 768afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 769afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project @Override 770afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public String toString() { 771afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mAddress; 772afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 773afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 774afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project @Override 775afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public boolean equals(Object o) { 776afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if ((o == null) || !(o instanceof LocalBluetoothDevice)) { 777afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project throw new ClassCastException(); 778afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 779afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 780afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mAddress.equals(((LocalBluetoothDevice) o).mAddress); 781afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 782afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 783afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project @Override 784afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public int hashCode() { 785afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return mAddress.hashCode(); 786afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 787afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 788afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public int compareTo(LocalBluetoothDevice another) { 789afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project int comparison; 790afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 791afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Connected above not connected 792afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0); 793afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (comparison != 0) return comparison; 794afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 795afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Paired above not paired 796afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) - 797afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project (getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0); 798afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (comparison != 0) return comparison; 799afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 800afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Visible above not visible 801afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0); 802afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (comparison != 0) return comparison; 803afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 804afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Stronger signal above weaker signal 805afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project comparison = another.mRssi - mRssi; 806afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project if (comparison != 0) return comparison; 807afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 808afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project // Fallback on name 809afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project return getName().compareTo(another.getName()); 810afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 811afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project 812afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project public interface Callback { 813afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project void onDeviceAttributesChanged(LocalBluetoothDevice device); 814afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project } 815afc4ab2ffbb8327ddce9907961295a32cbf49d0fThe Android Open Source Project} 816