1 /* 2 * Copyright (C) 2012 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 17 package com.android.bluetooth.a2dp; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothProfile; 21 import android.bluetooth.BluetoothUuid; 22 import android.bluetooth.IBluetoothA2dp; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.os.ParcelUuid; 26 import android.provider.Settings; 27 import android.util.Log; 28 import com.android.bluetooth.avrcp.Avrcp; 29 import com.android.bluetooth.btservice.ProfileService; 30 import com.android.bluetooth.Utils; 31 import java.util.ArrayList; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Map; 35 36 /** 37 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application. 38 * @hide 39 */ 40 public class A2dpService extends ProfileService { 41 private static final boolean DBG = false; 42 private static final String TAG="A2dpService"; 43 44 private A2dpStateMachine mStateMachine; 45 private Avrcp mAvrcp; 46 private static A2dpService sAd2dpService; 47 static final ParcelUuid[] A2DP_SOURCE_UUID = { 48 BluetoothUuid.AudioSource 49 }; 50 static final ParcelUuid[] A2DP_SOURCE_SINK_UUIDS = { 51 BluetoothUuid.AudioSource, 52 BluetoothUuid.AudioSink 53 }; 54 55 protected String getName() { 56 return TAG; 57 } 58 59 protected IProfileServiceBinder initBinder() { 60 return new BluetoothA2dpBinder(this); 61 } 62 63 protected boolean start() { 64 mAvrcp = Avrcp.make(this); 65 mStateMachine = A2dpStateMachine.make(this, this); 66 setA2dpService(this); 67 return true; 68 } 69 70 protected boolean stop() { 71 if (mStateMachine != null) { 72 mStateMachine.doQuit(); 73 } 74 if (mAvrcp != null) { 75 mAvrcp.doQuit(); 76 } 77 return true; 78 } 79 80 protected boolean cleanup() { 81 if (mStateMachine!= null) { 82 mStateMachine.cleanup(); 83 } 84 if (mAvrcp != null) { 85 mAvrcp.cleanup(); 86 mAvrcp = null; 87 } 88 clearA2dpService(); 89 return true; 90 } 91 92 //API Methods 93 94 public static synchronized A2dpService getA2dpService(){ 95 if (sAd2dpService != null && sAd2dpService.isAvailable()) { 96 if (DBG) Log.d(TAG, "getA2DPService(): returning " + sAd2dpService); 97 return sAd2dpService; 98 } 99 if (DBG) { 100 if (sAd2dpService == null) { 101 Log.d(TAG, "getA2dpService(): service is NULL"); 102 } else if (!(sAd2dpService.isAvailable())) { 103 Log.d(TAG,"getA2dpService(): service is not available"); 104 } 105 } 106 return null; 107 } 108 109 private static synchronized void setA2dpService(A2dpService instance) { 110 if (instance != null && instance.isAvailable()) { 111 if (DBG) Log.d(TAG, "setA2dpService(): set to: " + sAd2dpService); 112 sAd2dpService = instance; 113 } else { 114 if (DBG) { 115 if (sAd2dpService == null) { 116 Log.d(TAG, "setA2dpService(): service not available"); 117 } else if (!sAd2dpService.isAvailable()) { 118 Log.d(TAG,"setA2dpService(): service is cleaning up"); 119 } 120 } 121 } 122 } 123 124 private static synchronized void clearA2dpService() { 125 sAd2dpService = null; 126 } 127 128 public boolean connect(BluetoothDevice device) { 129 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 130 "Need BLUETOOTH ADMIN permission"); 131 132 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 133 return false; 134 } 135 ParcelUuid[] featureUuids = device.getUuids(); 136 if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) && 137 !(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) { 138 Log.e(TAG,"Remote does not have A2dp Sink UUID"); 139 return false; 140 } 141 142 int connectionState = mStateMachine.getConnectionState(device); 143 if (connectionState == BluetoothProfile.STATE_CONNECTED || 144 connectionState == BluetoothProfile.STATE_CONNECTING) { 145 return false; 146 } 147 148 mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device); 149 return true; 150 } 151 152 boolean disconnect(BluetoothDevice device) { 153 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 154 "Need BLUETOOTH ADMIN permission"); 155 int connectionState = mStateMachine.getConnectionState(device); 156 if (connectionState != BluetoothProfile.STATE_CONNECTED && 157 connectionState != BluetoothProfile.STATE_CONNECTING) { 158 return false; 159 } 160 161 mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device); 162 return true; 163 } 164 165 public List<BluetoothDevice> getConnectedDevices() { 166 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 167 return mStateMachine.getConnectedDevices(); 168 } 169 170 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 171 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 172 return mStateMachine.getDevicesMatchingConnectionStates(states); 173 } 174 175 int getConnectionState(BluetoothDevice device) { 176 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 177 return mStateMachine.getConnectionState(device); 178 } 179 180 public boolean setPriority(BluetoothDevice device, int priority) { 181 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 182 "Need BLUETOOTH_ADMIN permission"); 183 Settings.Global.putInt(getContentResolver(), 184 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), 185 priority); 186 if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority); 187 return true; 188 } 189 190 public int getPriority(BluetoothDevice device) { 191 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 192 "Need BLUETOOTH_ADMIN permission"); 193 int priority = Settings.Global.getInt(getContentResolver(), 194 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), 195 BluetoothProfile.PRIORITY_UNDEFINED); 196 return priority; 197 } 198 199 /* Absolute volume implementation */ 200 public boolean isAvrcpAbsoluteVolumeSupported() { 201 return mAvrcp.isAbsoluteVolumeSupported(); 202 } 203 204 public void adjustAvrcpAbsoluteVolume(int direction) { 205 mAvrcp.adjustVolume(direction); 206 } 207 208 public void setAvrcpAbsoluteVolume(int volume) { 209 mAvrcp.setAbsoluteVolume(volume); 210 } 211 212 public void setAvrcpAudioState(int state) { 213 mAvrcp.setA2dpAudioState(state); 214 } 215 216 public void resetAvrcpBlacklist(BluetoothDevice device) { 217 if (mAvrcp != null) { 218 mAvrcp.resetBlackList(device.getAddress()); 219 } 220 } 221 222 synchronized boolean isA2dpPlaying(BluetoothDevice device) { 223 enforceCallingOrSelfPermission(BLUETOOTH_PERM, 224 "Need BLUETOOTH permission"); 225 if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")"); 226 return mStateMachine.isPlaying(device); 227 } 228 229 //Binder object: Must be static class or memory leak may occur 230 private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 231 implements IProfileServiceBinder { 232 private A2dpService mService; 233 234 private A2dpService getService() { 235 if (!Utils.checkCaller()) { 236 Log.w(TAG,"A2dp call not allowed for non-active user"); 237 return null; 238 } 239 240 if (mService != null && mService.isAvailable()) { 241 return mService; 242 } 243 return null; 244 } 245 246 BluetoothA2dpBinder(A2dpService svc) { 247 mService = svc; 248 } 249 250 public boolean cleanup() { 251 mService = null; 252 return true; 253 } 254 255 public boolean connect(BluetoothDevice device) { 256 A2dpService service = getService(); 257 if (service == null) return false; 258 return service.connect(device); 259 } 260 261 public boolean disconnect(BluetoothDevice device) { 262 A2dpService service = getService(); 263 if (service == null) return false; 264 return service.disconnect(device); 265 } 266 267 public List<BluetoothDevice> getConnectedDevices() { 268 A2dpService service = getService(); 269 if (service == null) return new ArrayList<BluetoothDevice>(0); 270 return service.getConnectedDevices(); 271 } 272 273 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 274 A2dpService service = getService(); 275 if (service == null) return new ArrayList<BluetoothDevice>(0); 276 return service.getDevicesMatchingConnectionStates(states); 277 } 278 279 public int getConnectionState(BluetoothDevice device) { 280 A2dpService service = getService(); 281 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 282 return service.getConnectionState(device); 283 } 284 285 public boolean setPriority(BluetoothDevice device, int priority) { 286 A2dpService service = getService(); 287 if (service == null) return false; 288 return service.setPriority(device, priority); 289 } 290 291 public int getPriority(BluetoothDevice device) { 292 A2dpService service = getService(); 293 if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; 294 return service.getPriority(device); 295 } 296 297 public boolean isAvrcpAbsoluteVolumeSupported() { 298 A2dpService service = getService(); 299 if (service == null) return false; 300 return service.isAvrcpAbsoluteVolumeSupported(); 301 } 302 303 public void adjustAvrcpAbsoluteVolume(int direction) { 304 A2dpService service = getService(); 305 if (service == null) return; 306 service.adjustAvrcpAbsoluteVolume(direction); 307 } 308 309 public void setAvrcpAbsoluteVolume(int volume) { 310 A2dpService service = getService(); 311 if (service == null) return; 312 service.setAvrcpAbsoluteVolume(volume); 313 } 314 315 public boolean isA2dpPlaying(BluetoothDevice device) { 316 A2dpService service = getService(); 317 if (service == null) return false; 318 return service.isA2dpPlaying(device); 319 } 320 }; 321 322 @Override 323 public void dump(StringBuilder sb) { 324 super.dump(sb); 325 if (mStateMachine != null) { 326 mStateMachine.dump(sb); 327 } 328 if (mAvrcp != null) { 329 mAvrcp.dump(sb); 330 } 331 } 332 } 333