blob: 1ca2be5b2a1f92ae10367011ecb2a42e109da8a5 [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
Tor Norbye2d497522015-04-23 17:10:21 -070019import android.Manifest;
20import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080023import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080025import android.content.Intent;
26import android.content.ServiceConnection;
RoboErik4197cb62015-01-21 15:45:32 -080027import android.media.AudioManager;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060028import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070030import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070031import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.Log;
33
Calvin Ond7d16b92016-06-20 15:59:48 -070034import com.android.internal.annotations.GuardedBy;
35
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070036import java.util.ArrayList;
37import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070038import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070042 * This class provides the public APIs to control the Bluetooth A2DP
43 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070045 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
46 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
47 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070049 * <p> Android only supports one connected Bluetooth A2dp device at a time.
50 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070052public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080054 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070055 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070057 /**
58 * Intent used to broadcast the change in connection state of the A2DP
59 * profile.
60 *
61 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080062 * <ul>
63 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
64 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
65 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
66 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070067 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080068 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070069 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
70 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
71 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080072 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
73 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 */
75 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070076 public static final String ACTION_CONNECTION_STATE_CHANGED =
77 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070079 /**
80 * Intent used to broadcast the change in the Playing state of the A2DP
81 * profile.
82 *
83 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080084 * <ul>
85 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
86 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080087 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080088 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070089 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080090 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070091 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
92 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080093 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
94 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070095 */
96 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
97 public static final String ACTION_PLAYING_STATE_CHANGED =
98 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530100 /** @hide */
101 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
102 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
103 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
104
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700105 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800106 * Intent used to broadcast the change in the Audio Codec state of the
107 * A2DP Source profile.
108 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800109 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800110 * <ul>
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800111 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800112 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
113 * connected, otherwise it is not included.</li>
114 * </ul>
115 *
116 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
117 * receive.
118 *
119 * @hide
120 */
121 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
122 public static final String ACTION_CODEC_CONFIG_CHANGED =
123 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
124
125 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700126 * A2DP sink device is streaming music. This state can be one of
127 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
128 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
129 */
130 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700131
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700132 /**
133 * A2DP sink device is NOT streaming music. This state can be one of
134 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
135 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
136 */
137 public static final int STATE_NOT_PLAYING = 11;
138
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800139 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700140 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700141 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
142 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700143 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144
fredc0f420372012-04-12 00:02:00 -0700145 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
146 new IBluetoothStateChangeCallback.Stub() {
147 public void onBluetoothStateChange(boolean up) {
148 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
149 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700150 if (VDBG) Log.d(TAG, "Unbinding service...");
151 try {
152 mServiceLock.writeLock().lock();
153 mService = null;
154 mContext.unbindService(mConnection);
155 } catch (Exception re) {
156 Log.e(TAG, "", re);
157 } finally {
158 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700159 }
160 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700161 try {
162 mServiceLock.readLock().lock();
163 if (mService == null) {
164 if (VDBG) Log.d(TAG,"Binding service...");
165 doBind();
fredc0f420372012-04-12 00:02:00 -0700166 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700167 } catch (Exception re) {
168 Log.e(TAG,"",re);
169 } finally {
170 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700171 }
172 }
173 }
174 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 /**
176 * Create a BluetoothA2dp proxy object for interacting with the local
177 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700178 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800180 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
181 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700182 mServiceListener = l;
183 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700184 IBluetoothManager mgr = mAdapter.getBluetoothManager();
185 if (mgr != null) {
186 try {
187 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
188 } catch (RemoteException e) {
189 Log.e(TAG,"",e);
190 }
191 }
192
Dianne Hackborn221ea892013-08-04 16:50:16 -0700193 doBind();
194 }
195
196 boolean doBind() {
197 Intent intent = new Intent(IBluetoothA2dp.class.getName());
198 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
199 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700200 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
201 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700202 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
203 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700205 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800208 /*package*/ void close() {
209 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700210 IBluetoothManager mgr = mAdapter.getBluetoothManager();
211 if (mgr != null) {
212 try {
213 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
214 } catch (Exception e) {
215 Log.e(TAG,"",e);
216 }
217 }
218
Calvin Ond7d16b92016-06-20 15:59:48 -0700219 try {
220 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700221 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700222 mService = null;
223 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700224 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700225 } catch (Exception re) {
226 Log.e(TAG, "", re);
227 } finally {
228 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700229 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800230 }
231
fredc0f420372012-04-12 00:02:00 -0700232 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200233 // The empty finalize needs to be kept or the
234 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700235 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700236 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700237 * Initiate connection to a profile of the remote bluetooth device.
238 *
239 * <p> Currently, the system supports only 1 connection to the
240 * A2DP profile. The API will automatically disconnect connected
241 * devices before connecting.
242 *
243 * <p> This API returns false in scenarios like the profile on the
244 * device is already connected or Bluetooth is not turned on.
245 * When this API returns true, it is guaranteed that
246 * connection state intent for the profile will be broadcasted with
247 * the state. Users can get the connection state of the profile
248 * from this intent.
249 *
250 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
251 * permission.
252 *
253 * @param device Remote Bluetooth Device
254 * @return false on immediate error,
255 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700256 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700258 public boolean connect(BluetoothDevice device) {
259 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700260 try {
261 mServiceLock.readLock().lock();
262 if (mService != null && isEnabled() &&
263 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700264 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700265 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700266 if (mService == null) Log.w(TAG, "Proxy not attached to service");
267 return false;
268 } catch (RemoteException e) {
269 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
270 return false;
271 } finally {
272 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
274 }
275
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700276 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700277 * Initiate disconnection from a profile
278 *
279 * <p> This API will return false in scenarios like the profile on the
280 * Bluetooth device is not in connected state etc. When this API returns,
281 * true, it is guaranteed that the connection state change
282 * intent will be broadcasted with the state. Users can get the
283 * disconnection state of the profile from this intent.
284 *
285 * <p> If the disconnection is initiated by a remote device, the state
286 * will transition from {@link #STATE_CONNECTED} to
287 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
288 * host (local) device the state will transition from
289 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
290 * state {@link #STATE_DISCONNECTED}. The transition to
291 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
292 * two scenarios.
293 *
294 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
295 * permission.
296 *
297 * @param device Remote Bluetooth Device
298 * @return false on immediate error,
299 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700300 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700302 public boolean disconnect(BluetoothDevice device) {
303 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700304 try {
305 mServiceLock.readLock().lock();
306 if (mService != null && isEnabled() &&
307 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700308 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700309 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700310 if (mService == null) Log.w(TAG, "Proxy not attached to service");
311 return false;
312 } catch (RemoteException e) {
313 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
314 return false;
315 } finally {
316 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 }
318 }
319
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700320 /**
321 * {@inheritDoc}
322 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700323 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700324 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700325 try {
326 mServiceLock.readLock().lock();
327 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700328 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700329 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700330 if (mService == null) Log.w(TAG, "Proxy not attached to service");
331 return new ArrayList<BluetoothDevice>();
332 } catch (RemoteException e) {
333 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
334 return new ArrayList<BluetoothDevice>();
335 } finally {
336 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700337 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700338 }
339
340 /**
341 * {@inheritDoc}
342 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700343 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700344 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700345 try {
346 mServiceLock.readLock().lock();
347 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700348 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700349 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700350 if (mService == null) Log.w(TAG, "Proxy not attached to service");
351 return new ArrayList<BluetoothDevice>();
352 } catch (RemoteException e) {
353 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
354 return new ArrayList<BluetoothDevice>();
355 } finally {
356 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700357 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700358 }
359
360 /**
361 * {@inheritDoc}
362 */
363 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700364 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700365 try {
366 mServiceLock.readLock().lock();
367 if (mService != null && isEnabled()
368 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700369 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700370 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700371 if (mService == null) Log.w(TAG, "Proxy not attached to service");
372 return BluetoothProfile.STATE_DISCONNECTED;
373 } catch (RemoteException e) {
374 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
375 return BluetoothProfile.STATE_DISCONNECTED;
376 } finally {
377 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700378 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700379 }
380
381 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700382 * Set priority of the profile
383 *
384 * <p> The device should already be paired.
fredc0f420372012-04-12 00:02:00 -0700385 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700386 * {@link #PRIORITY_OFF},
387 *
388 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
389 * permission.
390 *
391 * @param device Paired bluetooth device
392 * @param priority
393 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700394 * @hide
395 */
396 public boolean setPriority(BluetoothDevice device, int priority) {
397 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700398 try {
399 mServiceLock.readLock().lock();
400 if (mService != null && isEnabled()
401 && isValidDevice(device)) {
402 if (priority != BluetoothProfile.PRIORITY_OFF &&
403 priority != BluetoothProfile.PRIORITY_ON) {
404 return false;
405 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700406 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700407 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700408 if (mService == null) Log.w(TAG, "Proxy not attached to service");
409 return false;
410 } catch (RemoteException e) {
411 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
412 return false;
413 } finally {
414 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700415 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700416 }
417
418 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700419 * Get the priority of the profile.
420 *
421 * <p> The priority can be any of:
422 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
423 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
424 *
425 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
426 *
427 * @param device Bluetooth device
428 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700429 * @hide
430 */
Tor Norbye2d497522015-04-23 17:10:21 -0700431 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700432 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700433 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700434 try {
435 mServiceLock.readLock().lock();
436 if (mService != null && isEnabled()
437 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700438 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700439 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700440 if (mService == null) Log.w(TAG, "Proxy not attached to service");
441 return BluetoothProfile.PRIORITY_OFF;
442 } catch (RemoteException e) {
443 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
444 return BluetoothProfile.PRIORITY_OFF;
445 } finally {
446 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700447 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700448 }
449
450 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700451 * Checks if Avrcp device supports the absolute volume feature.
452 *
453 * @return true if device supports absolute volume
454 * @hide
455 */
456 public boolean isAvrcpAbsoluteVolumeSupported() {
457 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700458 try {
459 mServiceLock.readLock().lock();
460 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700461 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700462 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700463 if (mService == null) Log.w(TAG, "Proxy not attached to service");
464 return false;
465 } catch (RemoteException e) {
466 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
467 return false;
468 } finally {
469 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700470 }
John Du5a0cf7a2013-07-19 11:30:34 -0700471 }
472
473 /**
RoboErik4197cb62015-01-21 15:45:32 -0800474 * Tells remote device to adjust volume. Only if absolute volume is
475 * supported. Uses the following values:
476 * <ul>
477 * <li>{@link AudioManager#ADJUST_LOWER}</li>
478 * <li>{@link AudioManager#ADJUST_RAISE}</li>
479 * <li>{@link AudioManager#ADJUST_MUTE}</li>
480 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
481 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700482 *
RoboErik4197cb62015-01-21 15:45:32 -0800483 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700484 * @hide
485 */
486 public void adjustAvrcpAbsoluteVolume(int direction) {
487 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700488 try {
489 mServiceLock.readLock().lock();
490 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700491 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700492 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700493 if (mService == null) Log.w(TAG, "Proxy not attached to service");
494 } catch (RemoteException e) {
495 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
496 } finally {
497 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700498 }
John Du5a0cf7a2013-07-19 11:30:34 -0700499 }
500
501 /**
502 * Tells remote device to set an absolute volume. Only if absolute volume is supported
503 *
504 * @param volume Absolute volume to be set on AVRCP side
505 * @hide
506 */
507 public void setAvrcpAbsoluteVolume(int volume) {
508 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700509 try {
510 mServiceLock.readLock().lock();
511 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700512 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700513 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700514 if (mService == null) Log.w(TAG, "Proxy not attached to service");
515 } catch (RemoteException e) {
516 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
517 } finally {
518 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700519 }
John Du5a0cf7a2013-07-19 11:30:34 -0700520 }
521
522 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700523 * Check if A2DP profile is streaming music.
524 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800525 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700526 *
527 * @param device BluetoothDevice device
528 */
529 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700530 try {
531 mServiceLock.readLock().lock();
532 if (mService != null && isEnabled()
533 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700534 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700535 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700536 if (mService == null) Log.w(TAG, "Proxy not attached to service");
537 return false;
538 } catch (RemoteException e) {
539 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
540 return false;
541 } finally {
542 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700543 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700544 }
545
546 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700547 * This function checks if the remote device is an AVCRP
548 * target and thus whether we should send volume keys
549 * changes or not.
550 * @hide
551 */
552 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
553 if (isEnabled() && isValidDevice(device)) {
554 ParcelUuid[] uuids = device.getUuids();
555 if (uuids == null) return false;
556
557 for (ParcelUuid uuid: uuids) {
558 if (BluetoothUuid.isAvrcpTarget(uuid)) {
559 return true;
560 }
561 }
562 }
563 return false;
564 }
565
Matthew Xiea0c68032011-06-25 21:47:07 -0700566 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800567 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800568 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800569 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800570 * @hide
571 */
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800572 public BluetoothCodecStatus getCodecStatus() {
573 if (DBG) Log.d(TAG, "getCodecStatus");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800574 try {
575 mServiceLock.readLock().lock();
576 if (mService != null && isEnabled()) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800577 return mService.getCodecStatus();
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800578 }
579 if (mService == null) {
580 Log.w(TAG, "Proxy not attached to service");
581 }
582 return null;
583 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800584 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800585 return null;
586 } finally {
587 mServiceLock.readLock().unlock();
588 }
589 }
590
591 /**
592 * Sets the codec configuration preference.
593 *
594 * @param codecConfig the codec configuration preference
595 * @hide
596 */
597 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
598 if (DBG) Log.d(TAG, "setCodecConfigPreference");
599 try {
600 mServiceLock.readLock().lock();
601 if (mService != null && isEnabled()) {
602 mService.setCodecConfigPreference(codecConfig);
603 }
604 if (mService == null) Log.w(TAG, "Proxy not attached to service");
605 return;
606 } catch (RemoteException e) {
607 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
608 return;
609 } finally {
610 mServiceLock.readLock().unlock();
611 }
612 }
613
614 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800615 * Enables the optional codecs.
616 *
617 * @hide
618 */
619 public void enableOptionalCodecs() {
620 if (DBG) Log.d(TAG, "enableOptionalCodecs");
621 enableDisableOptionalCodecs(true);
622 }
623
624 /**
625 * Disables the optional codecs.
626 *
627 * @hide
628 */
629 public void disableOptionalCodecs() {
630 if (DBG) Log.d(TAG, "disableOptionalCodecs");
631 enableDisableOptionalCodecs(false);
632 }
633
634 /**
635 * Enables or disables the optional codecs.
636 *
637 * @param enable if true, enable the optional codecs, other disable them
638 */
639 private void enableDisableOptionalCodecs(boolean enable) {
640 try {
641 mServiceLock.readLock().lock();
642 if (mService != null && isEnabled()) {
643 if (enable) {
644 mService.enableOptionalCodecs();
645 } else {
646 mService.disableOptionalCodecs();
647 }
648 }
649 if (mService == null) Log.w(TAG, "Proxy not attached to service");
650 return;
651 } catch (RemoteException e) {
652 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
653 return;
654 } finally {
655 mServiceLock.readLock().unlock();
656 }
657 }
658
659 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700660 * Helper for converting a state to a string.
661 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 * For debug use only - strings are not internationalized.
663 * @hide
664 */
665 public static String stateToString(int state) {
666 switch (state) {
667 case STATE_DISCONNECTED:
668 return "disconnected";
669 case STATE_CONNECTING:
670 return "connecting";
671 case STATE_CONNECTED:
672 return "connected";
673 case STATE_DISCONNECTING:
674 return "disconnecting";
675 case STATE_PLAYING:
676 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700677 case STATE_NOT_PLAYING:
678 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 default:
680 return "<unknown state " + state + ">";
681 }
682 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800683
Matthew Xie9b693992013-10-10 11:21:40 -0700684 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800685 public void onServiceConnected(ComponentName className, IBinder service) {
686 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700687 try {
688 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600689 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700690 } finally {
691 mServiceLock.writeLock().unlock();
692 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800693
694 if (mServiceListener != null) {
695 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
696 }
697 }
698 public void onServiceDisconnected(ComponentName className) {
699 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700700 try {
701 mServiceLock.writeLock().lock();
702 mService = null;
703 } finally {
704 mServiceLock.writeLock().unlock();
705 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800706 if (mServiceListener != null) {
707 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
708 }
709 }
710 };
711
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700712 private boolean isEnabled() {
713 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
714 return false;
715 }
716
717 private boolean isValidDevice(BluetoothDevice device) {
718 if (device == null) return false;
719
720 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
721 return false;
722 }
723
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800724 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700725 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800726 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727}