blob: 9246a1035087a275ccfad453aebc0fe7eedf6416 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070023import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070024import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.server.BluetoothA2dpService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.util.Log;
28
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070029import java.util.ArrayList;
30import java.util.List;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070031
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070034 * This class provides the public APIs to control the Bluetooth A2DP
35 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070037 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
38 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
39 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070041 * <p> Android only supports one connected Bluetooth A2dp device at a time.
42 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070044public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 private static final String TAG = "BluetoothA2dp";
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -080046 private static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070048 /**
49 * Intent used to broadcast the change in connection state of the A2DP
50 * profile.
51 *
52 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080053 * <ul>
54 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
55 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
56 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
57 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070058 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080059 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070060 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
61 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
62 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080063 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
64 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 */
66 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070067 public static final String ACTION_CONNECTION_STATE_CHANGED =
68 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070070 /**
71 * Intent used to broadcast the change in the Playing state of the A2DP
72 * profile.
73 *
74 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080075 * <ul>
76 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
77 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080078 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080079 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070080 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080081 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070082 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
83 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080084 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
85 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070086 */
87 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
88 public static final String ACTION_PLAYING_STATE_CHANGED =
89 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070091 /**
92 * A2DP sink device is streaming music. This state can be one of
93 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
94 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
95 */
96 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -070097
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070098 /**
99 * A2DP sink device is NOT streaming music. This state can be one of
100 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
101 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
102 */
103 public static final int STATE_NOT_PLAYING = 11;
104
105 private ServiceListener mServiceListener;
106 private IBluetoothA2dp mService;
107 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
109 /**
110 * Create a BluetoothA2dp proxy object for interacting with the local
111 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700112 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700114 /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700116 mServiceListener = l;
117 mAdapter = BluetoothAdapter.getDefaultAdapter();
Michael Chan081f58a2009-09-30 17:59:40 -0700118 if (b != null) {
119 mService = IBluetoothA2dp.Stub.asInterface(b);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700120 if (mServiceListener != null) {
121 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this);
122 }
Michael Chan081f58a2009-09-30 17:59:40 -0700123 } else {
124 Log.w(TAG, "Bluetooth A2DP service not available!");
125
126 // Instead of throwing an exception which prevents people from going
127 // into Wireless settings in the emulator. Let it crash later when it is actually used.
128 mService = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 }
131
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700132 /**
133 * {@inheritDoc}
134 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700136 public boolean connect(BluetoothDevice device) {
137 if (DBG) log("connect(" + device + ")");
138 if (mService != null && isEnabled() &&
139 isValidDevice(device)) {
140 try {
141 return mService.connect(device);
142 } catch (RemoteException e) {
143 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
144 return false;
145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700147 if (mService == null) Log.w(TAG, "Proxy not attached to service");
148 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700151 /**
152 * {@inheritDoc}
153 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700155 public boolean disconnect(BluetoothDevice device) {
156 if (DBG) log("disconnect(" + device + ")");
157 if (mService != null && isEnabled() &&
158 isValidDevice(device)) {
159 try {
160 return mService.disconnect(device);
161 } catch (RemoteException e) {
162 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
163 return false;
164 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700166 if (mService == null) Log.w(TAG, "Proxy not attached to service");
167 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
169
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700170 /**
171 * {@inheritDoc}
172 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700173 public List<BluetoothDevice> getConnectedDevices() {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700174 if (DBG) log("getConnectedDevices()");
175 if (mService != null && isEnabled()) {
176 try {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700177 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700178 } catch (RemoteException e) {
179 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700180 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700181 }
182 }
183 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700184 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700185 }
186
187 /**
188 * {@inheritDoc}
189 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700190 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700191 if (DBG) log("getDevicesMatchingStates()");
192 if (mService != null && isEnabled()) {
193 try {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700194 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700195 } catch (RemoteException e) {
196 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700197 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700198 }
199 }
200 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700201 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700202 }
203
204 /**
205 * {@inheritDoc}
206 */
207 public int getConnectionState(BluetoothDevice device) {
208 if (DBG) log("getState(" + device + ")");
209 if (mService != null && isEnabled()
210 && isValidDevice(device)) {
211 try {
212 return mService.getConnectionState(device);
213 } catch (RemoteException e) {
214 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
215 return BluetoothProfile.STATE_DISCONNECTED;
216 }
217 }
218 if (mService == null) Log.w(TAG, "Proxy not attached to service");
219 return BluetoothProfile.STATE_DISCONNECTED;
220 }
221
222 /**
223 * {@inheritDoc}
224 * @hide
225 */
226 public boolean setPriority(BluetoothDevice device, int priority) {
227 if (DBG) log("setPriority(" + device + ", " + priority + ")");
228 if (mService != null && isEnabled()
229 && isValidDevice(device)) {
230 if (priority != BluetoothProfile.PRIORITY_OFF &&
231 priority != BluetoothProfile.PRIORITY_ON) {
232 return false;
233 }
234 try {
235 return mService.setPriority(device, priority);
236 } catch (RemoteException e) {
237 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
238 return false;
239 }
240 }
241 if (mService == null) Log.w(TAG, "Proxy not attached to service");
242 return false;
243 }
244
245 /**
246 * {@inheritDoc}
247 * @hide
248 */
249 public int getPriority(BluetoothDevice device) {
250 if (DBG) log("getPriority(" + device + ")");
251 if (mService != null && isEnabled()
252 && isValidDevice(device)) {
253 try {
254 return mService.getPriority(device);
255 } catch (RemoteException e) {
256 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
257 return BluetoothProfile.PRIORITY_OFF;
258 }
259 }
260 if (mService == null) Log.w(TAG, "Proxy not attached to service");
261 return BluetoothProfile.PRIORITY_OFF;
262 }
263
264 /**
265 * Check if A2DP profile is streaming music.
266 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800267 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700268 *
269 * @param device BluetoothDevice device
270 */
271 public boolean isA2dpPlaying(BluetoothDevice device) {
272 if (mService != null && isEnabled()
273 && isValidDevice(device)) {
274 try {
275 return mService.isA2dpPlaying(device);
276 } catch (RemoteException e) {
277 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
278 return false;
279 }
280 }
281 if (mService == null) Log.w(TAG, "Proxy not attached to service");
282 return false;
283 }
284
285 /**
286 * Initiate suspend from an A2DP sink.
287 *
288 * <p> This API will return false in scenarios like the A2DP
289 * device is not in connected state etc. When this API returns,
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800290 * true, it is guaranteed that {@link #ACTION_CONNECTION_STATE_CHANGED}
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700291 * intent will be broadcasted with the state. Users can get the
292 * state of the A2DP device from this intent.
293 *
294 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800295 * permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700296 *
297 * @param device Remote A2DP sink
298 * @return false on immediate error,
299 * true otherwise
300 * @hide
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800301 */
Nick Pellye766eae2009-09-30 12:06:28 -0700302 public boolean suspendSink(BluetoothDevice device) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700303 if (mService != null && isEnabled()
304 && isValidDevice(device)) {
305 try {
306 return mService.suspendSink(device);
307 } catch (RemoteException e) {
308 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
309 return false;
310 }
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800311 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700312 if (mService == null) Log.w(TAG, "Proxy not attached to service");
313 return false;
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800314 }
315
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700316 /**
317 * Initiate resume from a suspended A2DP sink.
318 *
319 * <p> This API will return false in scenarios like the A2DP
320 * device is not in suspended state etc. When this API returns,
321 * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
322 * intent will be broadcasted with the state. Users can get the
323 * state of the A2DP device from this intent.
324 *
325 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
326 *
327 * @param device Remote A2DP sink
328 * @return false on immediate error,
329 * true otherwise
330 * @hide
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800331 */
Nick Pellye766eae2009-09-30 12:06:28 -0700332 public boolean resumeSink(BluetoothDevice device) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700333 if (mService != null && isEnabled()
334 && isValidDevice(device)) {
335 try {
336 return mService.resumeSink(device);
337 } catch (RemoteException e) {
338 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
339 return false;
340 }
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800341 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700342 if (mService == null) Log.w(TAG, "Proxy not attached to service");
343 return false;
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800344 }
345
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700346 /**
347 * This function checks if the remote device is an AVCRP
348 * target and thus whether we should send volume keys
349 * changes or not.
350 * @hide
351 */
352 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
353 if (isEnabled() && isValidDevice(device)) {
354 ParcelUuid[] uuids = device.getUuids();
355 if (uuids == null) return false;
356
357 for (ParcelUuid uuid: uuids) {
358 if (BluetoothUuid.isAvrcpTarget(uuid)) {
359 return true;
360 }
361 }
362 }
363 return false;
364 }
365
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700366 /**
367 * Helper for converting a state to a string.
368 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 * For debug use only - strings are not internationalized.
370 * @hide
371 */
372 public static String stateToString(int state) {
373 switch (state) {
374 case STATE_DISCONNECTED:
375 return "disconnected";
376 case STATE_CONNECTING:
377 return "connecting";
378 case STATE_CONNECTED:
379 return "connected";
380 case STATE_DISCONNECTING:
381 return "disconnecting";
382 case STATE_PLAYING:
383 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700384 case STATE_NOT_PLAYING:
385 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 default:
387 return "<unknown state " + state + ">";
388 }
389 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800390
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700391 private boolean isEnabled() {
392 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
393 return false;
394 }
395
396 private boolean isValidDevice(BluetoothDevice device) {
397 if (device == null) return false;
398
399 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
400 return false;
401 }
402
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800403 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700404 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406}