Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2008 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 android.bluetooth;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.server.BluetoothA2dpService;
     22 import android.content.Context;
     23 import android.os.ServiceManager;
     24 import android.os.RemoteException;
     25 import android.os.IBinder;
     26 import android.util.Log;
     27 
     28 import java.util.Arrays;
     29 import java.util.Collections;
     30 import java.util.Set;
     31 import java.util.HashSet;
     32 
     33 /**
     34  * Public API for controlling the Bluetooth A2DP Profile Service.
     35  *
     36  * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
     37  * Service via IPC.
     38  *
     39  * Creating a BluetoothA2dp object will initiate a binding with the
     40  * BluetoothHeadset service. Users of this object should call close() when they
     41  * are finished, so that this proxy object can unbind from the service.
     42  *
     43  * Currently the BluetoothA2dp service runs in the system server and this
     44  * proxy object will be immediately bound to the service on construction.
     45  *
     46  * Currently this class provides methods to connect to A2DP audio sinks.
     47  *
     48  * @hide
     49  */
     50 public final class BluetoothA2dp {
     51     private static final String TAG = "BluetoothA2dp";
     52     private static final boolean DBG = false;
     53 
     54     /** int extra for ACTION_SINK_STATE_CHANGED */
     55     public static final String EXTRA_SINK_STATE =
     56         "android.bluetooth.a2dp.extra.SINK_STATE";
     57     /** int extra for ACTION_SINK_STATE_CHANGED */
     58     public static final String EXTRA_PREVIOUS_SINK_STATE =
     59         "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE";
     60 
     61     /** Indicates the state of an A2DP audio sink has changed.
     62      * This intent will always contain EXTRA_SINK_STATE,
     63      * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE
     64      * extras.
     65      */
     66     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     67     public static final String ACTION_SINK_STATE_CHANGED =
     68         "android.bluetooth.a2dp.action.SINK_STATE_CHANGED";
     69 
     70     public static final int STATE_DISCONNECTED = 0;
     71     public static final int STATE_CONNECTING   = 1;
     72     public static final int STATE_CONNECTED    = 2;
     73     public static final int STATE_DISCONNECTING = 3;
     74     /** Playing implies connected */
     75     public static final int STATE_PLAYING    = 4;
     76 
     77     /** Default priority for a2dp devices that we try to auto-connect
     78      * and allow incoming connections */
     79     public static final int PRIORITY_AUTO_CONNECT = 1000;
     80     /** Default priority for a2dp devices that should allow incoming
     81      * connections */
     82     public static final int PRIORITY_ON = 100;
     83     /** Default priority for a2dp devices that should not allow incoming
     84      * connections */
     85     public static final int PRIORITY_OFF = 0;
     86     /** Default priority when not set or when the device is unpaired */
     87     public static final int PRIORITY_UNDEFINED = -1;
     88 
     89     private final IBluetoothA2dp mService;
     90     private final Context mContext;
     91 
     92     /**
     93      * Create a BluetoothA2dp proxy object for interacting with the local
     94      * Bluetooth A2DP service.
     95      * @param c Context
     96      */
     97     public BluetoothA2dp(Context c) {
     98         mContext = c;
     99 
    100         IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
    101         if (b != null) {
    102             mService = IBluetoothA2dp.Stub.asInterface(b);
    103         } else {
    104             Log.w(TAG, "Bluetooth A2DP service not available!");
    105 
    106             // Instead of throwing an exception which prevents people from going
    107             // into Wireless settings in the emulator. Let it crash later when it is actually used.
    108             mService = null;
    109         }
    110     }
    111 
    112     /** Initiate a connection to an A2DP sink.
    113      *  Listen for SINK_STATE_CHANGED_ACTION to find out when the
    114      *  connection is completed.
    115      *  @param device Remote BT device.
    116      *  @return false on immediate error, true otherwise
    117      *  @hide
    118      */
    119     public boolean connectSink(BluetoothDevice device) {
    120         if (DBG) log("connectSink(" + device + ")");
    121         try {
    122             return mService.connectSink(device);
    123         } catch (RemoteException e) {
    124             Log.e(TAG, "", e);
    125             return false;
    126         }
    127     }
    128 
    129     /** Initiate disconnect from an A2DP sink.
    130      *  Listen for SINK_STATE_CHANGED_ACTION to find out when
    131      *  disconnect is completed.
    132      *  @param device Remote BT device.
    133      *  @return false on immediate error, true otherwise
    134      *  @hide
    135      */
    136     public boolean disconnectSink(BluetoothDevice device) {
    137         if (DBG) log("disconnectSink(" + device + ")");
    138         try {
    139             return mService.disconnectSink(device);
    140         } catch (RemoteException e) {
    141             Log.e(TAG, "", e);
    142             return false;
    143         }
    144     }
    145 
    146     /** Initiate suspend from an A2DP sink.
    147      *  Listen for SINK_STATE_CHANGED_ACTION to find out when
    148      *  suspend is completed.
    149      *  @param device Remote BT device.
    150      *  @return false on immediate error, true otherwise
    151      *  @hide
    152      */
    153     public boolean suspendSink(BluetoothDevice device) {
    154         try {
    155             return mService.suspendSink(device);
    156         } catch (RemoteException e) {
    157             Log.e(TAG, "", e);
    158             return false;
    159         }
    160     }
    161 
    162     /** Initiate resume from an suspended A2DP sink.
    163      *  Listen for SINK_STATE_CHANGED_ACTION to find out when
    164      *  resume is completed.
    165      *  @param device Remote BT device.
    166      *  @return false on immediate error, true otherwise
    167      *  @hide
    168      */
    169     public boolean resumeSink(BluetoothDevice device) {
    170         try {
    171             return mService.resumeSink(device);
    172         } catch (RemoteException e) {
    173             Log.e(TAG, "", e);
    174             return false;
    175         }
    176     }
    177 
    178     /** Check if a specified A2DP sink is connected.
    179      *  @param device Remote BT device.
    180      *  @return True if connected (or playing), false otherwise and on error.
    181      *  @hide
    182      */
    183     public boolean isSinkConnected(BluetoothDevice device) {
    184         if (DBG) log("isSinkConnected(" + device + ")");
    185         int state = getSinkState(device);
    186         return state == STATE_CONNECTED || state == STATE_PLAYING;
    187     }
    188 
    189     /** Check if any A2DP sink is connected.
    190      * @return a unmodifiable set of connected A2DP sinks, or null on error.
    191      * @hide
    192      */
    193     public Set<BluetoothDevice> getConnectedSinks() {
    194         if (DBG) log("getConnectedSinks()");
    195         try {
    196             return Collections.unmodifiableSet(
    197                     new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
    198         } catch (RemoteException e) {
    199             Log.e(TAG, "", e);
    200             return null;
    201         }
    202     }
    203 
    204     /** Check if any A2DP sink is in Non Disconnected state
    205      * i.e playing, connected, connecting, disconnecting.
    206      * @return a unmodifiable set of connected A2DP sinks, or null on error.
    207      * @hide
    208      */
    209     public Set<BluetoothDevice> getNonDisconnectedSinks() {
    210         if (DBG) log("getNonDisconnectedSinks()");
    211         try {
    212             return Collections.unmodifiableSet(
    213                     new HashSet<BluetoothDevice>(Arrays.asList(mService.getNonDisconnectedSinks())));
    214         } catch (RemoteException e) {
    215             Log.e(TAG, "", e);
    216             return null;
    217         }
    218     }
    219 
    220     /** Get the state of an A2DP sink
    221      *  @param device Remote BT device.
    222      *  @return State code, one of STATE_
    223      *  @hide
    224      */
    225     public int getSinkState(BluetoothDevice device) {
    226         if (DBG) log("getSinkState(" + device + ")");
    227         try {
    228             return mService.getSinkState(device);
    229         } catch (RemoteException e) {
    230             Log.e(TAG, "", e);
    231             return BluetoothA2dp.STATE_DISCONNECTED;
    232         }
    233     }
    234 
    235     /**
    236      * Set priority of a2dp sink.
    237      * Priority is a non-negative integer. By default paired sinks will have
    238      * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
    239      * Sinks with priority greater than zero will accept incoming connections
    240      * (if no sink is currently connected).
    241      * Priority for unpaired sink must be PRIORITY_NONE.
    242      * @param device Paired sink
    243      * @param priority Integer priority, for example PRIORITY_AUTO or
    244      *                 PRIORITY_NONE
    245      * @return true if priority is set, false on error
    246      */
    247     public boolean setSinkPriority(BluetoothDevice device, int priority) {
    248         if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
    249         try {
    250             return mService.setSinkPriority(device, priority);
    251         } catch (RemoteException e) {
    252             Log.e(TAG, "", e);
    253             return false;
    254         }
    255     }
    256 
    257     /**
    258      * Get priority of a2dp sink.
    259      * @param device Sink
    260      * @return non-negative priority, or negative error code on error.
    261      */
    262     public int getSinkPriority(BluetoothDevice device) {
    263         if (DBG) log("getSinkPriority(" + device + ")");
    264         try {
    265             return mService.getSinkPriority(device);
    266         } catch (RemoteException e) {
    267             Log.e(TAG, "", e);
    268             return PRIORITY_OFF;
    269         }
    270     }
    271 
    272     /**
    273      * Allow or disallow incoming connection
    274      * @param device Sink
    275      * @param value True / False
    276      * @return Success or Failure of the binder call.
    277      */
    278     public boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
    279         if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")");
    280         try {
    281             return mService.allowIncomingConnect(device, value);
    282         } catch (RemoteException e) {
    283             Log.e(TAG, "", e);
    284             return false;
    285         }
    286     }
    287 
    288     /** Helper for converting a state to a string.
    289      * For debug use only - strings are not internationalized.
    290      * @hide
    291      */
    292     public static String stateToString(int state) {
    293         switch (state) {
    294         case STATE_DISCONNECTED:
    295             return "disconnected";
    296         case STATE_CONNECTING:
    297             return "connecting";
    298         case STATE_CONNECTED:
    299             return "connected";
    300         case STATE_DISCONNECTING:
    301             return "disconnecting";
    302         case STATE_PLAYING:
    303             return "playing";
    304         default:
    305             return "<unknown state " + state + ">";
    306         }
    307     }
    308 
    309     private static void log(String msg) {
    310         Log.d(TAG, msg);
    311     }
    312 }
    313