A2dpService.java revision e013986fbc8796db569587afa5c6b34e53d7a31b
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        mAvrcp = Avrcp.make(this);
55        mStateMachine = A2dpStateMachine.make(this, 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    public void setAvrcpAudioState(int state) {
193        mAvrcp.setA2dpAudioState(state);
194    }
195
196    synchronized boolean isA2dpPlaying(BluetoothDevice device) {
197        enforceCallingOrSelfPermission(BLUETOOTH_PERM,
198                                       "Need BLUETOOTH permission");
199        if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")");
200        return mStateMachine.isPlaying(device);
201    }
202
203    synchronized public void sendPassThroughCmd(int keyCode, int keyState) {
204        if (DBG) Log.d(TAG, "sendPassThroughCmd");
205        if (mAvrcp != null) {
206            mAvrcp.sendPassThroughCmd(keyCode, keyState);
207        } else {
208            Log.e(TAG,"mAvrcp is null");
209        }
210    }
211
212    synchronized boolean isAvrcpConnected(BluetoothDevice device) {
213        if (DBG) Log.d(TAG, "isAvrcpConnected");
214        if (mAvrcp != null) {
215            return mAvrcp.isAvrcpConnected(device);
216        } else {
217            Log.e(TAG,"mAvrcp is null");
218            return false;
219        }
220    }
221
222    //Binder object: Must be static class or memory leak may occur
223    private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
224        implements IProfileServiceBinder {
225        private A2dpService mService;
226
227        private A2dpService getService() {
228            if (!Utils.checkCaller()) {
229                Log.w(TAG,"A2dp call not allowed for non-active user");
230                return null;
231            }
232
233            if (mService != null && mService.isAvailable()) {
234                return mService;
235            }
236            return null;
237        }
238
239        BluetoothA2dpBinder(A2dpService svc) {
240            mService = svc;
241        }
242
243        public boolean cleanup()  {
244            mService = null;
245            return true;
246        }
247
248        public boolean connect(BluetoothDevice device) {
249            A2dpService service = getService();
250            if (service == null) return false;
251            return service.connect(device);
252        }
253
254        public boolean disconnect(BluetoothDevice device) {
255            A2dpService service = getService();
256            if (service == null) return false;
257            return service.disconnect(device);
258        }
259
260        public List<BluetoothDevice> getConnectedDevices() {
261            A2dpService service = getService();
262            if (service == null) return new ArrayList<BluetoothDevice>(0);
263            return service.getConnectedDevices();
264        }
265
266        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
267            A2dpService service = getService();
268            if (service == null) return new ArrayList<BluetoothDevice>(0);
269            return service.getDevicesMatchingConnectionStates(states);
270        }
271
272        public int getConnectionState(BluetoothDevice device) {
273            A2dpService service = getService();
274            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
275            return service.getConnectionState(device);
276        }
277
278        public boolean setPriority(BluetoothDevice device, int priority) {
279            A2dpService service = getService();
280            if (service == null) return false;
281            return service.setPriority(device, priority);
282        }
283
284        public int getPriority(BluetoothDevice device) {
285            A2dpService service = getService();
286            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
287            return service.getPriority(device);
288        }
289
290        public boolean isAvrcpAbsoluteVolumeSupported() {
291            A2dpService service = getService();
292            if (service == null) return false;
293            return service.isAvrcpAbsoluteVolumeSupported();
294        }
295
296        public void adjustAvrcpAbsoluteVolume(int direction) {
297            A2dpService service = getService();
298            if (service == null) return;
299            service.adjustAvrcpAbsoluteVolume(direction);
300        }
301
302        public void setAvrcpAbsoluteVolume(int volume) {
303            A2dpService service = getService();
304            if (service == null) return;
305            service.setAvrcpAbsoluteVolume(volume);
306        }
307
308        public boolean isA2dpPlaying(BluetoothDevice device) {
309            A2dpService service = getService();
310            if (service == null) return false;
311            return service.isA2dpPlaying(device);
312        }
313
314        public boolean isAvrcpConnected(BluetoothDevice device) {
315            Log.v(TAG,"Binder Call: isAvrcpConnected: " + device.getAddress());
316            A2dpService service = getService();
317            if (service == null) return false;
318            return service.isAvrcpConnected(device);
319        }
320
321        public void sendPassThroughCmd(int keyCode, int keyState) {
322            Log.v(TAG,"Binder Call: sendPassThroughCmd");
323            A2dpService service = getService();
324            if (service == null) return;
325            service.sendPassThroughCmd(keyCode, keyState);
326        }
327    };
328}
329