LocalBluetoothProfileManager.java revision 845e740fc63657438b9085376c8e7d60d8334a72
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 com.android.settings.R; 20 21import android.bluetooth.BluetoothA2dp; 22import android.bluetooth.BluetoothDevice; 23import android.bluetooth.BluetoothHeadset; 24import android.bluetooth.BluetoothUuid; 25import android.os.Handler; 26import android.os.ParcelUuid; 27import android.util.Log; 28 29import java.util.HashMap; 30import java.util.HashSet; 31import java.util.List; 32import java.util.Map; 33import java.util.Set; 34 35/** 36 * LocalBluetoothProfileManager is an abstract class defining the basic 37 * functionality related to a profile. 38 */ 39public abstract class LocalBluetoothProfileManager { 40 private static final String TAG = "LocalBluetoothProfileManager"; 41 42 /* package */ static final ParcelUuid[] HEADSET_PROFILE_UUIDS = new ParcelUuid[] { 43 BluetoothUuid.HSP, 44 BluetoothUuid.Handsfree, 45 }; 46 47 /* package */ static final ParcelUuid[] A2DP_PROFILE_UUIDS = new ParcelUuid[] { 48 BluetoothUuid.AudioSink, 49 BluetoothUuid.AdvAudioDist, 50 }; 51 52 /* package */ static final ParcelUuid[] OPP_PROFILE_UUIDS = new ParcelUuid[] { 53 BluetoothUuid.ObexObjectPush 54 }; 55 56 // TODO: close profiles when we're shutting down 57 private static Map<Profile, LocalBluetoothProfileManager> sProfileMap = 58 new HashMap<Profile, LocalBluetoothProfileManager>(); 59 60 protected LocalBluetoothManager mLocalManager; 61 62 public static void init(LocalBluetoothManager localManager) { 63 synchronized (sProfileMap) { 64 if (sProfileMap.size() == 0) { 65 LocalBluetoothProfileManager profileManager; 66 67 profileManager = new A2dpProfileManager(localManager); 68 sProfileMap.put(Profile.A2DP, profileManager); 69 70 profileManager = new HeadsetProfileManager(localManager); 71 sProfileMap.put(Profile.HEADSET, profileManager); 72 73 profileManager = new OppProfileManager(localManager); 74 sProfileMap.put(Profile.OPP, profileManager); 75 } 76 } 77 } 78 79 public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager, 80 Profile profile) { 81 // Note: This code assumes that "localManager" is same as the 82 // LocalBluetoothManager that was used to initialize the sProfileMap. 83 // If that every changes, we can't just keep one copy of sProfileMap. 84 synchronized (sProfileMap) { 85 LocalBluetoothProfileManager profileManager = sProfileMap.get(profile); 86 if (profileManager == null) { 87 Log.e(TAG, "profileManager can't be found for " + profile.toString()); 88 } 89 return profileManager; 90 } 91 } 92 93 /** 94 * Temporary method to fill profiles based on a device's class. 95 * 96 * NOTE: This list happens to define the connection order. We should put this logic in a more 97 * well known place when this method is no longer temporary. 98 * @param uuids of the remote device 99 * @param profiles The list of profiles to fill 100 */ 101 public static void updateProfiles(ParcelUuid[] uuids, List<Profile> profiles) { 102 profiles.clear(); 103 104 if (uuids == null) { 105 return; 106 } 107 108 if (BluetoothUuid.containsAnyUuid(uuids, HEADSET_PROFILE_UUIDS)) { 109 profiles.add(Profile.HEADSET); 110 } 111 112 if (BluetoothUuid.containsAnyUuid(uuids, A2DP_PROFILE_UUIDS)) { 113 profiles.add(Profile.A2DP); 114 } 115 116 if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) { 117 profiles.add(Profile.OPP); 118 } 119 } 120 121 protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) { 122 mLocalManager = localManager; 123 } 124 125 public abstract Set<BluetoothDevice> getConnectedDevices(); 126 127 public abstract boolean connect(BluetoothDevice device); 128 129 public abstract boolean disconnect(BluetoothDevice device); 130 131 public abstract int getConnectionStatus(BluetoothDevice device); 132 133 public abstract int getSummary(BluetoothDevice device); 134 135 public abstract int convertState(int a2dpState); 136 137 public abstract boolean isPreferred(BluetoothDevice device); 138 139 public abstract int getPreferred(BluetoothDevice device); 140 141 public abstract void setPreferred(BluetoothDevice device, boolean preferred); 142 143 public boolean isConnected(BluetoothDevice device) { 144 return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device)); 145 } 146 147 // TODO: int instead of enum 148 public enum Profile { 149 HEADSET(R.string.bluetooth_profile_headset), 150 A2DP(R.string.bluetooth_profile_a2dp), 151 OPP(R.string.bluetooth_profile_opp); 152 153 public final int localizedString; 154 155 private Profile(int localizedString) { 156 this.localizedString = localizedString; 157 } 158 } 159 160 /** 161 * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service. 162 */ 163 private static class A2dpProfileManager extends LocalBluetoothProfileManager { 164 private BluetoothA2dp mService; 165 166 public A2dpProfileManager(LocalBluetoothManager localManager) { 167 super(localManager); 168 mService = new BluetoothA2dp(localManager.getContext()); 169 } 170 171 @Override 172 public Set<BluetoothDevice> getConnectedDevices() { 173 return mService.getNonDisconnectedSinks(); 174 } 175 176 @Override 177 public boolean connect(BluetoothDevice device) { 178 Set<BluetoothDevice> sinks = mService.getNonDisconnectedSinks(); 179 if (sinks != null) { 180 for (BluetoothDevice sink : sinks) { 181 mService.disconnectSink(sink); 182 } 183 } 184 return mService.connectSink(device); 185 } 186 187 @Override 188 public boolean disconnect(BluetoothDevice device) { 189 // Downgrade priority as user is disconnecting the sink. 190 if (mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_ON) { 191 mService.setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); 192 } 193 return mService.disconnectSink(device); 194 } 195 196 @Override 197 public int getConnectionStatus(BluetoothDevice device) { 198 return convertState(mService.getSinkState(device)); 199 } 200 201 @Override 202 public int getSummary(BluetoothDevice device) { 203 int connectionStatus = getConnectionStatus(device); 204 205 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 206 return R.string.bluetooth_a2dp_profile_summary_connected; 207 } else { 208 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 209 } 210 } 211 212 @Override 213 public boolean isPreferred(BluetoothDevice device) { 214 return mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF; 215 } 216 217 @Override 218 public int getPreferred(BluetoothDevice device) { 219 return mService.getSinkPriority(device); 220 } 221 222 @Override 223 public void setPreferred(BluetoothDevice device, boolean preferred) { 224 if (preferred) { 225 if (mService.getSinkPriority(device) < BluetoothA2dp.PRIORITY_ON) { 226 mService.setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); 227 } 228 } else { 229 mService.setSinkPriority(device, BluetoothA2dp.PRIORITY_OFF); 230 } 231 } 232 233 @Override 234 public int convertState(int a2dpState) { 235 switch (a2dpState) { 236 case BluetoothA2dp.STATE_CONNECTED: 237 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 238 case BluetoothA2dp.STATE_CONNECTING: 239 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 240 case BluetoothA2dp.STATE_DISCONNECTED: 241 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 242 case BluetoothA2dp.STATE_DISCONNECTING: 243 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; 244 case BluetoothA2dp.STATE_PLAYING: 245 return SettingsBtStatus.CONNECTION_STATUS_ACTIVE; 246 default: 247 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 248 } 249 } 250 } 251 252 /** 253 * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service. 254 */ 255 private static class HeadsetProfileManager extends LocalBluetoothProfileManager 256 implements BluetoothHeadset.ServiceListener { 257 private BluetoothHeadset mService; 258 private Handler mUiHandler = new Handler(); 259 260 public HeadsetProfileManager(LocalBluetoothManager localManager) { 261 super(localManager); 262 mService = new BluetoothHeadset(localManager.getContext(), this); 263 } 264 265 public void onServiceConnected() { 266 // This could be called on a non-UI thread, funnel to UI thread. 267 mUiHandler.post(new Runnable() { 268 public void run() { 269 /* 270 * We just bound to the service, so refresh the UI of the 271 * headset device. 272 */ 273 BluetoothDevice device = mService.getCurrentHeadset(); 274 if (device == null) return; 275 mLocalManager.getCachedDeviceManager() 276 .onProfileStateChanged(device, Profile.HEADSET, 277 BluetoothHeadset.STATE_CONNECTED); 278 } 279 }); 280 } 281 282 public void onServiceDisconnected() { 283 } 284 285 @Override 286 public Set<BluetoothDevice> getConnectedDevices() { 287 Set<BluetoothDevice> devices = null; 288 BluetoothDevice device = mService.getCurrentHeadset(); 289 if (device != null) { 290 devices = new HashSet<BluetoothDevice>(); 291 devices.add(device); 292 } 293 return devices; 294 } 295 296 @Override 297 public boolean connect(BluetoothDevice device) { 298 // Since connectHeadset fails if already connected to a headset, we 299 // disconnect from any headset first 300 mService.disconnectHeadset(); 301 return mService.connectHeadset(device); 302 } 303 304 @Override 305 public boolean disconnect(BluetoothDevice device) { 306 if (mService.getCurrentHeadset().equals(device)) { 307 // Downgrade prority as user is disconnecting the headset. 308 if (mService.getPriority(device) > BluetoothHeadset.PRIORITY_ON) { 309 mService.setPriority(device, BluetoothHeadset.PRIORITY_ON); 310 } 311 return mService.disconnectHeadset(); 312 } else { 313 return false; 314 } 315 } 316 317 @Override 318 public int getConnectionStatus(BluetoothDevice device) { 319 BluetoothDevice currentDevice = mService.getCurrentHeadset(); 320 return currentDevice != null && currentDevice.equals(device) 321 ? convertState(mService.getState()) 322 : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 323 } 324 325 @Override 326 public int getSummary(BluetoothDevice device) { 327 int connectionStatus = getConnectionStatus(device); 328 329 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 330 return R.string.bluetooth_headset_profile_summary_connected; 331 } else { 332 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 333 } 334 } 335 336 @Override 337 public boolean isPreferred(BluetoothDevice device) { 338 return mService.getPriority(device) > BluetoothHeadset.PRIORITY_OFF; 339 } 340 341 @Override 342 public int getPreferred(BluetoothDevice device) { 343 return mService.getPriority(device); 344 } 345 346 @Override 347 public void setPreferred(BluetoothDevice device, boolean preferred) { 348 if (preferred) { 349 if (mService.getPriority(device) < BluetoothHeadset.PRIORITY_ON) { 350 mService.setPriority(device, BluetoothHeadset.PRIORITY_ON); 351 } 352 } else { 353 mService.setPriority(device, BluetoothHeadset.PRIORITY_OFF); 354 } 355 } 356 357 @Override 358 public int convertState(int headsetState) { 359 switch (headsetState) { 360 case BluetoothHeadset.STATE_CONNECTED: 361 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 362 case BluetoothHeadset.STATE_CONNECTING: 363 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 364 case BluetoothHeadset.STATE_DISCONNECTED: 365 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 366 default: 367 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 368 } 369 } 370 } 371 372 /** 373 * OppProfileManager 374 */ 375 private static class OppProfileManager extends LocalBluetoothProfileManager { 376 377 public OppProfileManager(LocalBluetoothManager localManager) { 378 super(localManager); 379 } 380 381 @Override 382 public Set<BluetoothDevice> getConnectedDevices() { 383 return null; 384 } 385 386 @Override 387 public boolean connect(BluetoothDevice device) { 388 return false; 389 } 390 391 @Override 392 public boolean disconnect(BluetoothDevice device) { 393 return false; 394 } 395 396 @Override 397 public int getConnectionStatus(BluetoothDevice device) { 398 return -1; 399 } 400 401 @Override 402 public int getSummary(BluetoothDevice device) { 403 int connectionStatus = getConnectionStatus(device); 404 405 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 406 return R.string.bluetooth_opp_profile_summary_connected; 407 } else { 408 return R.string.bluetooth_opp_profile_summary_not_connected; 409 } 410 } 411 412 @Override 413 public boolean isPreferred(BluetoothDevice device) { 414 return false; 415 } 416 417 @Override 418 public int getPreferred(BluetoothDevice device) { 419 return -1; 420 } 421 422 @Override 423 public void setPreferred(BluetoothDevice device, boolean preferred) { 424 } 425 426 @Override 427 public int convertState(int oppState) { 428 switch (oppState) { 429 case 0: 430 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 431 case 1: 432 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 433 case 2: 434 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 435 default: 436 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 437 } 438 } 439 } 440} 441