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