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