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.avrcp.Avrcp;
27import com.android.bluetooth.btservice.ProfileService;
28import com.android.bluetooth.Utils;
29import java.util.ArrayList;
30import java.util.Iterator;
31import java.util.List;
32import java.util.Map;
33
34/**
35 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
36 * @hide
37 */
38public 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
46    protected String getName() {
47        return TAG;
48    }
49
50    protected IProfileServiceBinder initBinder() {
51        return new BluetoothA2dpBinder(this);
52    }
53
54    protected boolean start() {
55        mAvrcp = Avrcp.make(this);
56        mStateMachine = A2dpStateMachine.make(this, this);
57        setA2dpService(this);
58        return true;
59    }
60
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
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
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
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
115    private static synchronized void clearA2dpService() {
116        sAd2dpService = null;
117    }
118
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
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
150    public List<BluetoothDevice> getConnectedDevices() {
151        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
152        return mStateMachine.getConnectedDevices();
153    }
154
155    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
156        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
157        return mStateMachine.getDevicesMatchingConnectionStates(states);
158    }
159
160    int getConnectionState(BluetoothDevice device) {
161        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
162        return mStateMachine.getConnectionState(device);
163    }
164
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
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 */
185    public boolean isAvrcpAbsoluteVolumeSupported() {
186        return mAvrcp.isAbsoluteVolumeSupported();
187    }
188
189    public void adjustAvrcpAbsoluteVolume(int direction) {
190        mAvrcp.adjustVolume(direction);
191    }
192
193    public void setAvrcpAbsoluteVolume(int volume) {
194        mAvrcp.setAbsoluteVolume(volume);
195    }
196
197    public void setAvrcpAudioState(int state) {
198        mAvrcp.setA2dpAudioState(state);
199    }
200
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
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
225        BluetoothA2dpBinder(A2dpService svc) {
226            mService = svc;
227        }
228
229        public boolean cleanup()  {
230            mService = null;
231            return true;
232        }
233
234        public boolean connect(BluetoothDevice device) {
235            A2dpService service = getService();
236            if (service == null) return false;
237            return service.connect(device);
238        }
239
240        public boolean disconnect(BluetoothDevice device) {
241            A2dpService service = getService();
242            if (service == null) return false;
243            return service.disconnect(device);
244        }
245
246        public List<BluetoothDevice> getConnectedDevices() {
247            A2dpService service = getService();
248            if (service == null) return new ArrayList<BluetoothDevice>(0);
249            return service.getConnectedDevices();
250        }
251
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
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
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
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
276        public boolean isAvrcpAbsoluteVolumeSupported() {
277            A2dpService service = getService();
278            if (service == null) return false;
279            return service.isAvrcpAbsoluteVolumeSupported();
280        }
281
282        public void adjustAvrcpAbsoluteVolume(int direction) {
283            A2dpService service = getService();
284            if (service == null) return;
285            service.adjustAvrcpAbsoluteVolume(direction);
286        }
287
288        public void setAvrcpAbsoluteVolume(int volume) {
289            A2dpService service = getService();
290            if (service == null) return;
291            service.setAvrcpAbsoluteVolume(volume);
292        }
293
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