CachedBluetoothDeviceManager.java revision c065348ebaf1ea9301d7b96633bea4de8c78c2aa
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.bluetooth; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothClass; 21import android.bluetooth.BluetoothDevice; 22import android.util.Log; 23 24import com.android.settings.R; 25import com.android.settings.bluetooth.LocalBluetoothManager.Callback; 26import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; 27 28import java.util.ArrayList; 29import java.util.List; 30import java.util.Set; 31 32/** 33 * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices. 34 */ 35public class CachedBluetoothDeviceManager { 36 private static final String TAG = "CachedBluetoothDeviceManager"; 37 38 final LocalBluetoothManager mLocalManager; 39 final List<Callback> mCallbacks; 40 41 final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>(); 42 43 public CachedBluetoothDeviceManager(LocalBluetoothManager localManager) { 44 mLocalManager = localManager; 45 mCallbacks = localManager.getCallbacks(); 46 readPairedDevices(); 47 } 48 49 private synchronized boolean readPairedDevices() { 50 BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter(); 51 Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices(); 52 if (bondedDevices == null) return false; 53 54 boolean deviceAdded = false; 55 for (BluetoothDevice device : bondedDevices) { 56 CachedBluetoothDevice cachedDevice = findDevice(device); 57 if (cachedDevice == null) { 58 cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device); 59 mCachedDevices.add(cachedDevice); 60 dispatchDeviceAdded(cachedDevice); 61 deviceAdded = true; 62 } 63 } 64 65 return deviceAdded; 66 } 67 68 public synchronized List<CachedBluetoothDevice> getCachedDevicesCopy() { 69 return new ArrayList<CachedBluetoothDevice>(mCachedDevices); 70 } 71 72 void onBluetoothStateChanged(boolean enabled) { 73 if (enabled) { 74 readPairedDevices(); 75 } 76 } 77 78 public synchronized void onDeviceAppeared(BluetoothDevice device, short rssi, 79 BluetoothClass btClass, String name) { 80 boolean deviceAdded = false; 81 82 CachedBluetoothDevice cachedDevice = findDevice(device); 83 if (cachedDevice == null) { 84 cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device); 85 mCachedDevices.add(cachedDevice); 86 deviceAdded = true; 87 } 88 cachedDevice.setRssi(rssi); 89 cachedDevice.setBtClass(btClass); 90 cachedDevice.setName(name); 91 cachedDevice.setVisible(true); 92 93 if (deviceAdded) { 94 dispatchDeviceAdded(cachedDevice); 95 } 96 } 97 98 public synchronized void onDeviceDisappeared(BluetoothDevice device) { 99 CachedBluetoothDevice cachedDevice = findDevice(device); 100 if (cachedDevice == null) return; 101 102 cachedDevice.setVisible(false); 103 checkForDeviceRemoval(cachedDevice); 104 } 105 106 private void checkForDeviceRemoval(CachedBluetoothDevice cachedDevice) { 107 if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE && 108 !cachedDevice.isVisible()) { 109 // If device isn't paired, remove it altogether 110 mCachedDevices.remove(cachedDevice); 111 dispatchDeviceDeleted(cachedDevice); 112 } 113 } 114 115 public synchronized void onDeviceNameUpdated(BluetoothDevice device) { 116 CachedBluetoothDevice cachedDevice = findDevice(device); 117 if (cachedDevice != null) { 118 cachedDevice.refreshName(); 119 } 120 } 121 122 public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) { 123 124 for (int i = mCachedDevices.size() - 1; i >= 0; i--) { 125 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); 126 127 if (cachedDevice.getDevice().equals(device)) { 128 return cachedDevice; 129 } 130 } 131 132 return null; 133 } 134 135 /** 136 * Attempts to get the name of a remote device, otherwise returns the address. 137 * 138 * @param device The remote device. 139 * @return The name, or if unavailable, the address. 140 */ 141 public String getName(BluetoothDevice device) { 142 CachedBluetoothDevice cachedDevice = findDevice(device); 143 if (cachedDevice != null) return cachedDevice.getName(); 144 145 String name = device.getName(); 146 if (name != null) return name; 147 148 return device.getAddress(); 149 } 150 151 private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) { 152 synchronized (mCallbacks) { 153 for (Callback callback : mCallbacks) { 154 callback.onDeviceAdded(cachedDevice); 155 } 156 } 157 158 // TODO: divider between prev paired/connected and scanned 159 } 160 161 private void dispatchDeviceDeleted(CachedBluetoothDevice cachedDevice) { 162 synchronized (mCallbacks) { 163 for (Callback callback : mCallbacks) { 164 callback.onDeviceDeleted(cachedDevice); 165 } 166 } 167 } 168 169 public synchronized void onBondingStateChanged(BluetoothDevice device, int bondState) { 170 CachedBluetoothDevice cachedDevice = findDevice(device); 171 if (cachedDevice == null) { 172 if (!readPairedDevices()) { 173 Log.e(TAG, "Got bonding state changed for " + device + 174 ", but we have no record of that device."); 175 return; 176 } 177 cachedDevice = findDevice(device); 178 if (cachedDevice == null) { 179 Log.e(TAG, "Got bonding state changed for " + device + 180 "but device not added in cache"); 181 return; 182 } 183 } 184 185 cachedDevice.onBondingStateChanged(bondState); 186 187 if (bondState == BluetoothDevice.BOND_BONDED) { 188 // Auto-connect after pairing 189 cachedDevice.connect(); 190 } 191 } 192 193 /** 194 * Called when we have reached the un-bond state. 195 * 196 * @param device The remote device. 197 * @param reason The reason, one of the error reasons from 198 * BluetoothDevice.UNBOND_REASON_* 199 */ 200 public synchronized void showUnbondMessage(BluetoothDevice device, int reason) { 201 int errorMsg; 202 203 switch(reason) { 204 case BluetoothDevice.UNBOND_REASON_AUTH_FAILED: 205 errorMsg = R.string.bluetooth_pairing_pin_error_message; 206 mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); 207 break; 208 case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED: 209 errorMsg = R.string.bluetooth_pairing_rejected_error_message; 210 mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); 211 break; 212 case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN: 213 errorMsg = R.string.bluetooth_pairing_device_down_error_message; 214 mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); 215 break; 216 case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS: 217 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: 218 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: 219 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: 220 errorMsg = R.string.bluetooth_pairing_error_message; 221 mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); 222 break; 223 default: 224 Log.w(TAG, "showUnbondMessage: Not displaying any message for reason:" + reason); 225 break; 226 } 227 } 228 229 public synchronized void onProfileStateChanged(BluetoothDevice device, Profile profile, 230 int newProfileState) { 231 CachedBluetoothDevice cachedDevice = findDevice(device); 232 if (cachedDevice == null) return; 233 234 cachedDevice.onProfileStateChanged(profile, newProfileState); 235 cachedDevice.refresh(); 236 } 237 238 public synchronized void onConnectingError(BluetoothDevice device) { 239 CachedBluetoothDevice cachedDevice = findDevice(device); 240 if (cachedDevice == null) return; 241 242 /* 243 * Go through the device's delegate so we don't spam the user with 244 * errors connecting to different profiles, and instead make sure the 245 * user sees a single error for his single 'connect' action. 246 */ 247 cachedDevice.showConnectingError(); 248 } 249 250 public synchronized void onScanningStateChanged(boolean started) { 251 if (!started) return; 252 253 // If starting a new scan, clear old visibility 254 for (int i = mCachedDevices.size() - 1; i >= 0; i--) { 255 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); 256 cachedDevice.setVisible(false); 257 checkForDeviceRemoval(cachedDevice); 258 } 259 } 260 261 public synchronized void onBtClassChanged(BluetoothDevice device) { 262 CachedBluetoothDevice cachedDevice = findDevice(device); 263 if (cachedDevice != null) { 264 cachedDevice.refreshBtClass(); 265 } 266 } 267 268 public synchronized void onUuidChanged(BluetoothDevice device) { 269 CachedBluetoothDevice cachedDevice = findDevice(device); 270 if (cachedDevice != null) { 271 cachedDevice.onUuidChanged(); 272 } 273 } 274} 275