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