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 static A2dpService sAd2dpService;
43
44    protected String getName() {
45        return TAG;
46    }
47
48    protected IProfileServiceBinder initBinder() {
49        return new BluetoothA2dpBinder(this);
50    }
51
52    protected boolean start() {
53        mStateMachine = A2dpStateMachine.make(this, this);
54        setA2dpService(this);
55        return true;
56    }
57
58    protected boolean stop() {
59        mStateMachine.doQuit();
60        return true;
61    }
62
63    protected boolean cleanup() {
64        if (mStateMachine!= null) {
65            mStateMachine.cleanup();
66        }
67        clearA2dpService();
68        return true;
69    }
70
71    //API Methods
72
73    public static synchronized A2dpService getA2dpService(){
74        if (sAd2dpService != null && sAd2dpService.isAvailable()) {
75            if (DBG) Log.d(TAG, "getA2DPService(): returning " + sAd2dpService);
76            return sAd2dpService;
77        }
78        if (DBG)  {
79            if (sAd2dpService == null) {
80                Log.d(TAG, "getA2dpService(): service is NULL");
81            } else if (!(sAd2dpService.isAvailable())) {
82                Log.d(TAG,"getA2dpService(): service is not available");
83            }
84        }
85        return null;
86    }
87
88    private static synchronized void setA2dpService(A2dpService instance) {
89        if (instance != null && instance.isAvailable()) {
90            if (DBG) Log.d(TAG, "setA2dpService(): set to: " + sAd2dpService);
91            sAd2dpService = instance;
92        } else {
93            if (DBG)  {
94                if (sAd2dpService == null) {
95                    Log.d(TAG, "setA2dpService(): service not available");
96                } else if (!sAd2dpService.isAvailable()) {
97                    Log.d(TAG,"setA2dpService(): service is cleaning up");
98                }
99            }
100        }
101    }
102
103    private static synchronized void clearA2dpService() {
104        sAd2dpService = null;
105    }
106
107    public boolean connect(BluetoothDevice device) {
108        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
109                                       "Need BLUETOOTH ADMIN permission");
110
111        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
112            return false;
113        }
114
115        int connectionState = mStateMachine.getConnectionState(device);
116        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
117            connectionState == BluetoothProfile.STATE_CONNECTING) {
118            return false;
119        }
120
121        mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
122        return true;
123    }
124
125    boolean disconnect(BluetoothDevice device) {
126        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
127                                       "Need BLUETOOTH ADMIN permission");
128        int connectionState = mStateMachine.getConnectionState(device);
129        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
130            connectionState != BluetoothProfile.STATE_CONNECTING) {
131            return false;
132        }
133
134        mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device);
135        return true;
136    }
137
138    public List<BluetoothDevice> getConnectedDevices() {
139        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
140        return mStateMachine.getConnectedDevices();
141    }
142
143    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
144        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
145        return mStateMachine.getDevicesMatchingConnectionStates(states);
146    }
147
148    int getConnectionState(BluetoothDevice device) {
149        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
150        return mStateMachine.getConnectionState(device);
151    }
152
153    public boolean setPriority(BluetoothDevice device, int priority) {
154        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
155                                       "Need BLUETOOTH_ADMIN permission");
156        Settings.Global.putInt(getContentResolver(),
157            Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
158            priority);
159        if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority);
160        return true;
161    }
162
163    public int getPriority(BluetoothDevice device) {
164        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
165                                       "Need BLUETOOTH_ADMIN permission");
166        int priority = Settings.Global.getInt(getContentResolver(),
167            Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
168            BluetoothProfile.PRIORITY_UNDEFINED);
169        return priority;
170    }
171
172    synchronized boolean isA2dpPlaying(BluetoothDevice device) {
173        enforceCallingOrSelfPermission(BLUETOOTH_PERM,
174                                       "Need BLUETOOTH permission");
175        if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")");
176        return mStateMachine.isPlaying(device);
177    }
178
179    //Binder object: Must be static class or memory leak may occur
180    private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
181        implements IProfileServiceBinder {
182        private A2dpService mService;
183
184        private A2dpService getService() {
185            if (!Utils.checkCaller()) {
186                Log.w(TAG,"A2dp call not allowed for non-active user");
187                return null;
188            }
189
190            if (mService != null && mService.isAvailable()) {
191                return mService;
192            }
193            return null;
194        }
195
196        BluetoothA2dpBinder(A2dpService svc) {
197            mService = svc;
198        }
199
200        public boolean cleanup()  {
201            mService = null;
202            return true;
203        }
204
205        public boolean connect(BluetoothDevice device) {
206            A2dpService service = getService();
207            if (service == null) return false;
208            return service.connect(device);
209        }
210
211        public boolean disconnect(BluetoothDevice device) {
212            A2dpService service = getService();
213            if (service == null) return false;
214            return service.disconnect(device);
215        }
216
217        public List<BluetoothDevice> getConnectedDevices() {
218            A2dpService service = getService();
219            if (service == null) return new ArrayList<BluetoothDevice>(0);
220            return service.getConnectedDevices();
221        }
222
223        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
224            A2dpService service = getService();
225            if (service == null) return new ArrayList<BluetoothDevice>(0);
226            return service.getDevicesMatchingConnectionStates(states);
227        }
228
229        public int getConnectionState(BluetoothDevice device) {
230            A2dpService service = getService();
231            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
232            return service.getConnectionState(device);
233        }
234
235        public boolean setPriority(BluetoothDevice device, int priority) {
236            A2dpService service = getService();
237            if (service == null) return false;
238            return service.setPriority(device, priority);
239        }
240
241        public int getPriority(BluetoothDevice device) {
242            A2dpService service = getService();
243            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
244            return service.getPriority(device);
245        }
246
247        public boolean isA2dpPlaying(BluetoothDevice device) {
248            A2dpService service = getService();
249            if (service == null) return false;
250            return service.isA2dpPlaying(device);
251        }
252    };
253}
254