blob: b0b0154abc90670e9b7d258eceae527f1cb077ac [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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.List;
29
30/**
31 * Public API for controlling the Bluetooth A2DP Profile Service.
32 *
33 * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
34 * Service via IPC.
35 *
36 * Creating a BluetoothA2dp object will initiate a binding with the
37 * BluetoothHeadset service. Users of this object should call close() when they
38 * are finished, so that this proxy object can unbind from the service.
39 *
40 * Currently the BluetoothA2dp service runs in the system server and this
41 * proxy object will be immediately bound to the service on construction.
42 * However this may change in future releases, and error codes such as
43 * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the
44 * proxy object is not yet attached.
45 *
46 * Currently this class provides methods to connect to A2DP audio sinks.
47 *
48 * @hide
49 */
50public class BluetoothA2dp {
51 private static final String TAG = "BluetoothA2dp";
52
53 /** int extra for SINK_STATE_CHANGED_ACTION */
54 public static final String SINK_STATE =
55 "android.bluetooth.a2dp.intent.SINK_STATE";
56 /** int extra for SINK_STATE_CHANGED_ACTION */
57 public static final String SINK_PREVIOUS_STATE =
58 "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE";
59
60 /** Indicates the state of an A2DP audio sink has changed.
61 * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and
62 * BluetoothIntent.ADDRESS extras.
63 */
64 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
65 public static final String SINK_STATE_CHANGED_ACTION =
66 "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED";
67
68 public static final int STATE_DISCONNECTED = 0;
69 public static final int STATE_CONNECTING = 1;
70 public static final int STATE_CONNECTED = 2;
71 public static final int STATE_DISCONNECTING = 3;
72 /** Playing implies connected */
73 public static final int STATE_PLAYING = 4;
74
75 /** Default priority for a2dp devices that should allow incoming
76 * connections */
77 public static final int PRIORITY_AUTO = 100;
78 /** Default priority for a2dp devices that should not allow incoming
79 * connections */
80 public static final int PRIORITY_OFF = 0;
81 private final IBluetoothA2dp mService;
82 private final Context mContext;
83
84 /**
85 * Create a BluetoothA2dp proxy object for interacting with the local
86 * Bluetooth A2DP service.
87 * @param c Context
88 */
89 public BluetoothA2dp(Context c) {
90 mContext = c;
91 IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
92 if (b == null) {
93 throw new RuntimeException("Bluetooth A2DP service not available!");
94 }
95 mService = IBluetoothA2dp.Stub.asInterface(b);
96 }
97
98 /** Initiate a connection to an A2DP sink.
99 * Listen for SINK_STATE_CHANGED_ACTION to find out when the
100 * connection is completed.
101 * @param address Remote BT address.
102 * @return Result code, negative indicates an immediate error.
103 * @hide
104 */
105 public int connectSink(String address) {
106 try {
107 return mService.connectSink(address);
108 } catch (RemoteException e) {
109 Log.w(TAG, "", e);
110 return BluetoothError.ERROR_IPC;
111 }
112 }
113
114 /** Initiate disconnect from an A2DP sink.
115 * Listen for SINK_STATE_CHANGED_ACTION to find out when
116 * disconnect is completed.
117 * @param address Remote BT address.
118 * @return Result code, negative indicates an immediate error.
119 * @hide
120 */
121 public int disconnectSink(String address) {
122 try {
123 return mService.disconnectSink(address);
124 } catch (RemoteException e) {
125 Log.w(TAG, "", e);
126 return BluetoothError.ERROR_IPC;
127 }
128 }
129
130 /** Check if a specified A2DP sink is connected.
131 * @param address Remote BT address.
132 * @return True if connected (or playing), false otherwise and on error.
133 * @hide
134 */
135 public boolean isSinkConnected(String address) {
136 int state = getSinkState(address);
137 return state == STATE_CONNECTED || state == STATE_PLAYING;
138 }
139
140 /** Check if any A2DP sink is connected.
141 * @return a List of connected A2DP sinks, or null on error.
142 * @hide
143 */
144 public List<String> listConnectedSinks() {
145 try {
146 return mService.listConnectedSinks();
147 } catch (RemoteException e) {
148 Log.w(TAG, "", e);
149 return null;
150 }
151 }
152
153 /** Get the state of an A2DP sink
154 * @param address Remote BT address.
155 * @return State code, or negative on error
156 * @hide
157 */
158 public int getSinkState(String address) {
159 try {
160 return mService.getSinkState(address);
161 } catch (RemoteException e) {
162 Log.w(TAG, "", e);
163 return BluetoothError.ERROR_IPC;
164 }
165 }
166
167 /**
168 * Set priority of a2dp sink.
169 * Priority is a non-negative integer. By default paired sinks will have
170 * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
171 * Sinks with priority greater than zero will accept incoming connections
172 * (if no sink is currently connected).
173 * Priority for unpaired sink must be PRIORITY_NONE.
174 * @param address Paired sink
175 * @param priority Integer priority, for example PRIORITY_AUTO or
176 * PRIORITY_NONE
177 * @return Result code, negative indicates an error
178 */
179 public int setSinkPriority(String address, int priority) {
180 try {
181 return mService.setSinkPriority(address, priority);
182 } catch (RemoteException e) {
183 Log.w(TAG, "", e);
184 return BluetoothError.ERROR_IPC;
185 }
186 }
187
188 /**
189 * Get priority of a2dp sink.
190 * @param address Sink
191 * @return non-negative priority, or negative error code on error.
192 */
193 public int getSinkPriority(String address) {
194 try {
195 return mService.getSinkPriority(address);
196 } catch (RemoteException e) {
197 Log.w(TAG, "", e);
198 return BluetoothError.ERROR_IPC;
199 }
200 }
201
202 /**
203 * Check class bits for possible A2DP Sink support.
204 * This is a simple heuristic that tries to guess if a device with the
205 * given class bits might be a A2DP Sink. It is not accurate for all
206 * devices. It tries to err on the side of false positives.
207 * @return True if this device might be a A2DP sink
208 */
209 public static boolean doesClassMatchSink(int btClass) {
210 if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
211 return true;
212 }
213 // By the A2DP spec, sinks must indicate the RENDER service.
214 // However we found some that do not (Chordette). So lets also
215 // match on some other class bits.
216 switch (BluetoothClass.Device.getDevice(btClass)) {
217 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
218 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
219 case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
220 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
221 return true;
222 default:
223 return false;
224 }
225 }
226
227 /** Helper for converting a state to a string.
228 * For debug use only - strings are not internationalized.
229 * @hide
230 */
231 public static String stateToString(int state) {
232 switch (state) {
233 case STATE_DISCONNECTED:
234 return "disconnected";
235 case STATE_CONNECTING:
236 return "connecting";
237 case STATE_CONNECTED:
238 return "connected";
239 case STATE_DISCONNECTING:
240 return "disconnecting";
241 case STATE_PLAYING:
242 return "playing";
243 default:
244 return "<unknown state " + state + ">";
245 }
246 }
247}