• 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.BluetoothA2dp;
20 import android.bluetooth.BluetoothCodecConfig;
21 import android.bluetooth.BluetoothCodecStatus;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothProfile;
24 import android.bluetooth.BluetoothUuid;
25 import android.bluetooth.IBluetoothA2dp;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.os.ParcelUuid;
31 import android.provider.Settings;
32 import android.util.Log;
33 import com.android.bluetooth.avrcp.Avrcp;
34 import com.android.bluetooth.btservice.ProfileService;
35 import com.android.bluetooth.Utils;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Objects;
39 
40 /**
41  * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
42  * @hide
43  */
44 public class A2dpService extends ProfileService {
45     private static final boolean DBG = false;
46     private static final String TAG="A2dpService";
47 
48     private A2dpStateMachine mStateMachine;
49     private Avrcp mAvrcp;
50 
51     private BroadcastReceiver mConnectionStateChangedReceiver = new BroadcastReceiver() {
52         @Override
53         public void onReceive(Context context, Intent intent) {
54             if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
55                 return;
56             }
57             int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
58             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
59             if (state != BluetoothProfile.STATE_CONNECTED || device == null) {
60                 return;
61             }
62             // Each time a device connects, we want to re-check if it supports optional
63             // codecs (perhaps it's had a firmware update, etc.) and save that state if
64             // it differs from what we had saved before.
65             int previousSupport = getSupportsOptionalCodecs(device);
66             boolean supportsOptional = false;
67             for (BluetoothCodecConfig config :
68                     mStateMachine.getCodecStatus().getCodecsSelectableCapabilities()) {
69                 if (!config.isMandatoryCodec()) {
70                     supportsOptional = true;
71                     break;
72                 }
73             }
74             if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
75                     || supportsOptional
76                             != (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) {
77                 setSupportsOptionalCodecs(device, supportsOptional);
78             }
79             if (supportsOptional) {
80                 int enabled = getOptionalCodecsEnabled(device);
81                 if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
82                     enableOptionalCodecs();
83                 } else if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED) {
84                     disableOptionalCodecs();
85                 }
86             }
87         }
88     };
89 
90     private static A2dpService sAd2dpService;
91     static final ParcelUuid[] A2DP_SOURCE_UUID = {
92         BluetoothUuid.AudioSource
93     };
94     static final ParcelUuid[] A2DP_SOURCE_SINK_UUIDS = {
95         BluetoothUuid.AudioSource,
96         BluetoothUuid.AudioSink
97     };
98 
getName()99     protected String getName() {
100         return TAG;
101     }
102 
initBinder()103     protected IProfileServiceBinder initBinder() {
104         return new BluetoothA2dpBinder(this);
105     }
106 
start()107     protected boolean start() {
108         mAvrcp = Avrcp.make(this);
109         mStateMachine = A2dpStateMachine.make(this, this);
110         setA2dpService(this);
111         IntentFilter filter = new IntentFilter();
112         filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
113         registerReceiver(mConnectionStateChangedReceiver, filter);
114         return true;
115     }
116 
stop()117     protected boolean stop() {
118         if (mStateMachine != null) {
119             mStateMachine.doQuit();
120         }
121         if (mAvrcp != null) {
122             mAvrcp.doQuit();
123         }
124         return true;
125     }
126 
cleanup()127     protected boolean cleanup() {
128         unregisterReceiver(mConnectionStateChangedReceiver);
129         if (mStateMachine!= null) {
130             mStateMachine.cleanup();
131         }
132         if (mAvrcp != null) {
133             mAvrcp.cleanup();
134             mAvrcp = null;
135         }
136         clearA2dpService();
137         return true;
138     }
139 
140     //API Methods
141 
getA2dpService()142     public static synchronized A2dpService getA2dpService(){
143         if (sAd2dpService != null && sAd2dpService.isAvailable()) {
144             if (DBG) Log.d(TAG, "getA2DPService(): returning " + sAd2dpService);
145             return sAd2dpService;
146         }
147         if (DBG)  {
148             if (sAd2dpService == null) {
149                 Log.d(TAG, "getA2dpService(): service is NULL");
150             } else if (!(sAd2dpService.isAvailable())) {
151                 Log.d(TAG,"getA2dpService(): service is not available");
152             }
153         }
154         return null;
155     }
156 
setA2dpService(A2dpService instance)157     private static synchronized void setA2dpService(A2dpService instance) {
158         if (instance != null && instance.isAvailable()) {
159             if (DBG) Log.d(TAG, "setA2dpService(): set to: " + sAd2dpService);
160             sAd2dpService = instance;
161         } else {
162             if (DBG)  {
163                 if (sAd2dpService == null) {
164                     Log.d(TAG, "setA2dpService(): service not available");
165                 } else if (!sAd2dpService.isAvailable()) {
166                     Log.d(TAG,"setA2dpService(): service is cleaning up");
167                 }
168             }
169         }
170     }
171 
clearA2dpService()172     private static synchronized void clearA2dpService() {
173         sAd2dpService = null;
174     }
175 
connect(BluetoothDevice device)176     public boolean connect(BluetoothDevice device) {
177         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
178                                        "Need BLUETOOTH ADMIN permission");
179 
180         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
181             return false;
182         }
183         ParcelUuid[] featureUuids = device.getUuids();
184         if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) &&
185             !(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {
186             Log.e(TAG,"Remote does not have A2dp Sink UUID");
187             return false;
188         }
189 
190         int connectionState = mStateMachine.getConnectionState(device);
191         if (connectionState == BluetoothProfile.STATE_CONNECTED ||
192             connectionState == BluetoothProfile.STATE_CONNECTING) {
193             return false;
194         }
195 
196         mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
197         return true;
198     }
199 
disconnect(BluetoothDevice device)200     boolean disconnect(BluetoothDevice device) {
201         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
202                                        "Need BLUETOOTH ADMIN permission");
203         int connectionState = mStateMachine.getConnectionState(device);
204         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
205             connectionState != BluetoothProfile.STATE_CONNECTING) {
206             return false;
207         }
208 
209         mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device);
210         return true;
211     }
212 
getConnectedDevices()213     public List<BluetoothDevice> getConnectedDevices() {
214         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
215         return mStateMachine.getConnectedDevices();
216     }
217 
getDevicesMatchingConnectionStates(int[] states)218     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
219         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
220         return mStateMachine.getDevicesMatchingConnectionStates(states);
221     }
222 
getConnectionState(BluetoothDevice device)223     public int getConnectionState(BluetoothDevice device) {
224         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
225         return mStateMachine.getConnectionState(device);
226     }
227 
setPriority(BluetoothDevice device, int priority)228     public boolean setPriority(BluetoothDevice device, int priority) {
229         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
230                                        "Need BLUETOOTH_ADMIN permission");
231         Settings.Global.putInt(getContentResolver(),
232             Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
233             priority);
234         if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority);
235         return true;
236     }
237 
getPriority(BluetoothDevice device)238     public int getPriority(BluetoothDevice device) {
239         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
240                                        "Need BLUETOOTH_ADMIN permission");
241         int priority = Settings.Global.getInt(getContentResolver(),
242             Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
243             BluetoothProfile.PRIORITY_UNDEFINED);
244         return priority;
245     }
246 
247     /* Absolute volume implementation */
isAvrcpAbsoluteVolumeSupported()248     public boolean isAvrcpAbsoluteVolumeSupported() {
249         return mAvrcp.isAbsoluteVolumeSupported();
250     }
251 
adjustAvrcpAbsoluteVolume(int direction)252     public void adjustAvrcpAbsoluteVolume(int direction) {
253         mAvrcp.adjustVolume(direction);
254     }
255 
setAvrcpAbsoluteVolume(int volume)256     public void setAvrcpAbsoluteVolume(int volume) {
257         mAvrcp.setAbsoluteVolume(volume);
258     }
259 
setAvrcpAudioState(int state)260     public void setAvrcpAudioState(int state) {
261         mAvrcp.setA2dpAudioState(state);
262     }
263 
resetAvrcpBlacklist(BluetoothDevice device)264     public void resetAvrcpBlacklist(BluetoothDevice device) {
265         if (mAvrcp != null) {
266             mAvrcp.resetBlackList(device.getAddress());
267         }
268     }
269 
isA2dpPlaying(BluetoothDevice device)270     synchronized boolean isA2dpPlaying(BluetoothDevice device) {
271         enforceCallingOrSelfPermission(BLUETOOTH_PERM,
272                                        "Need BLUETOOTH permission");
273         if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")");
274         return mStateMachine.isPlaying(device);
275     }
276 
getCodecStatus()277     public BluetoothCodecStatus getCodecStatus() {
278         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
279         if (DBG) Log.d(TAG, "getCodecStatus()");
280         return mStateMachine.getCodecStatus();
281     }
282 
setCodecConfigPreference(BluetoothCodecConfig codecConfig)283     public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
284         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
285         if (DBG) Log.d(TAG, "setCodecConfigPreference(): " + Objects.toString(codecConfig));
286         mStateMachine.setCodecConfigPreference(codecConfig);
287     }
288 
enableOptionalCodecs()289     public void enableOptionalCodecs() {
290         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
291         if (DBG) Log.d(TAG, "enableOptionalCodecs()");
292         mStateMachine.enableOptionalCodecs();
293     }
294 
disableOptionalCodecs()295     public void disableOptionalCodecs() {
296         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
297         if (DBG) Log.d(TAG, "disableOptionalCodecs()");
298         mStateMachine.disableOptionalCodecs();
299     }
300 
getSupportsOptionalCodecs(BluetoothDevice device)301     public int getSupportsOptionalCodecs(BluetoothDevice device) {
302         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
303         int support = Settings.Global.getInt(getContentResolver(),
304                 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
305                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
306         return support;
307     }
308 
setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport)309     public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) {
310         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
311         int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED
312                                 : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
313         Settings.Global.putInt(getContentResolver(),
314                 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
315                 value);
316     }
317 
getOptionalCodecsEnabled(BluetoothDevice device)318     public int getOptionalCodecsEnabled(BluetoothDevice device) {
319         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
320         return Settings.Global.getInt(getContentResolver(),
321                 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
322                 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
323     }
324 
setOptionalCodecsEnabled(BluetoothDevice device, int value)325     public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
326         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
327         if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
328                 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
329                 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
330             Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value);
331             return;
332         }
333         Settings.Global.putInt(getContentResolver(),
334                 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
335                 value);
336     }
337 
338     //Binder object: Must be static class or memory leak may occur
339     private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
340         implements IProfileServiceBinder {
341         private A2dpService mService;
342 
getService()343         private A2dpService getService() {
344             if (!Utils.checkCaller()) {
345                 Log.w(TAG,"A2dp call not allowed for non-active user");
346                 return null;
347             }
348 
349             if (mService != null && mService.isAvailable()) {
350                 return mService;
351             }
352             return null;
353         }
354 
BluetoothA2dpBinder(A2dpService svc)355         BluetoothA2dpBinder(A2dpService svc) {
356             mService = svc;
357         }
358 
cleanup()359         public boolean cleanup()  {
360             mService = null;
361             return true;
362         }
363 
connect(BluetoothDevice device)364         public boolean connect(BluetoothDevice device) {
365             A2dpService service = getService();
366             if (service == null) return false;
367             return service.connect(device);
368         }
369 
disconnect(BluetoothDevice device)370         public boolean disconnect(BluetoothDevice device) {
371             A2dpService service = getService();
372             if (service == null) return false;
373             return service.disconnect(device);
374         }
375 
getConnectedDevices()376         public List<BluetoothDevice> getConnectedDevices() {
377             A2dpService service = getService();
378             if (service == null) return new ArrayList<BluetoothDevice>(0);
379             return service.getConnectedDevices();
380         }
381 
getDevicesMatchingConnectionStates(int[] states)382         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
383             A2dpService service = getService();
384             if (service == null) return new ArrayList<BluetoothDevice>(0);
385             return service.getDevicesMatchingConnectionStates(states);
386         }
387 
getConnectionState(BluetoothDevice device)388         public int getConnectionState(BluetoothDevice device) {
389             A2dpService service = getService();
390             if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
391             return service.getConnectionState(device);
392         }
393 
setPriority(BluetoothDevice device, int priority)394         public boolean setPriority(BluetoothDevice device, int priority) {
395             A2dpService service = getService();
396             if (service == null) return false;
397             return service.setPriority(device, priority);
398         }
399 
getPriority(BluetoothDevice device)400         public int getPriority(BluetoothDevice device) {
401             A2dpService service = getService();
402             if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
403             return service.getPriority(device);
404         }
405 
isAvrcpAbsoluteVolumeSupported()406         public boolean isAvrcpAbsoluteVolumeSupported() {
407             A2dpService service = getService();
408             if (service == null) return false;
409             return service.isAvrcpAbsoluteVolumeSupported();
410         }
411 
adjustAvrcpAbsoluteVolume(int direction)412         public void adjustAvrcpAbsoluteVolume(int direction) {
413             A2dpService service = getService();
414             if (service == null) return;
415             service.adjustAvrcpAbsoluteVolume(direction);
416         }
417 
setAvrcpAbsoluteVolume(int volume)418         public void setAvrcpAbsoluteVolume(int volume) {
419             A2dpService service = getService();
420             if (service == null) return;
421             service.setAvrcpAbsoluteVolume(volume);
422         }
423 
isA2dpPlaying(BluetoothDevice device)424         public boolean isA2dpPlaying(BluetoothDevice device) {
425             A2dpService service = getService();
426             if (service == null) return false;
427             return service.isA2dpPlaying(device);
428         }
429 
getCodecStatus()430         public BluetoothCodecStatus getCodecStatus() {
431             A2dpService service = getService();
432             if (service == null) return null;
433             return service.getCodecStatus();
434         }
435 
setCodecConfigPreference(BluetoothCodecConfig codecConfig)436         public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
437             A2dpService service = getService();
438             if (service == null) return;
439             service.setCodecConfigPreference(codecConfig);
440         }
441 
enableOptionalCodecs()442         public void enableOptionalCodecs() {
443             A2dpService service = getService();
444             if (service == null) return;
445             service.enableOptionalCodecs();
446         }
447 
disableOptionalCodecs()448         public void disableOptionalCodecs() {
449             A2dpService service = getService();
450             if (service == null) return;
451             service.disableOptionalCodecs();
452         }
453 
supportsOptionalCodecs(BluetoothDevice device)454         public int supportsOptionalCodecs(BluetoothDevice device) {
455             A2dpService service = getService();
456             if (service == null) return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
457             return service.getSupportsOptionalCodecs(device);
458         }
459 
getOptionalCodecsEnabled(BluetoothDevice device)460         public int getOptionalCodecsEnabled(BluetoothDevice device) {
461             A2dpService service = getService();
462             if (service == null) return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
463             return service.getOptionalCodecsEnabled(device);
464         }
465 
setOptionalCodecsEnabled(BluetoothDevice device, int value)466         public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
467             A2dpService service = getService();
468             if (service == null) return;
469             service.setOptionalCodecsEnabled(device, value);
470         }
471     };
472 
473     @Override
dump(StringBuilder sb)474     public void dump(StringBuilder sb) {
475         super.dump(sb);
476         if (mStateMachine != null) {
477             mStateMachine.dump(sb);
478         }
479         if (mAvrcp != null) {
480             mAvrcp.dump(sb);
481         }
482     }
483 }
484