• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 should allow incoming
78      * connections */
79     public static final int PRIORITY_AUTO = 100;
80     /** Default priority for a2dp devices that should not allow incoming
81      * connections */
82     public static final int PRIORITY_OFF = 0;
83 
84     private final IBluetoothA2dp mService;
85     private final Context mContext;
86 
87     /**
88      * Create a BluetoothA2dp proxy object for interacting with the local
89      * Bluetooth A2DP service.
90      * @param c Context
91      */
BluetoothA2dp(Context c)92     public BluetoothA2dp(Context c) {
93         mContext = c;
94 
95         IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
96         if (b != null) {
97             mService = IBluetoothA2dp.Stub.asInterface(b);
98         } else {
99             Log.w(TAG, "Bluetooth A2DP service not available!");
100 
101             // Instead of throwing an exception which prevents people from going
102             // into Wireless settings in the emulator. Let it crash later when it is actually used.
103             mService = null;
104         }
105     }
106 
107     /** Initiate a connection to an A2DP sink.
108      *  Listen for SINK_STATE_CHANGED_ACTION to find out when the
109      *  connection is completed.
110      *  @param device Remote BT device.
111      *  @return false on immediate error, true otherwise
112      *  @hide
113      */
connectSink(BluetoothDevice device)114     public boolean connectSink(BluetoothDevice device) {
115         if (DBG) log("connectSink(" + device + ")");
116         try {
117             return mService.connectSink(device);
118         } catch (RemoteException e) {
119             Log.e(TAG, "", e);
120             return false;
121         }
122     }
123 
124     /** Initiate disconnect from an A2DP sink.
125      *  Listen for SINK_STATE_CHANGED_ACTION to find out when
126      *  disconnect is completed.
127      *  @param device Remote BT device.
128      *  @return false on immediate error, true otherwise
129      *  @hide
130      */
disconnectSink(BluetoothDevice device)131     public boolean disconnectSink(BluetoothDevice device) {
132         if (DBG) log("disconnectSink(" + device + ")");
133         try {
134             return mService.disconnectSink(device);
135         } catch (RemoteException e) {
136             Log.e(TAG, "", e);
137             return false;
138         }
139     }
140 
141     /** Initiate suspend from an A2DP sink.
142      *  Listen for SINK_STATE_CHANGED_ACTION to find out when
143      *  suspend is completed.
144      *  @param device Remote BT device.
145      *  @return false on immediate error, true otherwise
146      *  @hide
147      */
suspendSink(BluetoothDevice device)148     public boolean suspendSink(BluetoothDevice device) {
149         try {
150             return mService.suspendSink(device);
151         } catch (RemoteException e) {
152             Log.e(TAG, "", e);
153             return false;
154         }
155     }
156 
157     /** Initiate resume from an suspended A2DP sink.
158      *  Listen for SINK_STATE_CHANGED_ACTION to find out when
159      *  resume is completed.
160      *  @param device Remote BT device.
161      *  @return false on immediate error, true otherwise
162      *  @hide
163      */
resumeSink(BluetoothDevice device)164     public boolean resumeSink(BluetoothDevice device) {
165         try {
166             return mService.resumeSink(device);
167         } catch (RemoteException e) {
168             Log.e(TAG, "", e);
169             return false;
170         }
171     }
172 
173     /** Check if a specified A2DP sink is connected.
174      *  @param device Remote BT device.
175      *  @return True if connected (or playing), false otherwise and on error.
176      *  @hide
177      */
isSinkConnected(BluetoothDevice device)178     public boolean isSinkConnected(BluetoothDevice device) {
179         if (DBG) log("isSinkConnected(" + device + ")");
180         int state = getSinkState(device);
181         return state == STATE_CONNECTED || state == STATE_PLAYING;
182     }
183 
184     /** Check if any A2DP sink is connected.
185      * @return a unmodifiable set of connected A2DP sinks, or null on error.
186      * @hide
187      */
getConnectedSinks()188     public Set<BluetoothDevice> getConnectedSinks() {
189         if (DBG) log("getConnectedSinks()");
190         try {
191             return Collections.unmodifiableSet(
192                     new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
193         } catch (RemoteException e) {
194             Log.e(TAG, "", e);
195             return null;
196         }
197     }
198 
199     /** Get the state of an A2DP sink
200      *  @param device Remote BT device.
201      *  @return State code, one of STATE_
202      *  @hide
203      */
getSinkState(BluetoothDevice device)204     public int getSinkState(BluetoothDevice device) {
205         if (DBG) log("getSinkState(" + device + ")");
206         try {
207             return mService.getSinkState(device);
208         } catch (RemoteException e) {
209             Log.e(TAG, "", e);
210             return BluetoothA2dp.STATE_DISCONNECTED;
211         }
212     }
213 
214     /**
215      * Set priority of a2dp sink.
216      * Priority is a non-negative integer. By default paired sinks will have
217      * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
218      * Sinks with priority greater than zero will accept incoming connections
219      * (if no sink is currently connected).
220      * Priority for unpaired sink must be PRIORITY_NONE.
221      * @param device Paired sink
222      * @param priority Integer priority, for example PRIORITY_AUTO or
223      *                 PRIORITY_NONE
224      * @return true if priority is set, false on error
225      */
setSinkPriority(BluetoothDevice device, int priority)226     public boolean setSinkPriority(BluetoothDevice device, int priority) {
227         if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
228         try {
229             return mService.setSinkPriority(device, priority);
230         } catch (RemoteException e) {
231             Log.e(TAG, "", e);
232             return false;
233         }
234     }
235 
236     /**
237      * Get priority of a2dp sink.
238      * @param device Sink
239      * @return non-negative priority, or negative error code on error.
240      */
getSinkPriority(BluetoothDevice device)241     public int getSinkPriority(BluetoothDevice device) {
242         if (DBG) log("getSinkPriority(" + device + ")");
243         try {
244             return mService.getSinkPriority(device);
245         } catch (RemoteException e) {
246             Log.e(TAG, "", e);
247             return PRIORITY_OFF;
248         }
249     }
250 
251     /** Helper for converting a state to a string.
252      * For debug use only - strings are not internationalized.
253      * @hide
254      */
stateToString(int state)255     public static String stateToString(int state) {
256         switch (state) {
257         case STATE_DISCONNECTED:
258             return "disconnected";
259         case STATE_CONNECTING:
260             return "connecting";
261         case STATE_CONNECTED:
262             return "connected";
263         case STATE_DISCONNECTING:
264             return "disconnecting";
265         case STATE_PLAYING:
266             return "playing";
267         default:
268             return "<unknown state " + state + ">";
269         }
270     }
271 
log(String msg)272     private static void log(String msg) {
273         Log.d(TAG, msg);
274     }
275 }
276