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         if (mAvrcp != null) {
218             mAvrcp.resetBlackList(device.getAddress());
219         }
220     }
221 
isA2dpPlaying(BluetoothDevice device)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 
getService()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 
BluetoothA2dpBinder(A2dpService svc)246         BluetoothA2dpBinder(A2dpService svc) {
247             mService = svc;
248         }
249 
cleanup()250         public boolean cleanup()  {
251             mService = null;
252             return true;
253         }
254 
connect(BluetoothDevice device)255         public boolean connect(BluetoothDevice device) {
256             A2dpService service = getService();
257             if (service == null) return false;
258             return service.connect(device);
259         }
260 
disconnect(BluetoothDevice device)261         public boolean disconnect(BluetoothDevice device) {
262             A2dpService service = getService();
263             if (service == null) return false;
264             return service.disconnect(device);
265         }
266 
getConnectedDevices()267         public List<BluetoothDevice> getConnectedDevices() {
268             A2dpService service = getService();
269             if (service == null) return new ArrayList<BluetoothDevice>(0);
270             return service.getConnectedDevices();
271         }
272 
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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 
isAvrcpAbsoluteVolumeSupported()297         public boolean isAvrcpAbsoluteVolumeSupported() {
298             A2dpService service = getService();
299             if (service == null) return false;
300             return service.isAvrcpAbsoluteVolumeSupported();
301         }
302 
adjustAvrcpAbsoluteVolume(int direction)303         public void adjustAvrcpAbsoluteVolume(int direction) {
304             A2dpService service = getService();
305             if (service == null) return;
306             service.adjustAvrcpAbsoluteVolume(direction);
307         }
308 
setAvrcpAbsoluteVolume(int volume)309         public void setAvrcpAbsoluteVolume(int volume) {
310             A2dpService service = getService();
311             if (service == null) return;
312             service.setAvrcpAbsoluteVolume(volume);
313         }
314 
isA2dpPlaying(BluetoothDevice device)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
dump(StringBuilder sb)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