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 getName()55 protected String getName() { 56 return TAG; 57 } 58 initBinder()59 protected IProfileServiceBinder initBinder() { 60 return new BluetoothA2dpBinder(this); 61 } 62 start()63 protected boolean start() { 64 mAvrcp = Avrcp.make(this); 65 mStateMachine = A2dpStateMachine.make(this, this); 66 setA2dpService(this); 67 return true; 68 } 69 stop()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 cleanup()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 getA2dpService()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 setA2dpService(A2dpService instance)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 clearA2dpService()124 private static synchronized void clearA2dpService() { 125 sAd2dpService = null; 126 } 127 connect(BluetoothDevice device)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 disconnect(BluetoothDevice device)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 getConnectedDevices()165 public List<BluetoothDevice> getConnectedDevices() { 166 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 167 return mStateMachine.getConnectedDevices(); 168 } 169 getDevicesMatchingConnectionStates(int[] states)170 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 171 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 172 return mStateMachine.getDevicesMatchingConnectionStates(states); 173 } 174 getConnectionState(BluetoothDevice device)175 int getConnectionState(BluetoothDevice device) { 176 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 177 return mStateMachine.getConnectionState(device); 178 } 179 setPriority(BluetoothDevice device, int priority)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 getPriority(BluetoothDevice device)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 */ isAvrcpAbsoluteVolumeSupported()200 public boolean isAvrcpAbsoluteVolumeSupported() { 201 return mAvrcp.isAbsoluteVolumeSupported(); 202 } 203 adjustAvrcpAbsoluteVolume(int direction)204 public void adjustAvrcpAbsoluteVolume(int direction) { 205 mAvrcp.adjustVolume(direction); 206 } 207 setAvrcpAbsoluteVolume(int volume)208 public void setAvrcpAbsoluteVolume(int volume) { 209 mAvrcp.setAbsoluteVolume(volume); 210 } 211 setAvrcpAudioState(int state)212 public void setAvrcpAudioState(int state) { 213 mAvrcp.setA2dpAudioState(state); 214 } 215 resetAvrcpBlacklist(BluetoothDevice device)216 public void resetAvrcpBlacklist(BluetoothDevice device) { 217 mAvrcp.resetBlackList(device.getAddress()); 218 } 219 isA2dpPlaying(BluetoothDevice device)220 synchronized boolean isA2dpPlaying(BluetoothDevice device) { 221 enforceCallingOrSelfPermission(BLUETOOTH_PERM, 222 "Need BLUETOOTH permission"); 223 if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")"); 224 return mStateMachine.isPlaying(device); 225 } 226 227 //Binder object: Must be static class or memory leak may occur 228 private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 229 implements IProfileServiceBinder { 230 private A2dpService mService; 231 getService()232 private A2dpService getService() { 233 if (!Utils.checkCaller()) { 234 Log.w(TAG,"A2dp call not allowed for non-active user"); 235 return null; 236 } 237 238 if (mService != null && mService.isAvailable()) { 239 return mService; 240 } 241 return null; 242 } 243 BluetoothA2dpBinder(A2dpService svc)244 BluetoothA2dpBinder(A2dpService svc) { 245 mService = svc; 246 } 247 cleanup()248 public boolean cleanup() { 249 mService = null; 250 return true; 251 } 252 connect(BluetoothDevice device)253 public boolean connect(BluetoothDevice device) { 254 A2dpService service = getService(); 255 if (service == null) return false; 256 return service.connect(device); 257 } 258 disconnect(BluetoothDevice device)259 public boolean disconnect(BluetoothDevice device) { 260 A2dpService service = getService(); 261 if (service == null) return false; 262 return service.disconnect(device); 263 } 264 getConnectedDevices()265 public List<BluetoothDevice> getConnectedDevices() { 266 A2dpService service = getService(); 267 if (service == null) return new ArrayList<BluetoothDevice>(0); 268 return service.getConnectedDevices(); 269 } 270 getDevicesMatchingConnectionStates(int[] states)271 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 272 A2dpService service = getService(); 273 if (service == null) return new ArrayList<BluetoothDevice>(0); 274 return service.getDevicesMatchingConnectionStates(states); 275 } 276 getConnectionState(BluetoothDevice device)277 public int getConnectionState(BluetoothDevice device) { 278 A2dpService service = getService(); 279 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 280 return service.getConnectionState(device); 281 } 282 setPriority(BluetoothDevice device, int priority)283 public boolean setPriority(BluetoothDevice device, int priority) { 284 A2dpService service = getService(); 285 if (service == null) return false; 286 return service.setPriority(device, priority); 287 } 288 getPriority(BluetoothDevice device)289 public int getPriority(BluetoothDevice device) { 290 A2dpService service = getService(); 291 if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; 292 return service.getPriority(device); 293 } 294 isAvrcpAbsoluteVolumeSupported()295 public boolean isAvrcpAbsoluteVolumeSupported() { 296 A2dpService service = getService(); 297 if (service == null) return false; 298 return service.isAvrcpAbsoluteVolumeSupported(); 299 } 300 adjustAvrcpAbsoluteVolume(int direction)301 public void adjustAvrcpAbsoluteVolume(int direction) { 302 A2dpService service = getService(); 303 if (service == null) return; 304 service.adjustAvrcpAbsoluteVolume(direction); 305 } 306 setAvrcpAbsoluteVolume(int volume)307 public void setAvrcpAbsoluteVolume(int volume) { 308 A2dpService service = getService(); 309 if (service == null) return; 310 service.setAvrcpAbsoluteVolume(volume); 311 } 312 isA2dpPlaying(BluetoothDevice device)313 public boolean isA2dpPlaying(BluetoothDevice device) { 314 A2dpService service = getService(); 315 if (service == null) return false; 316 return service.isA2dpPlaying(device); 317 } 318 }; 319 320 @Override dump(StringBuilder sb)321 public void dump(StringBuilder sb) { 322 super.dump(sb); 323 if (mStateMachine != null) { 324 mStateMachine.dump(sb); 325 } 326 if (mAvrcp != null) { 327 mAvrcp.dump(sb); 328 } 329 } 330 } 331