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 
getName()46     protected String getName() {
47         return TAG;
48     }
49 
initBinder()50     protected IProfileServiceBinder initBinder() {
51         return new BluetoothA2dpBinder(this);
52     }
53 
start()54     protected boolean start() {
55         mAvrcp = Avrcp.make(this);
56         mStateMachine = A2dpStateMachine.make(this, this);
57         setA2dpService(this);
58         return true;
59     }
60 
stop()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 
cleanup()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 
getA2dpService()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 
setA2dpService(A2dpService instance)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 
clearA2dpService()115     private static synchronized void clearA2dpService() {
116         sAd2dpService = null;
117     }
118 
connect(BluetoothDevice device)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 
disconnect(BluetoothDevice device)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 
getConnectedDevices()150     public List<BluetoothDevice> getConnectedDevices() {
151         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
152         return mStateMachine.getConnectedDevices();
153     }
154 
getDevicesMatchingConnectionStates(int[] states)155     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
156         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
157         return mStateMachine.getDevicesMatchingConnectionStates(states);
158     }
159 
getConnectionState(BluetoothDevice device)160     int getConnectionState(BluetoothDevice device) {
161         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
162         return mStateMachine.getConnectionState(device);
163     }
164 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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 */
isAvrcpAbsoluteVolumeSupported()185     public boolean isAvrcpAbsoluteVolumeSupported() {
186         return mAvrcp.isAbsoluteVolumeSupported();
187     }
188 
adjustAvrcpAbsoluteVolume(int direction)189     public void adjustAvrcpAbsoluteVolume(int direction) {
190         mAvrcp.adjustVolume(direction);
191     }
192 
setAvrcpAbsoluteVolume(int volume)193     public void setAvrcpAbsoluteVolume(int volume) {
194         mAvrcp.setAbsoluteVolume(volume);
195     }
196 
setAvrcpAudioState(int state)197     public void setAvrcpAudioState(int state) {
198         mAvrcp.setA2dpAudioState(state);
199     }
200 
isA2dpPlaying(BluetoothDevice device)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 
getService()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 
BluetoothA2dpBinder(A2dpService svc)225         BluetoothA2dpBinder(A2dpService svc) {
226             mService = svc;
227         }
228 
cleanup()229         public boolean cleanup()  {
230             mService = null;
231             return true;
232         }
233 
connect(BluetoothDevice device)234         public boolean connect(BluetoothDevice device) {
235             A2dpService service = getService();
236             if (service == null) return false;
237             return service.connect(device);
238         }
239 
disconnect(BluetoothDevice device)240         public boolean disconnect(BluetoothDevice device) {
241             A2dpService service = getService();
242             if (service == null) return false;
243             return service.disconnect(device);
244         }
245 
getConnectedDevices()246         public List<BluetoothDevice> getConnectedDevices() {
247             A2dpService service = getService();
248             if (service == null) return new ArrayList<BluetoothDevice>(0);
249             return service.getConnectedDevices();
250         }
251 
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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 
isAvrcpAbsoluteVolumeSupported()276         public boolean isAvrcpAbsoluteVolumeSupported() {
277             A2dpService service = getService();
278             if (service == null) return false;
279             return service.isAvrcpAbsoluteVolumeSupported();
280         }
281 
adjustAvrcpAbsoluteVolume(int direction)282         public void adjustAvrcpAbsoluteVolume(int direction) {
283             A2dpService service = getService();
284             if (service == null) return;
285             service.adjustAvrcpAbsoluteVolume(direction);
286         }
287 
setAvrcpAbsoluteVolume(int volume)288         public void setAvrcpAbsoluteVolume(int volume) {
289             A2dpService service = getService();
290             if (service == null) return;
291             service.setAvrcpAbsoluteVolume(volume);
292         }
293 
isA2dpPlaying(BluetoothDevice device)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     @Override
dump(StringBuilder sb)302     public void dump(StringBuilder sb) {
303         super.dump(sb);
304         if (mStateMachine != null) {
305             mStateMachine.dump(sb);
306         }
307         if (mAvrcp != null) {
308             mAvrcp.dump(sb);
309         }
310     }
311 }
312