LocalBluetoothProfileManager.java revision 0bd445b974292dc3910b6bb85dcee7e7c378968f
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 void setPreferred(BluetoothDevice device, boolean preferred); 140 141 public boolean isConnected(BluetoothDevice device) { 142 return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device)); 143 } 144 145 // TODO: int instead of enum 146 public enum Profile { 147 HEADSET(R.string.bluetooth_profile_headset), 148 A2DP(R.string.bluetooth_profile_a2dp), 149 OPP(R.string.bluetooth_profile_opp); 150 151 public final int localizedString; 152 153 private Profile(int localizedString) { 154 this.localizedString = localizedString; 155 } 156 } 157 158 /** 159 * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service. 160 */ 161 private static class A2dpProfileManager extends LocalBluetoothProfileManager { 162 private BluetoothA2dp mService; 163 164 public A2dpProfileManager(LocalBluetoothManager localManager) { 165 super(localManager); 166 mService = new BluetoothA2dp(localManager.getContext()); 167 } 168 169 @Override 170 public Set<BluetoothDevice> getConnectedDevices() { 171 return mService.getConnectedSinks(); 172 } 173 174 @Override 175 public boolean connect(BluetoothDevice device) { 176 Set<BluetoothDevice> sinks = mService.getConnectedSinks(); 177 if (sinks != null) { 178 for (BluetoothDevice sink : sinks) { 179 mService.disconnectSink(sink); 180 } 181 } 182 return mService.connectSink(device); 183 } 184 185 @Override 186 public boolean disconnect(BluetoothDevice device) { 187 // Downgrade priority as user is disconnecting the sink. 188 if (mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_ON) { 189 mService.setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); 190 } 191 return mService.disconnectSink(device); 192 } 193 194 @Override 195 public int getConnectionStatus(BluetoothDevice device) { 196 return convertState(mService.getSinkState(device)); 197 } 198 199 @Override 200 public int getSummary(BluetoothDevice device) { 201 int connectionStatus = getConnectionStatus(device); 202 203 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 204 return R.string.bluetooth_a2dp_profile_summary_connected; 205 } else { 206 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 207 } 208 } 209 210 @Override 211 public boolean isPreferred(BluetoothDevice device) { 212 return mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF; 213 } 214 215 @Override 216 public void setPreferred(BluetoothDevice device, boolean preferred) { 217 mService.setSinkPriority(device, 218 preferred ? BluetoothA2dp.PRIORITY_AUTO_CONNECT : BluetoothA2dp.PRIORITY_OFF); 219 } 220 221 @Override 222 public int convertState(int a2dpState) { 223 switch (a2dpState) { 224 case BluetoothA2dp.STATE_CONNECTED: 225 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 226 case BluetoothA2dp.STATE_CONNECTING: 227 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 228 case BluetoothA2dp.STATE_DISCONNECTED: 229 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 230 case BluetoothA2dp.STATE_DISCONNECTING: 231 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; 232 case BluetoothA2dp.STATE_PLAYING: 233 return SettingsBtStatus.CONNECTION_STATUS_ACTIVE; 234 default: 235 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 236 } 237 } 238 } 239 240 /** 241 * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service. 242 */ 243 private static class HeadsetProfileManager extends LocalBluetoothProfileManager 244 implements BluetoothHeadset.ServiceListener { 245 private BluetoothHeadset mService; 246 private Handler mUiHandler = new Handler(); 247 248 public HeadsetProfileManager(LocalBluetoothManager localManager) { 249 super(localManager); 250 mService = new BluetoothHeadset(localManager.getContext(), this); 251 } 252 253 public void onServiceConnected() { 254 // This could be called on a non-UI thread, funnel to UI thread. 255 mUiHandler.post(new Runnable() { 256 public void run() { 257 /* 258 * We just bound to the service, so refresh the UI of the 259 * headset device. 260 */ 261 BluetoothDevice device = mService.getCurrentHeadset(); 262 if (device == null) return; 263 mLocalManager.getCachedDeviceManager() 264 .onProfileStateChanged(device, Profile.HEADSET, 265 BluetoothHeadset.STATE_CONNECTED); 266 } 267 }); 268 } 269 270 public void onServiceDisconnected() { 271 } 272 273 @Override 274 public Set<BluetoothDevice> getConnectedDevices() { 275 Set<BluetoothDevice> devices = null; 276 BluetoothDevice device = mService.getCurrentHeadset(); 277 if (device != null) { 278 devices = new HashSet<BluetoothDevice>(); 279 devices.add(device); 280 } 281 return devices; 282 } 283 284 @Override 285 public boolean connect(BluetoothDevice device) { 286 // Since connectHeadset fails if already connected to a headset, we 287 // disconnect from any headset first 288 mService.disconnectHeadset(); 289 return mService.connectHeadset(device); 290 } 291 292 @Override 293 public boolean disconnect(BluetoothDevice device) { 294 if (mService.getCurrentHeadset().equals(device)) { 295 // Downgrade prority as user is disconnecting the headset. 296 if (mService.getPriority(device) > BluetoothHeadset.PRIORITY_ON) { 297 mService.setPriority(device, BluetoothHeadset.PRIORITY_ON); 298 } 299 return mService.disconnectHeadset(); 300 } else { 301 return false; 302 } 303 } 304 305 @Override 306 public int getConnectionStatus(BluetoothDevice device) { 307 BluetoothDevice currentDevice = mService.getCurrentHeadset(); 308 return currentDevice != null && currentDevice.equals(device) 309 ? convertState(mService.getState()) 310 : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 311 } 312 313 @Override 314 public int getSummary(BluetoothDevice device) { 315 int connectionStatus = getConnectionStatus(device); 316 317 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 318 return R.string.bluetooth_headset_profile_summary_connected; 319 } else { 320 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); 321 } 322 } 323 324 @Override 325 public boolean isPreferred(BluetoothDevice device) { 326 return mService.getPriority(device) > BluetoothHeadset.PRIORITY_OFF; 327 } 328 329 @Override 330 public void setPreferred(BluetoothDevice device, boolean preferred) { 331 mService.setPriority(device, 332 preferred ? BluetoothHeadset.PRIORITY_AUTO_CONNECT : BluetoothHeadset.PRIORITY_OFF); 333 } 334 335 @Override 336 public int convertState(int headsetState) { 337 switch (headsetState) { 338 case BluetoothHeadset.STATE_CONNECTED: 339 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 340 case BluetoothHeadset.STATE_CONNECTING: 341 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 342 case BluetoothHeadset.STATE_DISCONNECTED: 343 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 344 default: 345 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 346 } 347 } 348 } 349 350 /** 351 * OppProfileManager 352 */ 353 private static class OppProfileManager extends LocalBluetoothProfileManager { 354 355 public OppProfileManager(LocalBluetoothManager localManager) { 356 super(localManager); 357 } 358 359 @Override 360 public Set<BluetoothDevice> getConnectedDevices() { 361 return null; 362 } 363 364 @Override 365 public boolean connect(BluetoothDevice device) { 366 return false; 367 } 368 369 @Override 370 public boolean disconnect(BluetoothDevice device) { 371 return false; 372 } 373 374 @Override 375 public int getConnectionStatus(BluetoothDevice device) { 376 return -1; 377 } 378 379 @Override 380 public int getSummary(BluetoothDevice device) { 381 int connectionStatus = getConnectionStatus(device); 382 383 if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { 384 return R.string.bluetooth_opp_profile_summary_connected; 385 } else { 386 return R.string.bluetooth_opp_profile_summary_not_connected; 387 } 388 } 389 390 @Override 391 public boolean isPreferred(BluetoothDevice device) { 392 return false; 393 } 394 395 @Override 396 public void setPreferred(BluetoothDevice device, boolean preferred) { 397 } 398 399 @Override 400 public int convertState(int oppState) { 401 switch (oppState) { 402 case 0: 403 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; 404 case 1: 405 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; 406 case 2: 407 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; 408 default: 409 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; 410 } 411 } 412 } 413} 414