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