blob: c4bf1ebd1a6ff01417518e548dc38b61ad5cc3cf [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;
Pavlin Radoslavov64a94352017-12-19 13:20:06 -080020import android.annotation.Nullable;
Tor Norbye2d497522015-04-23 17:10:21 -070021import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010024import android.annotation.UnsupportedAppUsage;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080025import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080027import android.content.Intent;
28import android.content.ServiceConnection;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060029import android.os.Binder;
Mathew Inwood8c854f82018-09-14 12:35:36 +010030import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070032import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070033import android.os.RemoteException;
Cheney Ni4b6d6c22018-08-21 12:17:42 +080034import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.util.Log;
36
Calvin Ond7d16b92016-06-20 15:59:48 -070037import com.android.internal.annotations.GuardedBy;
38
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070039import java.util.ArrayList;
40import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070041import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070042
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
44/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070045 * This class provides the public APIs to control the Bluetooth A2DP
46 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 *
Jack Hea355e5e2017-08-22 16:06:54 -070048 * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070049 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
50 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070052 * <p> Android only supports one connected Bluetooth A2dp device at a time.
53 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070055public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080057 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070058 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070060 /**
61 * Intent used to broadcast the change in connection state of the A2DP
62 * profile.
63 *
64 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080065 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070066 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
67 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
68 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080069 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070070 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080071 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070072 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
73 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
74 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080075 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
76 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 */
78 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070079 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070080 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070082 /**
83 * Intent used to broadcast the change in the Playing state of the A2DP
84 * profile.
85 *
86 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080087 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070088 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
89 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
90 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080091 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070092 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080093 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070094 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
95 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080096 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
97 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070098 */
99 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
100 public static final String ACTION_PLAYING_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700101 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530103 /** @hide */
104 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
105 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700106 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530107
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700108 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800109 * Intent used to broadcast the selection of a connected device as active.
110 *
111 * <p>This intent will have one extra:
112 * <ul>
113 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
114 * be null if no device is active. </li>
115 * </ul>
116 *
117 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
118 * receive.
119 *
120 * @hide
121 */
122 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100123 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800124 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
125 "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
126
127 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800128 * Intent used to broadcast the change in the Audio Codec state of the
129 * A2DP Source profile.
130 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800131 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800132 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700133 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
134 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
135 * connected, otherwise it is not included.</li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800136 * </ul>
137 *
138 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
139 * receive.
140 *
141 * @hide
142 */
143 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100144 @UnsupportedAppUsage
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800145 public static final String ACTION_CODEC_CONFIG_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700146 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800147
148 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700149 * A2DP sink device is streaming music. This state can be one of
150 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
151 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
152 */
Jack Hea355e5e2017-08-22 16:06:54 -0700153 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700154
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700155 /**
156 * A2DP sink device is NOT streaming music. This state can be one of
157 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
158 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
159 */
Jack Hea355e5e2017-08-22 16:06:54 -0700160 public static final int STATE_NOT_PLAYING = 11;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700161
Antony Sargentf5772c62017-04-26 16:37:53 -0700162 /**
163 * We don't have a stored preference for whether or not the given A2DP sink device supports
164 * optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700165 *
166 * @hide
167 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100168 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700169 public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
170
171 /**
172 * The given A2DP sink device does not support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700173 *
174 * @hide
175 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100176 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700177 public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
178
179 /**
180 * The given A2DP sink device does support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700181 *
182 * @hide
183 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100184 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700185 public static final int OPTIONAL_CODECS_SUPPORTED = 1;
186
187 /**
188 * We don't have a stored preference for whether optional codecs should be enabled or disabled
189 * for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700190 *
191 * @hide
192 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100193 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700194 public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
195
196 /**
197 * Optional codecs should be disabled for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700198 *
199 * @hide
200 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100201 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700202 public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
203
204 /**
Jack Hea355e5e2017-08-22 16:06:54 -0700205 * Optional codecs should be enabled for the given A2DP device.
206 *
207 * @hide
208 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100209 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700210 public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
211
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800212 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700213 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700214 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
Jack Hea355e5e2017-08-22 16:06:54 -0700215 @GuardedBy("mServiceLock")
216 private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700217 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
Jack He2992cd02017-08-22 21:21:23 -0700219 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700220 new IBluetoothStateChangeCallback.Stub() {
221 public void onBluetoothStateChange(boolean up) {
222 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
223 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700224 if (VDBG) Log.d(TAG, "Unbinding service...");
225 try {
226 mServiceLock.writeLock().lock();
227 mService = null;
228 mContext.unbindService(mConnection);
229 } catch (Exception re) {
230 Log.e(TAG, "", re);
231 } finally {
232 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700233 }
234 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700235 try {
236 mServiceLock.readLock().lock();
237 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700238 if (VDBG) Log.d(TAG, "Binding service...");
Calvin Ond7d16b92016-06-20 15:59:48 -0700239 doBind();
fredc0f420372012-04-12 00:02:00 -0700240 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700241 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700242 Log.e(TAG, "", re);
Calvin Ond7d16b92016-06-20 15:59:48 -0700243 } finally {
244 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700245 }
246 }
247 }
Jack Hea355e5e2017-08-22 16:06:54 -0700248 };
249
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 /**
251 * Create a BluetoothA2dp proxy object for interacting with the local
252 * Bluetooth A2DP service.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800254 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
255 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700256 mServiceListener = l;
257 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700258 IBluetoothManager mgr = mAdapter.getBluetoothManager();
259 if (mgr != null) {
260 try {
261 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
262 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700263 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700264 }
265 }
266
Dianne Hackborn221ea892013-08-04 16:50:16 -0700267 doBind();
268 }
269
270 boolean doBind() {
271 Intent intent = new Intent(IBluetoothA2dp.class.getName());
272 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
273 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700274 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Cheney Ni4b6d6c22018-08-21 12:17:42 +0800275 UserHandle.CURRENT_OR_SELF)) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700276 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
277 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700279 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 }
281
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100282 @UnsupportedAppUsage
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800283 /*package*/ void close() {
284 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700285 IBluetoothManager mgr = mAdapter.getBluetoothManager();
286 if (mgr != null) {
287 try {
288 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
289 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700290 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700291 }
292 }
293
Calvin Ond7d16b92016-06-20 15:59:48 -0700294 try {
295 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700296 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700297 mService = null;
298 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700299 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700300 } catch (Exception re) {
301 Log.e(TAG, "", re);
302 } finally {
303 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700304 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800305 }
306
Jack He2992cd02017-08-22 21:21:23 -0700307 @Override
fredc0f420372012-04-12 00:02:00 -0700308 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200309 // The empty finalize needs to be kept or the
310 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700311 }
Jack Hea355e5e2017-08-22 16:06:54 -0700312
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700313 /**
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800314 * Initiate connection to a profile of the remote Bluetooth device.
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700315 *
316 * <p> This API returns false in scenarios like the profile on the
317 * device is already connected or Bluetooth is not turned on.
318 * When this API returns true, it is guaranteed that
319 * connection state intent for the profile will be broadcasted with
320 * the state. Users can get the connection state of the profile
321 * from this intent.
322 *
323 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
324 * permission.
325 *
326 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700327 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700328 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100330 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700331 public boolean connect(BluetoothDevice device) {
332 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700333 try {
334 mServiceLock.readLock().lock();
Jack He2992cd02017-08-22 21:21:23 -0700335 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700336 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700337 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700338 if (mService == null) Log.w(TAG, "Proxy not attached to service");
339 return false;
340 } catch (RemoteException e) {
341 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
342 return false;
343 } finally {
344 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
346 }
347
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700348 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700349 * Initiate disconnection from a profile
350 *
351 * <p> This API will return false in scenarios like the profile on the
352 * Bluetooth device is not in connected state etc. When this API returns,
353 * true, it is guaranteed that the connection state change
354 * intent will be broadcasted with the state. Users can get the
355 * disconnection state of the profile from this intent.
356 *
357 * <p> If the disconnection is initiated by a remote device, the state
358 * will transition from {@link #STATE_CONNECTED} to
359 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
360 * host (local) device the state will transition from
361 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
362 * state {@link #STATE_DISCONNECTED}. The transition to
363 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
364 * two scenarios.
365 *
366 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
367 * permission.
368 *
369 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700370 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700371 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100373 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700374 public boolean disconnect(BluetoothDevice device) {
375 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700376 try {
377 mServiceLock.readLock().lock();
Jack He2992cd02017-08-22 21:21:23 -0700378 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700379 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700380 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700381 if (mService == null) Log.w(TAG, "Proxy not attached to service");
382 return false;
383 } catch (RemoteException e) {
384 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
385 return false;
386 } finally {
387 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
389 }
390
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700391 /**
392 * {@inheritDoc}
393 */
Jack He2992cd02017-08-22 21:21:23 -0700394 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700395 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700396 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700397 try {
398 mServiceLock.readLock().lock();
399 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700400 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700401 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700402 if (mService == null) Log.w(TAG, "Proxy not attached to service");
403 return new ArrayList<BluetoothDevice>();
404 } catch (RemoteException e) {
405 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
406 return new ArrayList<BluetoothDevice>();
407 } finally {
408 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700409 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700410 }
411
412 /**
413 * {@inheritDoc}
414 */
Jack He2992cd02017-08-22 21:21:23 -0700415 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700416 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700417 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700418 try {
419 mServiceLock.readLock().lock();
420 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700421 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700422 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700423 if (mService == null) Log.w(TAG, "Proxy not attached to service");
424 return new ArrayList<BluetoothDevice>();
425 } catch (RemoteException e) {
426 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
427 return new ArrayList<BluetoothDevice>();
428 } finally {
429 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700430 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700431 }
432
433 /**
434 * {@inheritDoc}
435 */
Jack He2992cd02017-08-22 21:21:23 -0700436 @Override
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700437 public @BtProfileState int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700438 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700439 try {
440 mServiceLock.readLock().lock();
441 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700442 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700443 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700444 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700445 if (mService == null) Log.w(TAG, "Proxy not attached to service");
446 return BluetoothProfile.STATE_DISCONNECTED;
447 } catch (RemoteException e) {
448 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
449 return BluetoothProfile.STATE_DISCONNECTED;
450 } finally {
451 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700452 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700453 }
454
455 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800456 * Select a connected device as active.
457 *
458 * The active device selection is per profile. An active device's
459 * purpose is profile-specific. For example, A2DP audio streaming
460 * is to the active A2DP Sink device. If a remote device is not
461 * connected, it cannot be selected as active.
462 *
463 * <p> This API returns false in scenarios like the profile on the
464 * device is not connected or Bluetooth is not turned on.
465 * When this API returns true, it is guaranteed that the
466 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
467 * with the active device.
468 *
469 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
470 * permission.
471 *
472 * @param device the remote Bluetooth device. Could be null to clear
473 * the active device and stop streaming audio to a Bluetooth device.
474 * @return false on immediate error, true otherwise
475 * @hide
476 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100477 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800478 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
479 if (DBG) log("setActiveDevice(" + device + ")");
480 try {
481 mServiceLock.readLock().lock();
482 if (mService != null && isEnabled()
483 && ((device == null) || isValidDevice(device))) {
484 return mService.setActiveDevice(device);
485 }
486 if (mService == null) Log.w(TAG, "Proxy not attached to service");
487 return false;
488 } catch (RemoteException e) {
489 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
490 return false;
491 } finally {
492 mServiceLock.readLock().unlock();
493 }
494 }
495
496 /**
497 * Get the connected device that is active.
498 *
499 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
500 * permission.
501 *
502 * @return the connected device that is active or null if no device
503 * is active
504 * @hide
505 */
506 @RequiresPermission(Manifest.permission.BLUETOOTH)
507 @Nullable
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100508 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800509 public BluetoothDevice getActiveDevice() {
510 if (VDBG) log("getActiveDevice()");
511 try {
512 mServiceLock.readLock().lock();
513 if (mService != null && isEnabled()) {
514 return mService.getActiveDevice();
515 }
516 if (mService == null) Log.w(TAG, "Proxy not attached to service");
517 return null;
518 } catch (RemoteException e) {
519 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
520 return null;
521 } finally {
522 mServiceLock.readLock().unlock();
523 }
524 }
525
526 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700527 * Set priority of the profile
528 *
529 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700530 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700531 * {@link #PRIORITY_OFF},
532 *
533 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
534 * permission.
535 *
536 * @param device Paired bluetooth device
537 * @param priority
538 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700539 * @hide
540 */
541 public boolean setPriority(BluetoothDevice device, int priority) {
542 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700543 try {
544 mServiceLock.readLock().lock();
545 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700546 && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700547 if (priority != BluetoothProfile.PRIORITY_OFF
548 && priority != BluetoothProfile.PRIORITY_ON) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700549 return false;
550 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700551 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700552 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700553 if (mService == null) Log.w(TAG, "Proxy not attached to service");
554 return false;
555 } catch (RemoteException e) {
556 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
557 return false;
558 } finally {
559 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700560 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700561 }
562
563 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700564 * Get the priority of the profile.
565 *
566 * <p> The priority can be any of:
567 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
568 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
569 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700570 * @param device Bluetooth device
571 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700572 * @hide
573 */
Tor Norbye2d497522015-04-23 17:10:21 -0700574 @RequiresPermission(Manifest.permission.BLUETOOTH)
Mathew Inwood8c854f82018-09-14 12:35:36 +0100575 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700576 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700577 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700578 try {
579 mServiceLock.readLock().lock();
580 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700581 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700582 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700583 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700584 if (mService == null) Log.w(TAG, "Proxy not attached to service");
585 return BluetoothProfile.PRIORITY_OFF;
586 } catch (RemoteException e) {
587 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
588 return BluetoothProfile.PRIORITY_OFF;
589 } finally {
590 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700591 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700592 }
593
594 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700595 * Checks if Avrcp device supports the absolute volume feature.
596 *
597 * @return true if device supports absolute volume
598 * @hide
599 */
600 public boolean isAvrcpAbsoluteVolumeSupported() {
601 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700602 try {
603 mServiceLock.readLock().lock();
604 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700605 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700606 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700607 if (mService == null) Log.w(TAG, "Proxy not attached to service");
608 return false;
609 } catch (RemoteException e) {
610 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
611 return false;
612 } finally {
613 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700614 }
John Du5a0cf7a2013-07-19 11:30:34 -0700615 }
616
617 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700618 * Tells remote device to set an absolute volume. Only if absolute volume is supported
619 *
620 * @param volume Absolute volume to be set on AVRCP side
621 * @hide
622 */
623 public void setAvrcpAbsoluteVolume(int volume) {
624 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700625 try {
626 mServiceLock.readLock().lock();
627 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700628 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700629 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700630 if (mService == null) Log.w(TAG, "Proxy not attached to service");
631 } catch (RemoteException e) {
632 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
633 } finally {
634 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700635 }
John Du5a0cf7a2013-07-19 11:30:34 -0700636 }
637
638 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700639 * Check if A2DP profile is streaming music.
640 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800641 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700642 *
643 * @param device BluetoothDevice device
644 */
645 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700646 try {
647 mServiceLock.readLock().lock();
648 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700649 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700650 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700651 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700652 if (mService == null) Log.w(TAG, "Proxy not attached to service");
653 return false;
654 } catch (RemoteException e) {
655 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
656 return false;
657 } finally {
658 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700659 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700660 }
661
662 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700663 * This function checks if the remote device is an AVCRP
664 * target and thus whether we should send volume keys
665 * changes or not.
Jack Hea355e5e2017-08-22 16:06:54 -0700666 *
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700667 * @hide
668 */
669 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
670 if (isEnabled() && isValidDevice(device)) {
671 ParcelUuid[] uuids = device.getUuids();
672 if (uuids == null) return false;
673
Jack Hea355e5e2017-08-22 16:06:54 -0700674 for (ParcelUuid uuid : uuids) {
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700675 if (BluetoothUuid.isAvrcpTarget(uuid)) {
676 return true;
677 }
678 }
679 }
680 return false;
681 }
682
Matthew Xiea0c68032011-06-25 21:47:07 -0700683 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800684 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800685 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800686 * @param device the remote Bluetooth device. If null, use the current
687 * active A2DP Bluetooth device.
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800688 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800689 * @hide
690 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100691 @UnsupportedAppUsage
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700692 public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800693 if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800694 try {
695 mServiceLock.readLock().lock();
696 if (mService != null && isEnabled()) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800697 return mService.getCodecStatus(device);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800698 }
699 if (mService == null) {
700 Log.w(TAG, "Proxy not attached to service");
701 }
702 return null;
703 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800704 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800705 return null;
706 } finally {
707 mServiceLock.readLock().unlock();
708 }
709 }
710
711 /**
712 * Sets the codec configuration preference.
713 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800714 * @param device the remote Bluetooth device. If null, use the current
715 * active A2DP Bluetooth device.
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800716 * @param codecConfig the codec configuration preference
717 * @hide
718 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100719 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800720 public void setCodecConfigPreference(BluetoothDevice device,
721 BluetoothCodecConfig codecConfig) {
722 if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800723 try {
724 mServiceLock.readLock().lock();
725 if (mService != null && isEnabled()) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800726 mService.setCodecConfigPreference(device, codecConfig);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800727 }
728 if (mService == null) Log.w(TAG, "Proxy not attached to service");
729 return;
730 } catch (RemoteException e) {
731 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
732 return;
733 } finally {
734 mServiceLock.readLock().unlock();
735 }
736 }
737
738 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800739 * Enables the optional codecs.
740 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800741 * @param device the remote Bluetooth device. If null, use the currect
742 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800743 * @hide
744 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100745 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800746 public void enableOptionalCodecs(BluetoothDevice device) {
747 if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
748 enableDisableOptionalCodecs(device, true);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800749 }
750
751 /**
752 * Disables the optional codecs.
753 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800754 * @param device the remote Bluetooth device. If null, use the currect
755 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800756 * @hide
757 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100758 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800759 public void disableOptionalCodecs(BluetoothDevice device) {
760 if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
761 enableDisableOptionalCodecs(device, false);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800762 }
763
764 /**
765 * Enables or disables the optional codecs.
766 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800767 * @param device the remote Bluetooth device. If null, use the currect
768 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800769 * @param enable if true, enable the optional codecs, other disable them
770 */
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800771 private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800772 try {
773 mServiceLock.readLock().lock();
774 if (mService != null && isEnabled()) {
775 if (enable) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800776 mService.enableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800777 } else {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800778 mService.disableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800779 }
780 }
781 if (mService == null) Log.w(TAG, "Proxy not attached to service");
782 return;
783 } catch (RemoteException e) {
784 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
785 return;
786 } finally {
787 mServiceLock.readLock().unlock();
788 }
789 }
790
791 /**
Antony Sargentf5772c62017-04-26 16:37:53 -0700792 * Returns whether this device supports optional codecs.
793 *
794 * @param device The device to check
795 * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700796 * OPTIONAL_CODECS_SUPPORTED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700797 * @hide
798 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100799 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700800 public int supportsOptionalCodecs(BluetoothDevice device) {
801 try {
802 mServiceLock.readLock().lock();
803 if (mService != null && isEnabled() && isValidDevice(device)) {
804 return mService.supportsOptionalCodecs(device);
805 }
806 if (mService == null) Log.w(TAG, "Proxy not attached to service");
807 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
808 } catch (RemoteException e) {
809 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
810 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
811 } finally {
812 mServiceLock.readLock().unlock();
813 }
814 }
815
816 /**
817 * Returns whether this device should have optional codecs enabled.
818 *
819 * @param device The device in question.
820 * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700821 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700822 * @hide
823 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100824 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700825 public int getOptionalCodecsEnabled(BluetoothDevice device) {
826 try {
827 mServiceLock.readLock().lock();
828 if (mService != null && isEnabled() && isValidDevice(device)) {
829 return mService.getOptionalCodecsEnabled(device);
830 }
831 if (mService == null) Log.w(TAG, "Proxy not attached to service");
832 return OPTIONAL_CODECS_PREF_UNKNOWN;
833 } catch (RemoteException e) {
834 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
835 return OPTIONAL_CODECS_PREF_UNKNOWN;
836 } finally {
837 mServiceLock.readLock().unlock();
838 }
839 }
840
841 /**
842 * Sets a persistent preference for whether a given device should have optional codecs enabled.
843 *
844 * @param device The device to set this preference for.
845 * @param value Whether the optional codecs should be enabled for this device. This should be
Jack Hea355e5e2017-08-22 16:06:54 -0700846 * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
847 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700848 * @hide
849 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100850 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700851 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
852 try {
Jack He2992cd02017-08-22 21:21:23 -0700853 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
854 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
855 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
Antony Sargentf5772c62017-04-26 16:37:53 -0700856 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
857 return;
858 }
859 mServiceLock.readLock().lock();
860 if (mService != null && isEnabled()
861 && isValidDevice(device)) {
862 mService.setOptionalCodecsEnabled(device, value);
863 }
864 if (mService == null) Log.w(TAG, "Proxy not attached to service");
865 return;
866 } catch (RemoteException e) {
867 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
868 return;
869 } finally {
870 mServiceLock.readLock().unlock();
871 }
872 }
873
874 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700875 * Helper for converting a state to a string.
876 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 * For debug use only - strings are not internationalized.
Jack Hea355e5e2017-08-22 16:06:54 -0700878 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 * @hide
880 */
Mathew Inwood31755f92018-12-20 13:53:36 +0000881 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 public static String stateToString(int state) {
883 switch (state) {
Jack Hea355e5e2017-08-22 16:06:54 -0700884 case STATE_DISCONNECTED:
885 return "disconnected";
886 case STATE_CONNECTING:
887 return "connecting";
888 case STATE_CONNECTED:
889 return "connected";
890 case STATE_DISCONNECTING:
891 return "disconnecting";
892 case STATE_PLAYING:
893 return "playing";
894 case STATE_NOT_PLAYING:
895 return "not playing";
896 default:
897 return "<unknown state " + state + ">";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
899 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800900
Matthew Xie9b693992013-10-10 11:21:40 -0700901 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800902 public void onServiceConnected(ComponentName className, IBinder service) {
903 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700904 try {
905 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600906 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700907 } finally {
908 mServiceLock.writeLock().unlock();
909 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800910
911 if (mServiceListener != null) {
912 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
913 }
914 }
Jack Hea355e5e2017-08-22 16:06:54 -0700915
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800916 public void onServiceDisconnected(ComponentName className) {
917 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700918 try {
919 mServiceLock.writeLock().lock();
920 mService = null;
921 } finally {
922 mServiceLock.writeLock().unlock();
923 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800924 if (mServiceListener != null) {
925 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
926 }
927 }
928 };
929
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700930 private boolean isEnabled() {
Jack Hea355e5e2017-08-22 16:06:54 -0700931 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
932 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700933 }
934
935 private boolean isValidDevice(BluetoothDevice device) {
Jack Hea355e5e2017-08-22 16:06:54 -0700936 if (device == null) return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700937
Jack Hea355e5e2017-08-22 16:06:54 -0700938 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
939 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700940 }
941
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800942 private static void log(String msg) {
Jack Hea355e5e2017-08-22 16:06:54 -0700943 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800944 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945}