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