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.BluetoothDevice;
     20 import android.bluetooth.BluetoothProfile;
     21 import android.bluetooth.BluetoothUuid;
     22 import android.bluetooth.IBluetoothA2dp;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.os.ParcelUuid;
     26 import android.provider.Settings;
     27 import android.util.Log;
     28 import com.android.bluetooth.avrcp.Avrcp;
     29 import com.android.bluetooth.btservice.ProfileService;
     30 import com.android.bluetooth.Utils;
     31 import java.util.ArrayList;
     32 import java.util.Iterator;
     33 import java.util.List;
     34 import java.util.Map;
     35 
     36 /**
     37  * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
     38  * @hide
     39  */
     40 public class A2dpService extends ProfileService {
     41     private static final boolean DBG = false;
     42     private static final String TAG="A2dpService";
     43 
     44     private A2dpStateMachine mStateMachine;
     45     private Avrcp mAvrcp;
     46     private static A2dpService sAd2dpService;
     47     static final ParcelUuid[] A2DP_SOURCE_UUID = {
     48         BluetoothUuid.AudioSource
     49     };
     50     static final ParcelUuid[] A2DP_SOURCE_SINK_UUIDS = {
     51         BluetoothUuid.AudioSource,
     52         BluetoothUuid.AudioSink
     53     };
     54 
     55     protected String getName() {
     56         return TAG;
     57     }
     58 
     59     protected IProfileServiceBinder initBinder() {
     60         return new BluetoothA2dpBinder(this);
     61     }
     62 
     63     protected boolean start() {
     64         mAvrcp = Avrcp.make(this);
     65         mStateMachine = A2dpStateMachine.make(this, this);
     66         setA2dpService(this);
     67         return true;
     68     }
     69 
     70     protected boolean stop() {
     71         if (mStateMachine != null) {
     72             mStateMachine.doQuit();
     73         }
     74         if (mAvrcp != null) {
     75             mAvrcp.doQuit();
     76         }
     77         return true;
     78     }
     79 
     80     protected boolean cleanup() {
     81         if (mStateMachine!= null) {
     82             mStateMachine.cleanup();
     83         }
     84         if (mAvrcp != null) {
     85             mAvrcp.cleanup();
     86             mAvrcp = null;
     87         }
     88         clearA2dpService();
     89         return true;
     90     }
     91 
     92     //API Methods
     93 
     94     public static synchronized A2dpService getA2dpService(){
     95         if (sAd2dpService != null && sAd2dpService.isAvailable()) {
     96             if (DBG) Log.d(TAG, "getA2DPService(): returning " + sAd2dpService);
     97             return sAd2dpService;
     98         }
     99         if (DBG)  {
    100             if (sAd2dpService == null) {
    101                 Log.d(TAG, "getA2dpService(): service is NULL");
    102             } else if (!(sAd2dpService.isAvailable())) {
    103                 Log.d(TAG,"getA2dpService(): service is not available");
    104             }
    105         }
    106         return null;
    107     }
    108 
    109     private static synchronized void setA2dpService(A2dpService instance) {
    110         if (instance != null && instance.isAvailable()) {
    111             if (DBG) Log.d(TAG, "setA2dpService(): set to: " + sAd2dpService);
    112             sAd2dpService = instance;
    113         } else {
    114             if (DBG)  {
    115                 if (sAd2dpService == null) {
    116                     Log.d(TAG, "setA2dpService(): service not available");
    117                 } else if (!sAd2dpService.isAvailable()) {
    118                     Log.d(TAG,"setA2dpService(): service is cleaning up");
    119                 }
    120             }
    121         }
    122     }
    123 
    124     private static synchronized void clearA2dpService() {
    125         sAd2dpService = null;
    126     }
    127 
    128     public boolean connect(BluetoothDevice device) {
    129         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    130                                        "Need BLUETOOTH ADMIN permission");
    131 
    132         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
    133             return false;
    134         }
    135         ParcelUuid[] featureUuids = device.getUuids();
    136         if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) &&
    137             !(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {
    138             Log.e(TAG,"Remote does not have A2dp Sink UUID");
    139             return false;
    140         }
    141 
    142         int connectionState = mStateMachine.getConnectionState(device);
    143         if (connectionState == BluetoothProfile.STATE_CONNECTED ||
    144             connectionState == BluetoothProfile.STATE_CONNECTING) {
    145             return false;
    146         }
    147 
    148         mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
    149         return true;
    150     }
    151 
    152     boolean disconnect(BluetoothDevice device) {
    153         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    154                                        "Need BLUETOOTH ADMIN permission");
    155         int connectionState = mStateMachine.getConnectionState(device);
    156         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    157             connectionState != BluetoothProfile.STATE_CONNECTING) {
    158             return false;
    159         }
    160 
    161         mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device);
    162         return true;
    163     }
    164 
    165     public List<BluetoothDevice> getConnectedDevices() {
    166         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    167         return mStateMachine.getConnectedDevices();
    168     }
    169 
    170     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    171         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    172         return mStateMachine.getDevicesMatchingConnectionStates(states);
    173     }
    174 
    175     int getConnectionState(BluetoothDevice device) {
    176         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    177         return mStateMachine.getConnectionState(device);
    178     }
    179 
    180     public boolean setPriority(BluetoothDevice device, int priority) {
    181         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    182                                        "Need BLUETOOTH_ADMIN permission");
    183         Settings.Global.putInt(getContentResolver(),
    184             Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
    185             priority);
    186         if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority);
    187         return true;
    188     }
    189 
    190     public int getPriority(BluetoothDevice device) {
    191         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    192                                        "Need BLUETOOTH_ADMIN permission");
    193         int priority = Settings.Global.getInt(getContentResolver(),
    194             Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
    195             BluetoothProfile.PRIORITY_UNDEFINED);
    196         return priority;
    197     }
    198 
    199     /* Absolute volume implementation */
    200     public boolean isAvrcpAbsoluteVolumeSupported() {
    201         return mAvrcp.isAbsoluteVolumeSupported();
    202     }
    203 
    204     public void adjustAvrcpAbsoluteVolume(int direction) {
    205         mAvrcp.adjustVolume(direction);
    206     }
    207 
    208     public void setAvrcpAbsoluteVolume(int volume) {
    209         mAvrcp.setAbsoluteVolume(volume);
    210     }
    211 
    212     public void setAvrcpAudioState(int state) {
    213         mAvrcp.setA2dpAudioState(state);
    214     }
    215 
    216     public void resetAvrcpBlacklist(BluetoothDevice device) {
    217         if (mAvrcp != null) {
    218             mAvrcp.resetBlackList(device.getAddress());
    219         }
    220     }
    221 
    222     synchronized boolean isA2dpPlaying(BluetoothDevice device) {
    223         enforceCallingOrSelfPermission(BLUETOOTH_PERM,
    224                                        "Need BLUETOOTH permission");
    225         if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")");
    226         return mStateMachine.isPlaying(device);
    227     }
    228 
    229     //Binder object: Must be static class or memory leak may occur
    230     private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
    231         implements IProfileServiceBinder {
    232         private A2dpService mService;
    233 
    234         private A2dpService getService() {
    235             if (!Utils.checkCaller()) {
    236                 Log.w(TAG,"A2dp call not allowed for non-active user");
    237                 return null;
    238             }
    239 
    240             if (mService != null && mService.isAvailable()) {
    241                 return mService;
    242             }
    243             return null;
    244         }
    245 
    246         BluetoothA2dpBinder(A2dpService svc) {
    247             mService = svc;
    248         }
    249 
    250         public boolean cleanup()  {
    251             mService = null;
    252             return true;
    253         }
    254 
    255         public boolean connect(BluetoothDevice device) {
    256             A2dpService service = getService();
    257             if (service == null) return false;
    258             return service.connect(device);
    259         }
    260 
    261         public boolean disconnect(BluetoothDevice device) {
    262             A2dpService service = getService();
    263             if (service == null) return false;
    264             return service.disconnect(device);
    265         }
    266 
    267         public List<BluetoothDevice> getConnectedDevices() {
    268             A2dpService service = getService();
    269             if (service == null) return new ArrayList<BluetoothDevice>(0);
    270             return service.getConnectedDevices();
    271         }
    272 
    273         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    274             A2dpService service = getService();
    275             if (service == null) return new ArrayList<BluetoothDevice>(0);
    276             return service.getDevicesMatchingConnectionStates(states);
    277         }
    278 
    279         public int getConnectionState(BluetoothDevice device) {
    280             A2dpService service = getService();
    281             if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
    282             return service.getConnectionState(device);
    283         }
    284 
    285         public boolean setPriority(BluetoothDevice device, int priority) {
    286             A2dpService service = getService();
    287             if (service == null) return false;
    288             return service.setPriority(device, priority);
    289         }
    290 
    291         public int getPriority(BluetoothDevice device) {
    292             A2dpService service = getService();
    293             if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
    294             return service.getPriority(device);
    295         }
    296 
    297         public boolean isAvrcpAbsoluteVolumeSupported() {
    298             A2dpService service = getService();
    299             if (service == null) return false;
    300             return service.isAvrcpAbsoluteVolumeSupported();
    301         }
    302 
    303         public void adjustAvrcpAbsoluteVolume(int direction) {
    304             A2dpService service = getService();
    305             if (service == null) return;
    306             service.adjustAvrcpAbsoluteVolume(direction);
    307         }
    308 
    309         public void setAvrcpAbsoluteVolume(int volume) {
    310             A2dpService service = getService();
    311             if (service == null) return;
    312             service.setAvrcpAbsoluteVolume(volume);
    313         }
    314 
    315         public boolean isA2dpPlaying(BluetoothDevice device) {
    316             A2dpService service = getService();
    317             if (service == null) return false;
    318             return service.isA2dpPlaying(device);
    319         }
    320     };
    321 
    322     @Override
    323     public void dump(StringBuilder sb) {
    324         super.dump(sb);
    325         if (mStateMachine != null) {
    326             mStateMachine.dump(sb);
    327         }
    328         if (mAvrcp != null) {
    329             mAvrcp.dump(sb);
    330         }
    331     }
    332 }
    333