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