blob: d308a5c315bd7f8e8b6306719b557b0eab6eb878 [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 Ganesh62c37ef2010-08-24 17:36:13 -070023import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.server.BluetoothA2dpService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.util.Log;
27
Nick Pellybd022f42009-08-14 18:33:38 -070028import java.util.Collections;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070029import java.util.Arrays;
30import java.util.HashMap;
Nick Pellybd022f42009-08-14 18:33:38 -070031import java.util.HashSet;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070032import java.util.Set;
33
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034
35/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070036 * This class provides the public APIs to control the Bluetooth A2DP
37 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070039 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
40 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
41 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070043 * <p> Android only supports one connected Bluetooth A2dp device at a time.
44 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070046public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 private static final String TAG = "BluetoothA2dp";
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -080048 private static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070050 /**
51 * Intent used to broadcast the change in connection state of the A2DP
52 * profile.
53 *
54 * <p>This intent will have 3 extras:
55 * {@link #EXTRA_STATE} - The current state of the profile.
56 * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
57 * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
58 *
59 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
60 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
61 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
62 *
63 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 */
65 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070066 public static final String ACTION_CONNECTION_STATE_CHANGED =
67 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070069 /**
70 * Intent used to broadcast the change in the Playing state of the A2DP
71 * profile.
72 *
73 * <p>This intent will have 3 extras:
74 * {@link #EXTRA_STATE} - The current state of the profile.
75 * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
76 * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
77 *
78 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
79 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
80 *
81 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
82 */
83 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
84 public static final String ACTION_PLAYING_STATE_CHANGED =
85 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070087 /**
88 * A2DP sink device is streaming music. This state can be one of
89 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
90 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
91 */
92 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -070093
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070094 /**
95 * A2DP sink device is NOT streaming music. This state can be one of
96 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
97 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
98 */
99 public static final int STATE_NOT_PLAYING = 11;
100
101 private ServiceListener mServiceListener;
102 private IBluetoothA2dp mService;
103 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104
105 /**
106 * Create a BluetoothA2dp proxy object for interacting with the local
107 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700108 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700110 /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700112 mServiceListener = l;
113 mAdapter = BluetoothAdapter.getDefaultAdapter();
Michael Chan081f58a2009-09-30 17:59:40 -0700114 if (b != null) {
115 mService = IBluetoothA2dp.Stub.asInterface(b);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700116 if (mServiceListener != null) {
117 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this);
118 }
Michael Chan081f58a2009-09-30 17:59:40 -0700119 } else {
120 Log.w(TAG, "Bluetooth A2DP service not available!");
121
122 // Instead of throwing an exception which prevents people from going
123 // into Wireless settings in the emulator. Let it crash later when it is actually used.
124 mService = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 }
127
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700128 /**
129 * {@inheritDoc}
130 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700132 public boolean connect(BluetoothDevice device) {
133 if (DBG) log("connect(" + device + ")");
134 if (mService != null && isEnabled() &&
135 isValidDevice(device)) {
136 try {
137 return mService.connect(device);
138 } catch (RemoteException e) {
139 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
140 return false;
141 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700143 if (mService == null) Log.w(TAG, "Proxy not attached to service");
144 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 }
146
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700147 /**
148 * {@inheritDoc}
149 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700151 public boolean disconnect(BluetoothDevice device) {
152 if (DBG) log("disconnect(" + device + ")");
153 if (mService != null && isEnabled() &&
154 isValidDevice(device)) {
155 try {
156 return mService.disconnect(device);
157 } catch (RemoteException e) {
158 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
159 return false;
160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700162 if (mService == null) Log.w(TAG, "Proxy not attached to service");
163 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 }
165
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700166 /**
167 * {@inheritDoc}
168 */
169 public Set<BluetoothDevice> getConnectedDevices() {
170 if (DBG) log("getConnectedDevices()");
171 if (mService != null && isEnabled()) {
172 try {
173 return toDeviceSet(mService.getConnectedDevices());
174 } catch (RemoteException e) {
175 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
176 return toDeviceSet(new BluetoothDevice[0]);
177 }
178 }
179 if (mService == null) Log.w(TAG, "Proxy not attached to service");
180 return toDeviceSet(new BluetoothDevice[0]);
181 }
182
183 /**
184 * {@inheritDoc}
185 */
186 public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
187 if (DBG) log("getDevicesMatchingStates()");
188 if (mService != null && isEnabled()) {
189 try {
190 return toDeviceSet(mService.getDevicesMatchingConnectionStates(states));
191 } catch (RemoteException e) {
192 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
193 return toDeviceSet(new BluetoothDevice[0]);
194 }
195 }
196 if (mService == null) Log.w(TAG, "Proxy not attached to service");
197 return toDeviceSet(new BluetoothDevice[0]);
198 }
199
200 /**
201 * {@inheritDoc}
202 */
203 public int getConnectionState(BluetoothDevice device) {
204 if (DBG) log("getState(" + device + ")");
205 if (mService != null && isEnabled()
206 && isValidDevice(device)) {
207 try {
208 return mService.getConnectionState(device);
209 } catch (RemoteException e) {
210 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
211 return BluetoothProfile.STATE_DISCONNECTED;
212 }
213 }
214 if (mService == null) Log.w(TAG, "Proxy not attached to service");
215 return BluetoothProfile.STATE_DISCONNECTED;
216 }
217
218 /**
219 * {@inheritDoc}
220 * @hide
221 */
222 public boolean setPriority(BluetoothDevice device, int priority) {
223 if (DBG) log("setPriority(" + device + ", " + priority + ")");
224 if (mService != null && isEnabled()
225 && isValidDevice(device)) {
226 if (priority != BluetoothProfile.PRIORITY_OFF &&
227 priority != BluetoothProfile.PRIORITY_ON) {
228 return false;
229 }
230 try {
231 return mService.setPriority(device, priority);
232 } catch (RemoteException e) {
233 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
234 return false;
235 }
236 }
237 if (mService == null) Log.w(TAG, "Proxy not attached to service");
238 return false;
239 }
240
241 /**
242 * {@inheritDoc}
243 * @hide
244 */
245 public int getPriority(BluetoothDevice device) {
246 if (DBG) log("getPriority(" + device + ")");
247 if (mService != null && isEnabled()
248 && isValidDevice(device)) {
249 try {
250 return mService.getPriority(device);
251 } catch (RemoteException e) {
252 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
253 return BluetoothProfile.PRIORITY_OFF;
254 }
255 }
256 if (mService == null) Log.w(TAG, "Proxy not attached to service");
257 return BluetoothProfile.PRIORITY_OFF;
258 }
259
260 /**
261 * Check if A2DP profile is streaming music.
262 *
263 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
264 *
265 * @param device BluetoothDevice device
266 */
267 public boolean isA2dpPlaying(BluetoothDevice device) {
268 if (mService != null && isEnabled()
269 && isValidDevice(device)) {
270 try {
271 return mService.isA2dpPlaying(device);
272 } catch (RemoteException e) {
273 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
274 return false;
275 }
276 }
277 if (mService == null) Log.w(TAG, "Proxy not attached to service");
278 return false;
279 }
280
281 /**
282 * Initiate suspend from an A2DP sink.
283 *
284 * <p> This API will return false in scenarios like the A2DP
285 * device is not in connected state etc. When this API returns,
286 * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
287 * intent will be broadcasted with the state. Users can get the
288 * state of the A2DP device from this intent.
289 *
290 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
291 *
292 * @param device Remote A2DP sink
293 * @return false on immediate error,
294 * true otherwise
295 * @hide
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800296 */
Nick Pellye766eae2009-09-30 12:06:28 -0700297 public boolean suspendSink(BluetoothDevice device) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700298 if (mService != null && isEnabled()
299 && isValidDevice(device)) {
300 try {
301 return mService.suspendSink(device);
302 } catch (RemoteException e) {
303 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
304 return false;
305 }
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800306 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700307 if (mService == null) Log.w(TAG, "Proxy not attached to service");
308 return false;
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800309 }
310
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700311 /**
312 * Initiate resume from a suspended A2DP sink.
313 *
314 * <p> This API will return false in scenarios like the A2DP
315 * device is not in suspended state etc. When this API returns,
316 * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
317 * intent will be broadcasted with the state. Users can get the
318 * state of the A2DP device from this intent.
319 *
320 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
321 *
322 * @param device Remote A2DP sink
323 * @return false on immediate error,
324 * true otherwise
325 * @hide
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800326 */
Nick Pellye766eae2009-09-30 12:06:28 -0700327 public boolean resumeSink(BluetoothDevice device) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700328 if (mService != null && isEnabled()
329 && isValidDevice(device)) {
330 try {
331 return mService.resumeSink(device);
332 } catch (RemoteException e) {
333 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
334 return false;
335 }
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800336 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700337 if (mService == null) Log.w(TAG, "Proxy not attached to service");
338 return false;
Zhu Lanf9bbe1e2009-06-24 10:51:57 +0800339 }
340
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700341 /**
342 * Helper for converting a state to a string.
343 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 * For debug use only - strings are not internationalized.
345 * @hide
346 */
347 public static String stateToString(int state) {
348 switch (state) {
349 case STATE_DISCONNECTED:
350 return "disconnected";
351 case STATE_CONNECTING:
352 return "connecting";
353 case STATE_CONNECTED:
354 return "connected";
355 case STATE_DISCONNECTING:
356 return "disconnecting";
357 case STATE_PLAYING:
358 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700359 case STATE_NOT_PLAYING:
360 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 default:
362 return "<unknown state " + state + ">";
363 }
364 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800365
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700366 private boolean isEnabled() {
367 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
368 return false;
369 }
370
371 private boolean isValidDevice(BluetoothDevice device) {
372 if (device == null) return false;
373
374 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
375 return false;
376 }
377
378 private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
379 return Collections.unmodifiableSet(
380 new HashSet<BluetoothDevice>(Arrays.asList(devices)));
381 }
382
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800383 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700384 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800385 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386}