blob: c58eaa14ed821fb5482c1e502714a69bfaef48d5 [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
Antony Sargentf5772c62017-04-26 16:37:53 -0700139 /**
140 * We don't have a stored preference for whether or not the given A2DP sink device supports
141 * optional codecs.
142 * @hide */
143 public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
144
145 /**
146 * The given A2DP sink device does not support optional codecs.
147 * @hide */
148 public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
149
150 /**
151 * The given A2DP sink device does support optional codecs.
152 * @hide */
153 public static final int OPTIONAL_CODECS_SUPPORTED = 1;
154
155 /**
156 * We don't have a stored preference for whether optional codecs should be enabled or disabled
157 * for the given A2DP device.
158 * @hide */
159 public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
160
161 /**
162 * Optional codecs should be disabled for the given A2DP device.
163 * @hide */
164 public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
165
166 /**
167 * Optional codecs should be enabled for the given A2DP device.
168 * @hide */
169 public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
170
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800171 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700172 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700173 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
174 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700175 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176
fredc0f420372012-04-12 00:02:00 -0700177 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
178 new IBluetoothStateChangeCallback.Stub() {
179 public void onBluetoothStateChange(boolean up) {
180 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
181 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700182 if (VDBG) Log.d(TAG, "Unbinding service...");
183 try {
184 mServiceLock.writeLock().lock();
185 mService = null;
186 mContext.unbindService(mConnection);
187 } catch (Exception re) {
188 Log.e(TAG, "", re);
189 } finally {
190 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700191 }
192 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700193 try {
194 mServiceLock.readLock().lock();
195 if (mService == null) {
196 if (VDBG) Log.d(TAG,"Binding service...");
197 doBind();
fredc0f420372012-04-12 00:02:00 -0700198 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700199 } catch (Exception re) {
200 Log.e(TAG,"",re);
201 } finally {
202 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700203 }
204 }
205 }
206 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 /**
208 * Create a BluetoothA2dp proxy object for interacting with the local
209 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700210 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800212 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
213 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700214 mServiceListener = l;
215 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700216 IBluetoothManager mgr = mAdapter.getBluetoothManager();
217 if (mgr != null) {
218 try {
219 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
220 } catch (RemoteException e) {
221 Log.e(TAG,"",e);
222 }
223 }
224
Dianne Hackborn221ea892013-08-04 16:50:16 -0700225 doBind();
226 }
227
228 boolean doBind() {
229 Intent intent = new Intent(IBluetoothA2dp.class.getName());
230 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
231 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700232 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
233 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700234 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
235 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700237 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 }
239
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800240 /*package*/ void close() {
241 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700242 IBluetoothManager mgr = mAdapter.getBluetoothManager();
243 if (mgr != null) {
244 try {
245 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
246 } catch (Exception e) {
247 Log.e(TAG,"",e);
248 }
249 }
250
Calvin Ond7d16b92016-06-20 15:59:48 -0700251 try {
252 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700253 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700254 mService = null;
255 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700256 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700257 } catch (Exception re) {
258 Log.e(TAG, "", re);
259 } finally {
260 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700261 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800262 }
263
fredc0f420372012-04-12 00:02:00 -0700264 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200265 // The empty finalize needs to be kept or the
266 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700267 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700268 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700269 * Initiate connection to a profile of the remote bluetooth device.
270 *
271 * <p> Currently, the system supports only 1 connection to the
272 * A2DP profile. The API will automatically disconnect connected
273 * devices before connecting.
274 *
275 * <p> This API returns false in scenarios like the profile on the
276 * device is already connected or Bluetooth is not turned on.
277 * When this API returns true, it is guaranteed that
278 * connection state intent for the profile will be broadcasted with
279 * the state. Users can get the connection state of the profile
280 * from this intent.
281 *
282 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
283 * permission.
284 *
285 * @param device Remote Bluetooth Device
286 * @return false on immediate error,
287 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700288 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700290 public boolean connect(BluetoothDevice device) {
291 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700292 try {
293 mServiceLock.readLock().lock();
294 if (mService != null && isEnabled() &&
295 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700296 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700297 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700298 if (mService == null) Log.w(TAG, "Proxy not attached to service");
299 return false;
300 } catch (RemoteException e) {
301 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
302 return false;
303 } finally {
304 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
306 }
307
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700308 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700309 * Initiate disconnection from a profile
310 *
311 * <p> This API will return false in scenarios like the profile on the
312 * Bluetooth device is not in connected state etc. When this API returns,
313 * true, it is guaranteed that the connection state change
314 * intent will be broadcasted with the state. Users can get the
315 * disconnection state of the profile from this intent.
316 *
317 * <p> If the disconnection is initiated by a remote device, the state
318 * will transition from {@link #STATE_CONNECTED} to
319 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
320 * host (local) device the state will transition from
321 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
322 * state {@link #STATE_DISCONNECTED}. The transition to
323 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
324 * two scenarios.
325 *
326 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
327 * permission.
328 *
329 * @param device Remote Bluetooth Device
330 * @return false on immediate error,
331 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700332 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700334 public boolean disconnect(BluetoothDevice device) {
335 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700336 try {
337 mServiceLock.readLock().lock();
338 if (mService != null && isEnabled() &&
339 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700340 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700341 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700342 if (mService == null) Log.w(TAG, "Proxy not attached to service");
343 return false;
344 } catch (RemoteException e) {
345 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
346 return false;
347 } finally {
348 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 }
350 }
351
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700352 /**
353 * {@inheritDoc}
354 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700355 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700356 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700357 try {
358 mServiceLock.readLock().lock();
359 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700360 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700361 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700362 if (mService == null) Log.w(TAG, "Proxy not attached to service");
363 return new ArrayList<BluetoothDevice>();
364 } catch (RemoteException e) {
365 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
366 return new ArrayList<BluetoothDevice>();
367 } finally {
368 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700369 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700370 }
371
372 /**
373 * {@inheritDoc}
374 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700375 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700376 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700377 try {
378 mServiceLock.readLock().lock();
379 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700380 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700381 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700382 if (mService == null) Log.w(TAG, "Proxy not attached to service");
383 return new ArrayList<BluetoothDevice>();
384 } catch (RemoteException e) {
385 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
386 return new ArrayList<BluetoothDevice>();
387 } finally {
388 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700389 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700390 }
391
392 /**
393 * {@inheritDoc}
394 */
395 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700396 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700397 try {
398 mServiceLock.readLock().lock();
399 if (mService != null && isEnabled()
400 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700401 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700402 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700403 if (mService == null) Log.w(TAG, "Proxy not attached to service");
404 return BluetoothProfile.STATE_DISCONNECTED;
405 } catch (RemoteException e) {
406 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
407 return BluetoothProfile.STATE_DISCONNECTED;
408 } finally {
409 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700410 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700411 }
412
413 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700414 * Set priority of the profile
415 *
416 * <p> The device should already be paired.
fredc0f420372012-04-12 00:02:00 -0700417 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700418 * {@link #PRIORITY_OFF},
419 *
420 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
421 * permission.
422 *
423 * @param device Paired bluetooth device
424 * @param priority
425 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700426 * @hide
427 */
428 public boolean setPriority(BluetoothDevice device, int priority) {
429 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700430 try {
431 mServiceLock.readLock().lock();
432 if (mService != null && isEnabled()
433 && isValidDevice(device)) {
434 if (priority != BluetoothProfile.PRIORITY_OFF &&
435 priority != BluetoothProfile.PRIORITY_ON) {
436 return false;
437 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700438 return mService.setPriority(device, priority);
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 false;
442 } catch (RemoteException e) {
443 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
444 return false;
445 } finally {
446 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700447 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700448 }
449
450 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700451 * Get the priority of the profile.
452 *
453 * <p> The priority can be any of:
454 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
455 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
456 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700457 * @param device Bluetooth device
458 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700459 * @hide
460 */
Tor Norbye2d497522015-04-23 17:10:21 -0700461 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700462 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700463 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700464 try {
465 mServiceLock.readLock().lock();
466 if (mService != null && isEnabled()
467 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700468 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700469 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700470 if (mService == null) Log.w(TAG, "Proxy not attached to service");
471 return BluetoothProfile.PRIORITY_OFF;
472 } catch (RemoteException e) {
473 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
474 return BluetoothProfile.PRIORITY_OFF;
475 } finally {
476 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700477 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700478 }
479
480 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700481 * Checks if Avrcp device supports the absolute volume feature.
482 *
483 * @return true if device supports absolute volume
484 * @hide
485 */
486 public boolean isAvrcpAbsoluteVolumeSupported() {
487 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
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 return mService.isAvrcpAbsoluteVolumeSupported();
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 return false;
495 } catch (RemoteException e) {
496 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
497 return false;
498 } finally {
499 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700500 }
John Du5a0cf7a2013-07-19 11:30:34 -0700501 }
502
503 /**
RoboErik4197cb62015-01-21 15:45:32 -0800504 * Tells remote device to adjust volume. Only if absolute volume is
505 * supported. Uses the following values:
506 * <ul>
507 * <li>{@link AudioManager#ADJUST_LOWER}</li>
508 * <li>{@link AudioManager#ADJUST_RAISE}</li>
509 * <li>{@link AudioManager#ADJUST_MUTE}</li>
510 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
511 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700512 *
RoboErik4197cb62015-01-21 15:45:32 -0800513 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700514 * @hide
515 */
516 public void adjustAvrcpAbsoluteVolume(int direction) {
517 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700518 try {
519 mServiceLock.readLock().lock();
520 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700521 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700522 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700523 if (mService == null) Log.w(TAG, "Proxy not attached to service");
524 } catch (RemoteException e) {
525 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
526 } finally {
527 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700528 }
John Du5a0cf7a2013-07-19 11:30:34 -0700529 }
530
531 /**
532 * Tells remote device to set an absolute volume. Only if absolute volume is supported
533 *
534 * @param volume Absolute volume to be set on AVRCP side
535 * @hide
536 */
537 public void setAvrcpAbsoluteVolume(int volume) {
538 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700539 try {
540 mServiceLock.readLock().lock();
541 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700542 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700543 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700544 if (mService == null) Log.w(TAG, "Proxy not attached to service");
545 } catch (RemoteException e) {
546 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
547 } finally {
548 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700549 }
John Du5a0cf7a2013-07-19 11:30:34 -0700550 }
551
552 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700553 * Check if A2DP profile is streaming music.
554 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800555 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700556 *
557 * @param device BluetoothDevice device
558 */
559 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700560 try {
561 mServiceLock.readLock().lock();
562 if (mService != null && isEnabled()
563 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700564 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700565 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700566 if (mService == null) Log.w(TAG, "Proxy not attached to service");
567 return false;
568 } catch (RemoteException e) {
569 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
570 return false;
571 } finally {
572 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700573 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700574 }
575
576 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700577 * This function checks if the remote device is an AVCRP
578 * target and thus whether we should send volume keys
579 * changes or not.
580 * @hide
581 */
582 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
583 if (isEnabled() && isValidDevice(device)) {
584 ParcelUuid[] uuids = device.getUuids();
585 if (uuids == null) return false;
586
587 for (ParcelUuid uuid: uuids) {
588 if (BluetoothUuid.isAvrcpTarget(uuid)) {
589 return true;
590 }
591 }
592 }
593 return false;
594 }
595
Matthew Xiea0c68032011-06-25 21:47:07 -0700596 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800597 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800598 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800599 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800600 * @hide
601 */
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800602 public BluetoothCodecStatus getCodecStatus() {
603 if (DBG) Log.d(TAG, "getCodecStatus");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800604 try {
605 mServiceLock.readLock().lock();
606 if (mService != null && isEnabled()) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800607 return mService.getCodecStatus();
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800608 }
609 if (mService == null) {
610 Log.w(TAG, "Proxy not attached to service");
611 }
612 return null;
613 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800614 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800615 return null;
616 } finally {
617 mServiceLock.readLock().unlock();
618 }
619 }
620
621 /**
622 * Sets the codec configuration preference.
623 *
624 * @param codecConfig the codec configuration preference
625 * @hide
626 */
627 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
628 if (DBG) Log.d(TAG, "setCodecConfigPreference");
629 try {
630 mServiceLock.readLock().lock();
631 if (mService != null && isEnabled()) {
632 mService.setCodecConfigPreference(codecConfig);
633 }
634 if (mService == null) Log.w(TAG, "Proxy not attached to service");
635 return;
636 } catch (RemoteException e) {
637 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
638 return;
639 } finally {
640 mServiceLock.readLock().unlock();
641 }
642 }
643
644 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800645 * Enables the optional codecs.
646 *
647 * @hide
648 */
649 public void enableOptionalCodecs() {
650 if (DBG) Log.d(TAG, "enableOptionalCodecs");
651 enableDisableOptionalCodecs(true);
652 }
653
654 /**
655 * Disables the optional codecs.
656 *
657 * @hide
658 */
659 public void disableOptionalCodecs() {
660 if (DBG) Log.d(TAG, "disableOptionalCodecs");
661 enableDisableOptionalCodecs(false);
662 }
663
664 /**
665 * Enables or disables the optional codecs.
666 *
667 * @param enable if true, enable the optional codecs, other disable them
668 */
669 private void enableDisableOptionalCodecs(boolean enable) {
670 try {
671 mServiceLock.readLock().lock();
672 if (mService != null && isEnabled()) {
673 if (enable) {
674 mService.enableOptionalCodecs();
675 } else {
676 mService.disableOptionalCodecs();
677 }
678 }
679 if (mService == null) Log.w(TAG, "Proxy not attached to service");
680 return;
681 } catch (RemoteException e) {
682 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
683 return;
684 } finally {
685 mServiceLock.readLock().unlock();
686 }
687 }
688
689 /**
Antony Sargentf5772c62017-04-26 16:37:53 -0700690 * Returns whether this device supports optional codecs.
691 *
692 * @param device The device to check
693 * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
694 * OPTIONAL_CODECS_SUPPORTED.
695 *
696 * @hide
697 */
698 public int supportsOptionalCodecs(BluetoothDevice device) {
699 try {
700 mServiceLock.readLock().lock();
701 if (mService != null && isEnabled() && isValidDevice(device)) {
702 return mService.supportsOptionalCodecs(device);
703 }
704 if (mService == null) Log.w(TAG, "Proxy not attached to service");
705 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
706 } catch (RemoteException e) {
707 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
708 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
709 } finally {
710 mServiceLock.readLock().unlock();
711 }
712 }
713
714 /**
715 * Returns whether this device should have optional codecs enabled.
716 *
717 * @param device The device in question.
718 * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
719 * OPTIONAL_CODECS_PREF_DISABLED.
720 *
721 * @hide
722 */
723 public int getOptionalCodecsEnabled(BluetoothDevice device) {
724 try {
725 mServiceLock.readLock().lock();
726 if (mService != null && isEnabled() && isValidDevice(device)) {
727 return mService.getOptionalCodecsEnabled(device);
728 }
729 if (mService == null) Log.w(TAG, "Proxy not attached to service");
730 return OPTIONAL_CODECS_PREF_UNKNOWN;
731 } catch (RemoteException e) {
732 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
733 return OPTIONAL_CODECS_PREF_UNKNOWN;
734 } finally {
735 mServiceLock.readLock().unlock();
736 }
737 }
738
739 /**
740 * Sets a persistent preference for whether a given device should have optional codecs enabled.
741 *
742 * @param device The device to set this preference for.
743 * @param value Whether the optional codecs should be enabled for this device. This should be
744 * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
745 * OPTIONAL_CODECS_PREF_DISABLED.
746 * @hide
747 */
748 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
749 try {
750 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN &&
751 value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED &&
752 value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
753 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
754 return;
755 }
756 mServiceLock.readLock().lock();
757 if (mService != null && isEnabled()
758 && isValidDevice(device)) {
759 mService.setOptionalCodecsEnabled(device, value);
760 }
761 if (mService == null) Log.w(TAG, "Proxy not attached to service");
762 return;
763 } catch (RemoteException e) {
764 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
765 return;
766 } finally {
767 mServiceLock.readLock().unlock();
768 }
769 }
770
771 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700772 * Helper for converting a state to a string.
773 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 * For debug use only - strings are not internationalized.
775 * @hide
776 */
777 public static String stateToString(int state) {
778 switch (state) {
779 case STATE_DISCONNECTED:
780 return "disconnected";
781 case STATE_CONNECTING:
782 return "connecting";
783 case STATE_CONNECTED:
784 return "connected";
785 case STATE_DISCONNECTING:
786 return "disconnecting";
787 case STATE_PLAYING:
788 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700789 case STATE_NOT_PLAYING:
790 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 default:
792 return "<unknown state " + state + ">";
793 }
794 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800795
Matthew Xie9b693992013-10-10 11:21:40 -0700796 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800797 public void onServiceConnected(ComponentName className, IBinder service) {
798 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700799 try {
800 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600801 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700802 } finally {
803 mServiceLock.writeLock().unlock();
804 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800805
806 if (mServiceListener != null) {
807 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
808 }
809 }
810 public void onServiceDisconnected(ComponentName className) {
811 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700812 try {
813 mServiceLock.writeLock().lock();
814 mService = null;
815 } finally {
816 mServiceLock.writeLock().unlock();
817 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800818 if (mServiceListener != null) {
819 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
820 }
821 }
822 };
823
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700824 private boolean isEnabled() {
825 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
826 return false;
827 }
828
829 private boolean isValidDevice(BluetoothDevice device) {
830 if (device == null) return false;
831
832 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
833 return false;
834 }
835
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800836 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700837 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800838 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839}