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