blob: 61b43036ef3d16921d020087045253edbf3494b5 [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:
53 * {@link #EXTRA_STATE} - The current state of the profile.
54 * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
55 * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
56 *
57 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
58 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
59 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
60 *
61 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 */
63 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070064 public static final String ACTION_CONNECTION_STATE_CHANGED =
65 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070067 /**
68 * Intent used to broadcast the change in the Playing state of the A2DP
69 * profile.
70 *
71 * <p>This intent will have 3 extras:
72 * {@link #EXTRA_STATE} - The current state of the profile.
73 * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
74 * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
75 *
76 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
77 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
78 *
79 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
80 */
81 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
82 public static final String ACTION_PLAYING_STATE_CHANGED =
83 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070085 /**
86 * A2DP sink device is streaming music. This state can be one of
87 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
88 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
89 */
90 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -070091
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070092 /**
93 * A2DP sink device is NOT streaming music. This state can be one of
94 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
95 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
96 */
97 public static final int STATE_NOT_PLAYING = 11;
98
99 private ServiceListener mServiceListener;
100 private IBluetoothA2dp mService;
101 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
103 /**
104 * Create a BluetoothA2dp proxy object for interacting with the local
105 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700106 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700108 /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700110 mServiceListener = l;
111 mAdapter = BluetoothAdapter.getDefaultAdapter();
Michael Chan081f58a2009-09-30 17:59:40 -0700112 if (b != null) {
113 mService = IBluetoothA2dp.Stub.asInterface(b);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700114 if (mServiceListener != null) {
115 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this);
116 }
Michael Chan081f58a2009-09-30 17:59:40 -0700117 } else {
118 Log.w(TAG, "Bluetooth A2DP service not available!");
119
120 // Instead of throwing an exception which prevents people from going
121 // into Wireless settings in the emulator. Let it crash later when it is actually used.
122 mService = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 }
125
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700126 /**
127 * {@inheritDoc}
128 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700130 public boolean connect(BluetoothDevice device) {
131 if (DBG) log("connect(" + device + ")");
132 if (mService != null && isEnabled() &&
133 isValidDevice(device)) {
134 try {
135 return mService.connect(device);
136 } catch (RemoteException e) {
137 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
138 return false;
139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700141 if (mService == null) Log.w(TAG, "Proxy not attached to service");
142 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 }
144
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700145 /**
146 * {@inheritDoc}
147 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700149 public boolean disconnect(BluetoothDevice device) {
150 if (DBG) log("disconnect(" + device + ")");
151 if (mService != null && isEnabled() &&
152 isValidDevice(device)) {
153 try {
154 return mService.disconnect(device);
155 } catch (RemoteException e) {
156 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
157 return false;
158 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700160 if (mService == null) Log.w(TAG, "Proxy not attached to service");
161 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 }
163
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700164 /**
165 * {@inheritDoc}
166 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700167 public List<BluetoothDevice> getConnectedDevices() {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700168 if (DBG) log("getConnectedDevices()");
169 if (mService != null && isEnabled()) {
170 try {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700171 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700172 } catch (RemoteException e) {
173 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700174 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700175 }
176 }
177 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700178 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700179 }
180
181 /**
182 * {@inheritDoc}
183 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700184 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700185 if (DBG) log("getDevicesMatchingStates()");
186 if (mService != null && isEnabled()) {
187 try {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700188 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700189 } catch (RemoteException e) {
190 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700191 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700192 }
193 }
194 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700195 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700196 }
197
198 /**
199 * {@inheritDoc}
200 */
201 public int getConnectionState(BluetoothDevice device) {
202 if (DBG) log("getState(" + device + ")");
203 if (mService != null && isEnabled()
204 && isValidDevice(device)) {
205 try {
206 return mService.getConnectionState(device);
207 } catch (RemoteException e) {
208 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
209 return BluetoothProfile.STATE_DISCONNECTED;
210 }
211 }
212 if (mService == null) Log.w(TAG, "Proxy not attached to service");
213 return BluetoothProfile.STATE_DISCONNECTED;
214 }
215
216 /**
217 * {@inheritDoc}
218 * @hide
219 */
220 public boolean setPriority(BluetoothDevice device, int priority) {
221 if (DBG) log("setPriority(" + device + ", " + priority + ")");
222 if (mService != null && isEnabled()
223 && isValidDevice(device)) {
224 if (priority != BluetoothProfile.PRIORITY_OFF &&
225 priority != BluetoothProfile.PRIORITY_ON) {
226 return false;
227 }
228 try {
229 return mService.setPriority(device, priority);
230 } catch (RemoteException e) {
231 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
232 return false;
233 }
234 }
235 if (mService == null) Log.w(TAG, "Proxy not attached to service");
236 return false;
237 }
238
239 /**
240 * {@inheritDoc}
241 * @hide
242 */
243 public int getPriority(BluetoothDevice device) {
244 if (DBG) log("getPriority(" + device + ")");
245 if (mService != null && isEnabled()
246 && isValidDevice(device)) {
247 try {
248 return mService.getPriority(device);
249 } catch (RemoteException e) {
250 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
251 return BluetoothProfile.PRIORITY_OFF;
252 }
253 }
254 if (mService == null) Log.w(TAG, "Proxy not attached to service");
255 return BluetoothProfile.PRIORITY_OFF;
256 }
257
258 /**
259 * Check if A2DP profile is streaming music.
260 *
261 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
262 *
263 * @param device BluetoothDevice device
264 */
265 public boolean isA2dpPlaying(BluetoothDevice device) {
266 if (mService != null && isEnabled()
267 && isValidDevice(device)) {
268 try {
269 return mService.isA2dpPlaying(device);
270 } catch (RemoteException e) {
271 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
272 return false;
273 }
274 }
275 if (mService == null) Log.w(TAG, "Proxy not attached to service");
276 return false;
277 }
278
279 /**
280 * Initiate suspend from an A2DP sink.
281 *
282 * <p> This API will return false in scenarios like the A2DP
283 * device is not in connected state etc. When this API returns,
284 * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
285 * intent will be broadcasted with the state. Users can get the
286 * state of the A2DP device from this intent.
287 *
288 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
289 *
290 * @param device Remote A2DP sink
291 * @return false on immediate error,
292 * true otherwise
293 * @hide
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800294 */
Nick Pellye766eae2009-09-30 12:06:28 -0700295 public boolean suspendSink(BluetoothDevice device) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700296 if (mService != null && isEnabled()
297 && isValidDevice(device)) {
298 try {
299 return mService.suspendSink(device);
300 } catch (RemoteException e) {
301 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
302 return false;
303 }
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800304 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700305 if (mService == null) Log.w(TAG, "Proxy not attached to service");
306 return false;
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800307 }
308
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700309 /**
310 * Initiate resume from a suspended A2DP sink.
311 *
312 * <p> This API will return false in scenarios like the A2DP
313 * device is not in suspended state etc. When this API returns,
314 * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
315 * intent will be broadcasted with the state. Users can get the
316 * state of the A2DP device from this intent.
317 *
318 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
319 *
320 * @param device Remote A2DP sink
321 * @return false on immediate error,
322 * true otherwise
323 * @hide
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800324 */
Nick Pellye766eae2009-09-30 12:06:28 -0700325 public boolean resumeSink(BluetoothDevice device) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700326 if (mService != null && isEnabled()
327 && isValidDevice(device)) {
328 try {
329 return mService.resumeSink(device);
330 } catch (RemoteException e) {
331 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
332 return false;
333 }
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800334 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700335 if (mService == null) Log.w(TAG, "Proxy not attached to service");
336 return false;
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800337 }
338
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700339 /**
340 * This function checks if the remote device is an AVCRP
341 * target and thus whether we should send volume keys
342 * changes or not.
343 * @hide
344 */
345 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
346 if (isEnabled() && isValidDevice(device)) {
347 ParcelUuid[] uuids = device.getUuids();
348 if (uuids == null) return false;
349
350 for (ParcelUuid uuid: uuids) {
351 if (BluetoothUuid.isAvrcpTarget(uuid)) {
352 return true;
353 }
354 }
355 }
356 return false;
357 }
358
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700359 /**
360 * Helper for converting a state to a string.
361 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 * For debug use only - strings are not internationalized.
363 * @hide
364 */
365 public static String stateToString(int state) {
366 switch (state) {
367 case STATE_DISCONNECTED:
368 return "disconnected";
369 case STATE_CONNECTING:
370 return "connecting";
371 case STATE_CONNECTED:
372 return "connected";
373 case STATE_DISCONNECTING:
374 return "disconnecting";
375 case STATE_PLAYING:
376 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700377 case STATE_NOT_PLAYING:
378 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 default:
380 return "<unknown state " + state + ">";
381 }
382 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800383
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700384 private boolean isEnabled() {
385 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
386 return false;
387 }
388
389 private boolean isValidDevice(BluetoothDevice device) {
390 if (device == null) return false;
391
392 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
393 return false;
394 }
395
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800396 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700397 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800398 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399}