blob: c59a5aa85a7b8dc50f1355f8d1f70f99479b14e6 [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;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080021import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080023import android.content.Intent;
24import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070026import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070027import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.Log;
29
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070030import java.util.ArrayList;
31import java.util.List;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
34/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070035 * This class provides the public APIs to control the Bluetooth A2DP
36 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070038 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
39 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
40 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070042 * <p> Android only supports one connected Bluetooth A2dp device at a time.
43 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070045public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080047 private static final boolean DBG = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070049 /**
50 * Intent used to broadcast the change in connection state of the A2DP
51 * profile.
52 *
53 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080054 * <ul>
55 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
56 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
57 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
58 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070059 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080060 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070061 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
62 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
63 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080064 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
65 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 */
67 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070068 public static final String ACTION_CONNECTION_STATE_CHANGED =
69 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070071 /**
72 * Intent used to broadcast the change in the Playing state of the A2DP
73 * profile.
74 *
75 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080076 * <ul>
77 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
78 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080079 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080080 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070081 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080082 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070083 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
84 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080085 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
86 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070087 */
88 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
89 public static final String ACTION_PLAYING_STATE_CHANGED =
90 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070092 /**
93 * A2DP sink device is 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_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -070098
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070099 /**
100 * A2DP sink device is NOT streaming music. This state can be one of
101 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
102 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
103 */
104 public static final int STATE_NOT_PLAYING = 11;
105
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800106 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700107 private ServiceListener mServiceListener;
108 private IBluetoothA2dp mService;
109 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111 /**
112 * Create a BluetoothA2dp proxy object for interacting with the local
113 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700114 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800116 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
117 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700118 mServiceListener = l;
119 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800120 if (!context.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) {
121 Log.e(TAG, "Could not bind to Bluetooth A2DP Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 }
124
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800125 /*package*/ void close() {
126 mServiceListener = null;
127 }
128
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700129 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700130 * Initiate connection to a profile of the remote bluetooth device.
131 *
132 * <p> Currently, the system supports only 1 connection to the
133 * A2DP profile. The API will automatically disconnect connected
134 * devices before connecting.
135 *
136 * <p> This API returns false in scenarios like the profile on the
137 * device is already connected or Bluetooth is not turned on.
138 * When this API returns true, it is guaranteed that
139 * connection state intent for the profile will be broadcasted with
140 * the state. Users can get the connection state of the profile
141 * from this intent.
142 *
143 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
144 * permission.
145 *
146 * @param device Remote Bluetooth Device
147 * @return false on immediate error,
148 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700149 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700151 public boolean connect(BluetoothDevice device) {
152 if (DBG) log("connect(" + device + ")");
153 if (mService != null && isEnabled() &&
154 isValidDevice(device)) {
155 try {
156 return mService.connect(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 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700167 * Initiate disconnection from a profile
168 *
169 * <p> This API will return false in scenarios like the profile on the
170 * Bluetooth device is not in connected state etc. When this API returns,
171 * true, it is guaranteed that the connection state change
172 * intent will be broadcasted with the state. Users can get the
173 * disconnection state of the profile from this intent.
174 *
175 * <p> If the disconnection is initiated by a remote device, the state
176 * will transition from {@link #STATE_CONNECTED} to
177 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
178 * host (local) device the state will transition from
179 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
180 * state {@link #STATE_DISCONNECTED}. The transition to
181 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
182 * two scenarios.
183 *
184 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
185 * permission.
186 *
187 * @param device Remote Bluetooth Device
188 * @return false on immediate error,
189 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700190 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700192 public boolean disconnect(BluetoothDevice device) {
193 if (DBG) log("disconnect(" + device + ")");
194 if (mService != null && isEnabled() &&
195 isValidDevice(device)) {
196 try {
197 return mService.disconnect(device);
198 } catch (RemoteException e) {
199 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
200 return false;
201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700203 if (mService == null) Log.w(TAG, "Proxy not attached to service");
204 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
206
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700207 /**
208 * {@inheritDoc}
209 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700210 public List<BluetoothDevice> getConnectedDevices() {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700211 if (DBG) log("getConnectedDevices()");
212 if (mService != null && isEnabled()) {
213 try {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700214 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700215 } catch (RemoteException e) {
216 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700217 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700218 }
219 }
220 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700221 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700222 }
223
224 /**
225 * {@inheritDoc}
226 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700227 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700228 if (DBG) log("getDevicesMatchingStates()");
229 if (mService != null && isEnabled()) {
230 try {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700231 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700232 } catch (RemoteException e) {
233 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700234 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700235 }
236 }
237 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700238 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700239 }
240
241 /**
242 * {@inheritDoc}
243 */
244 public int getConnectionState(BluetoothDevice device) {
245 if (DBG) log("getState(" + device + ")");
246 if (mService != null && isEnabled()
247 && isValidDevice(device)) {
248 try {
249 return mService.getConnectionState(device);
250 } catch (RemoteException e) {
251 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
252 return BluetoothProfile.STATE_DISCONNECTED;
253 }
254 }
255 if (mService == null) Log.w(TAG, "Proxy not attached to service");
256 return BluetoothProfile.STATE_DISCONNECTED;
257 }
258
259 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700260 * Set priority of the profile
261 *
262 * <p> The device should already be paired.
263 * Priority can be one of {@link #PRIORITY_ON} or
264 * {@link #PRIORITY_OFF},
265 *
266 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
267 * permission.
268 *
269 * @param device Paired bluetooth device
270 * @param priority
271 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700272 * @hide
273 */
274 public boolean setPriority(BluetoothDevice device, int priority) {
275 if (DBG) log("setPriority(" + device + ", " + priority + ")");
276 if (mService != null && isEnabled()
277 && isValidDevice(device)) {
278 if (priority != BluetoothProfile.PRIORITY_OFF &&
279 priority != BluetoothProfile.PRIORITY_ON) {
280 return false;
281 }
282 try {
283 return mService.setPriority(device, priority);
284 } catch (RemoteException e) {
285 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
286 return false;
287 }
288 }
289 if (mService == null) Log.w(TAG, "Proxy not attached to service");
290 return false;
291 }
292
293 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700294 * Get the priority of the profile.
295 *
296 * <p> The priority can be any of:
297 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
298 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
299 *
300 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
301 *
302 * @param device Bluetooth device
303 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700304 * @hide
305 */
306 public int getPriority(BluetoothDevice device) {
307 if (DBG) log("getPriority(" + device + ")");
308 if (mService != null && isEnabled()
309 && isValidDevice(device)) {
310 try {
311 return mService.getPriority(device);
312 } catch (RemoteException e) {
313 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
314 return BluetoothProfile.PRIORITY_OFF;
315 }
316 }
317 if (mService == null) Log.w(TAG, "Proxy not attached to service");
318 return BluetoothProfile.PRIORITY_OFF;
319 }
320
321 /**
322 * Check if A2DP profile is streaming music.
323 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800324 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700325 *
326 * @param device BluetoothDevice device
327 */
328 public boolean isA2dpPlaying(BluetoothDevice device) {
329 if (mService != null && isEnabled()
330 && isValidDevice(device)) {
331 try {
332 return mService.isA2dpPlaying(device);
333 } catch (RemoteException e) {
334 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
335 return false;
336 }
337 }
338 if (mService == null) Log.w(TAG, "Proxy not attached to service");
339 return false;
340 }
341
342 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700343 * This function checks if the remote device is an AVCRP
344 * target and thus whether we should send volume keys
345 * changes or not.
346 * @hide
347 */
348 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
349 if (isEnabled() && isValidDevice(device)) {
350 ParcelUuid[] uuids = device.getUuids();
351 if (uuids == null) return false;
352
353 for (ParcelUuid uuid: uuids) {
354 if (BluetoothUuid.isAvrcpTarget(uuid)) {
355 return true;
356 }
357 }
358 }
359 return false;
360 }
361
Matthew Xiea0c68032011-06-25 21:47:07 -0700362 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700363 * Helper for converting a state to a string.
364 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 * For debug use only - strings are not internationalized.
366 * @hide
367 */
368 public static String stateToString(int state) {
369 switch (state) {
370 case STATE_DISCONNECTED:
371 return "disconnected";
372 case STATE_CONNECTING:
373 return "connecting";
374 case STATE_CONNECTED:
375 return "connected";
376 case STATE_DISCONNECTING:
377 return "disconnecting";
378 case STATE_PLAYING:
379 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700380 case STATE_NOT_PLAYING:
381 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 default:
383 return "<unknown state " + state + ">";
384 }
385 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800386
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800387 private ServiceConnection mConnection = new ServiceConnection() {
388 public void onServiceConnected(ComponentName className, IBinder service) {
389 if (DBG) Log.d(TAG, "Proxy object connected");
390 mService = IBluetoothA2dp.Stub.asInterface(service);
391
392 if (mServiceListener != null) {
393 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
394 }
395 }
396 public void onServiceDisconnected(ComponentName className) {
397 if (DBG) Log.d(TAG, "Proxy object disconnected");
398 mService = null;
399 if (mServiceListener != null) {
400 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
401 }
402 }
403 };
404
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700405 private boolean isEnabled() {
406 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
407 return false;
408 }
409
410 private boolean isValidDevice(BluetoothDevice device) {
411 if (device == null) return false;
412
413 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
414 return false;
415 }
416
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800417 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700418 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800419 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420}