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