• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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