blob: accdd8dc64bdc69328ac9defdd932b10c31e098b [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;
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -080020import android.annotation.NonNull;
Pavlin Radoslavov64a94352017-12-19 13:20:06 -080021import android.annotation.Nullable;
Tor Norbye2d497522015-04-23 17:10:21 -070022import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -080025import android.annotation.SystemApi;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010026import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Context;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060028import android.os.Binder;
Mathew Inwood8c854f82018-09-14 12:35:36 +010029import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070031import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070032import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.Log;
34
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070035import java.util.ArrayList;
36import java.util.List;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070037
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
39/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070040 * This class provides the public APIs to control the Bluetooth A2DP
41 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 *
Jack Hea355e5e2017-08-22 16:06:54 -070043 * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070044 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
45 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070047 * <p> Android only supports one connected Bluetooth A2dp device at a time.
48 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070050public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080052 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070053 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070055 /**
56 * Intent used to broadcast the change in connection state of the A2DP
57 * profile.
58 *
59 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080060 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070061 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
62 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
63 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080064 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070065 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080066 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070067 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
68 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
69 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080070 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
71 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 */
73 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070074 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070075 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070077 /**
78 * Intent used to broadcast the change in the Playing state of the A2DP
79 * profile.
80 *
81 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080082 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070083 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
84 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
85 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080086 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070087 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080088 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070089 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
90 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080091 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
92 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070093 */
94 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
95 public static final String ACTION_PLAYING_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070096 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
Hemant Gupta3b76a4b2014-02-14 19:53:31 +053098 /** @hide */
99 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
100 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700101 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530102
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700103 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800104 * Intent used to broadcast the selection of a connected device as active.
105 *
106 * <p>This intent will have one extra:
107 * <ul>
108 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
109 * be null if no device is active. </li>
110 * </ul>
111 *
112 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
113 * receive.
114 *
115 * @hide
116 */
117 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100118 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800119 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
120 "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
121
122 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800123 * Intent used to broadcast the change in the Audio Codec state of the
124 * A2DP Source profile.
125 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800126 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800127 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700128 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
129 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
130 * connected, otherwise it is not included.</li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800131 * </ul>
132 *
133 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
134 * receive.
135 *
136 * @hide
137 */
138 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100139 @UnsupportedAppUsage
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800140 public static final String ACTION_CODEC_CONFIG_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700141 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800142
143 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700144 * A2DP sink device is streaming music. This state can be one of
145 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
146 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
147 */
Jack Hea355e5e2017-08-22 16:06:54 -0700148 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700149
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700150 /**
151 * A2DP sink device is NOT streaming music. This state can be one of
152 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
153 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
154 */
Jack Hea355e5e2017-08-22 16:06:54 -0700155 public static final int STATE_NOT_PLAYING = 11;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700156
Antony Sargentf5772c62017-04-26 16:37:53 -0700157 /**
158 * We don't have a stored preference for whether or not the given A2DP sink device supports
159 * optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700160 *
161 * @hide
162 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100163 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700164 public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
165
166 /**
167 * The given A2DP sink device does not support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700168 *
169 * @hide
170 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100171 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700172 public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
173
174 /**
175 * The given A2DP sink device does support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700176 *
177 * @hide
178 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100179 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700180 public static final int OPTIONAL_CODECS_SUPPORTED = 1;
181
182 /**
183 * We don't have a stored preference for whether optional codecs should be enabled or disabled
184 * for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700185 *
186 * @hide
187 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100188 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700189 public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
190
191 /**
192 * Optional codecs should be disabled for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700193 *
194 * @hide
195 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100196 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700197 public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
198
199 /**
Jack Hea355e5e2017-08-22 16:06:54 -0700200 * Optional codecs should be enabled for the given A2DP device.
201 *
202 * @hide
203 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100204 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700205 public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
206
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700207 private BluetoothAdapter mAdapter;
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800208 private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
209 new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
210 IBluetoothA2dp.class.getName()) {
211 @Override
212 public IBluetoothA2dp getServiceInterface(IBinder service) {
213 return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
fredc0f420372012-04-12 00:02:00 -0700214 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800215 };
Jack Hea355e5e2017-08-22 16:06:54 -0700216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 /**
218 * Create a BluetoothA2dp proxy object for interacting with the local
219 * Bluetooth A2DP service.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 */
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800221 /*package*/ BluetoothA2dp(Context context, ServiceListener listener) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700222 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800223 mProfileConnector.connect(context, listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100226 @UnsupportedAppUsage
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800227 /*package*/ void close() {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800228 mProfileConnector.disconnect();
229 }
fredc0f420372012-04-12 00:02:00 -0700230
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800231 private IBluetoothA2dp getService() {
232 return mProfileConnector.getService();
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800233 }
234
Jack He2992cd02017-08-22 21:21:23 -0700235 @Override
fredc0f420372012-04-12 00:02:00 -0700236 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200237 // The empty finalize needs to be kept or the
238 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700239 }
Jack Hea355e5e2017-08-22 16:06:54 -0700240
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700241 /**
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800242 * Initiate connection to a profile of the remote Bluetooth device.
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700243 *
244 * <p> This API returns false in scenarios like the profile on the
245 * device is already connected or Bluetooth is not turned on.
246 * When this API returns true, it is guaranteed that
247 * connection state intent for the profile will be broadcasted with
248 * the state. Users can get the connection state of the profile
249 * from this intent.
250 *
251 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
252 * permission.
253 *
254 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700255 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700256 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100258 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700259 public boolean connect(BluetoothDevice device) {
260 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700261 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800262 final IBluetoothA2dp service = getService();
263 if (service != null && isEnabled() && isValidDevice(device)) {
264 return service.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700265 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800266 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700267 return false;
268 } catch (RemoteException e) {
269 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
270 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 }
272 }
273
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700274 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700275 * Initiate disconnection from a profile
276 *
277 * <p> This API will return false in scenarios like the profile on the
278 * Bluetooth device is not in connected state etc. When this API returns,
279 * true, it is guaranteed that the connection state change
280 * intent will be broadcasted with the state. Users can get the
281 * disconnection state of the profile from this intent.
282 *
283 * <p> If the disconnection is initiated by a remote device, the state
284 * will transition from {@link #STATE_CONNECTED} to
285 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
286 * host (local) device the state will transition from
287 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
288 * state {@link #STATE_DISCONNECTED}. The transition to
289 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
290 * two scenarios.
291 *
292 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
293 * permission.
294 *
295 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700296 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700297 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100299 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700300 public boolean disconnect(BluetoothDevice device) {
301 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700302 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800303 final IBluetoothA2dp service = getService();
304 if (service != null && isEnabled() && isValidDevice(device)) {
305 return service.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700306 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800307 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700308 return false;
309 } catch (RemoteException e) {
310 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
311 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313 }
314
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700315 /**
316 * {@inheritDoc}
317 */
Jack He2992cd02017-08-22 21:21:23 -0700318 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700319 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700320 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700321 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800322 final IBluetoothA2dp service = getService();
323 if (service != null && isEnabled()) {
324 return service.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700325 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800326 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700327 return new ArrayList<BluetoothDevice>();
328 } catch (RemoteException e) {
329 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
330 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700331 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700332 }
333
334 /**
335 * {@inheritDoc}
336 */
Jack He2992cd02017-08-22 21:21:23 -0700337 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700338 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700339 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700340 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800341 final IBluetoothA2dp service = getService();
342 if (service != null && isEnabled()) {
343 return service.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700344 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800345 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700346 return new ArrayList<BluetoothDevice>();
347 } catch (RemoteException e) {
348 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
349 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700350 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700351 }
352
353 /**
354 * {@inheritDoc}
355 */
Jack He2992cd02017-08-22 21:21:23 -0700356 @Override
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700357 public @BtProfileState int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700358 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700359 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800360 final IBluetoothA2dp service = getService();
361 if (service != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700362 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800363 return service.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700364 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800365 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700366 return BluetoothProfile.STATE_DISCONNECTED;
367 } catch (RemoteException e) {
368 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
369 return BluetoothProfile.STATE_DISCONNECTED;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700370 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700371 }
372
373 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800374 * Select a connected device as active.
375 *
376 * The active device selection is per profile. An active device's
377 * purpose is profile-specific. For example, A2DP audio streaming
378 * is to the active A2DP Sink device. If a remote device is not
379 * connected, it cannot be selected as active.
380 *
381 * <p> This API returns false in scenarios like the profile on the
382 * device is not connected or Bluetooth is not turned on.
383 * When this API returns true, it is guaranteed that the
384 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
385 * with the active device.
386 *
387 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
388 * permission.
389 *
390 * @param device the remote Bluetooth device. Could be null to clear
391 * the active device and stop streaming audio to a Bluetooth device.
392 * @return false on immediate error, true otherwise
393 * @hide
394 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100395 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800396 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
397 if (DBG) log("setActiveDevice(" + device + ")");
398 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800399 final IBluetoothA2dp service = getService();
400 if (service != null && isEnabled()
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800401 && ((device == null) || isValidDevice(device))) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800402 return service.setActiveDevice(device);
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800403 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800404 if (service == null) Log.w(TAG, "Proxy not attached to service");
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800405 return false;
406 } catch (RemoteException e) {
407 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
408 return false;
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800409 }
410 }
411
412 /**
413 * Get the connected device that is active.
414 *
415 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
416 * permission.
417 *
418 * @return the connected device that is active or null if no device
419 * is active
420 * @hide
421 */
422 @RequiresPermission(Manifest.permission.BLUETOOTH)
423 @Nullable
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100424 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800425 public BluetoothDevice getActiveDevice() {
426 if (VDBG) log("getActiveDevice()");
427 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800428 final IBluetoothA2dp service = getService();
429 if (service != null && isEnabled()) {
430 return service.getActiveDevice();
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800431 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800432 if (service == null) Log.w(TAG, "Proxy not attached to service");
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800433 return null;
434 } catch (RemoteException e) {
435 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
436 return null;
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800437 }
438 }
439
440 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700441 * Set priority of the profile
442 *
443 * <p> The device should already be paired.
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800444 * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700445 *
446 * @param device Paired bluetooth device
447 * @param priority
448 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700449 * @hide
450 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800451 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700452 public boolean setPriority(BluetoothDevice device, int priority) {
453 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800454 return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
455 }
456
457 /**
458 * Set connection policy of the profile
459 *
460 * <p> The device should already be paired.
461 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
462 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
463 *
464 * @param device Paired bluetooth device
465 * @param connectionPolicy is the connection policy to set to for this profile
466 * @return true if connectionPolicy is set, false on error
467 * @hide
468 */
469 @SystemApi
470 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
471 public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
472 @ConnectionPolicy int connectionPolicy) {
473 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700474 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800475 final IBluetoothA2dp service = getService();
476 if (service != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700477 && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800478 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
479 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700480 return false;
481 }
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800482 return service.setConnectionPolicy(device, connectionPolicy);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700483 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800484 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700485 return false;
486 } catch (RemoteException e) {
487 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
488 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700489 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700490 }
491
492 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700493 * Get the priority of the profile.
494 *
495 * <p> The priority can be any of:
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800496 * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700497 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700498 * @param device Bluetooth device
499 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700500 * @hide
501 */
Tor Norbye2d497522015-04-23 17:10:21 -0700502 @RequiresPermission(Manifest.permission.BLUETOOTH)
Mathew Inwood8c854f82018-09-14 12:35:36 +0100503 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700504 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700505 if (VDBG) log("getPriority(" + device + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800506 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
507 }
508
509 /**
510 * Get the connection policy of the profile.
511 *
512 * <p> The connection policy can be any of:
513 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
514 * {@link #CONNECTION_POLICY_UNKNOWN}
515 *
516 * @param device Bluetooth device
517 * @return connection policy of the device
518 * @hide
519 */
520 @SystemApi
521 @RequiresPermission(Manifest.permission.BLUETOOTH)
522 public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
523 if (VDBG) log("getConnectionPolicy(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700524 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800525 final IBluetoothA2dp service = getService();
526 if (service != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700527 && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800528 return service.getConnectionPolicy(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700529 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800530 if (service == null) Log.w(TAG, "Proxy not attached to service");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800531 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Calvin Ond7d16b92016-06-20 15:59:48 -0700532 } catch (RemoteException e) {
533 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800534 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700535 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700536 }
537
538 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700539 * Checks if Avrcp device supports the absolute volume feature.
540 *
541 * @return true if device supports absolute volume
542 * @hide
543 */
544 public boolean isAvrcpAbsoluteVolumeSupported() {
545 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700546 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800547 final IBluetoothA2dp service = getService();
548 if (service != null && isEnabled()) {
549 return service.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700550 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800551 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700552 return false;
553 } catch (RemoteException e) {
554 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
555 return false;
John Du5a0cf7a2013-07-19 11:30:34 -0700556 }
John Du5a0cf7a2013-07-19 11:30:34 -0700557 }
558
559 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700560 * Tells remote device to set an absolute volume. Only if absolute volume is supported
561 *
562 * @param volume Absolute volume to be set on AVRCP side
563 * @hide
564 */
565 public void setAvrcpAbsoluteVolume(int volume) {
566 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700567 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800568 final IBluetoothA2dp service = getService();
569 if (service != null && isEnabled()) {
570 service.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700571 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800572 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700573 } catch (RemoteException e) {
574 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
John Du5a0cf7a2013-07-19 11:30:34 -0700575 }
John Du5a0cf7a2013-07-19 11:30:34 -0700576 }
577
578 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700579 * Check if A2DP profile is streaming music.
580 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800581 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700582 *
583 * @param device BluetoothDevice device
584 */
585 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700586 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800587 final IBluetoothA2dp service = getService();
588 if (service != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700589 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800590 return service.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700591 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800592 if (service == null) Log.w(TAG, "Proxy not attached to service");
Calvin Ond7d16b92016-06-20 15:59:48 -0700593 return false;
594 } catch (RemoteException e) {
595 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
596 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700597 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700598 }
599
600 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700601 * This function checks if the remote device is an AVCRP
602 * target and thus whether we should send volume keys
603 * changes or not.
Jack Hea355e5e2017-08-22 16:06:54 -0700604 *
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700605 * @hide
606 */
607 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
608 if (isEnabled() && isValidDevice(device)) {
609 ParcelUuid[] uuids = device.getUuids();
610 if (uuids == null) return false;
611
Jack Hea355e5e2017-08-22 16:06:54 -0700612 for (ParcelUuid uuid : uuids) {
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700613 if (BluetoothUuid.isAvrcpTarget(uuid)) {
614 return true;
615 }
616 }
617 }
618 return false;
619 }
620
Matthew Xiea0c68032011-06-25 21:47:07 -0700621 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800622 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800623 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800624 * @param device the remote Bluetooth device. If null, use the current
625 * active A2DP Bluetooth device.
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800626 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800627 * @hide
628 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100629 @UnsupportedAppUsage
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700630 public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800631 if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800632 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800633 final IBluetoothA2dp service = getService();
634 if (service != null && isEnabled()) {
635 return service.getCodecStatus(device);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800636 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800637 if (service == null) {
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800638 Log.w(TAG, "Proxy not attached to service");
639 }
640 return null;
641 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800642 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800643 return null;
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800644 }
645 }
646
647 /**
648 * Sets the codec configuration preference.
649 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800650 * @param device the remote Bluetooth device. If null, use the current
651 * active A2DP Bluetooth device.
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800652 * @param codecConfig the codec configuration preference
653 * @hide
654 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100655 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800656 public void setCodecConfigPreference(BluetoothDevice device,
657 BluetoothCodecConfig codecConfig) {
658 if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800659 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800660 final IBluetoothA2dp service = getService();
661 if (service != null && isEnabled()) {
662 service.setCodecConfigPreference(device, codecConfig);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800663 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800664 if (service == null) Log.w(TAG, "Proxy not attached to service");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800665 return;
666 } catch (RemoteException e) {
667 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
668 return;
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800669 }
670 }
671
672 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800673 * Enables the optional codecs.
674 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800675 * @param device the remote Bluetooth device. If null, use the currect
676 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800677 * @hide
678 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100679 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800680 public void enableOptionalCodecs(BluetoothDevice device) {
681 if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
682 enableDisableOptionalCodecs(device, true);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800683 }
684
685 /**
686 * Disables the optional codecs.
687 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800688 * @param device the remote Bluetooth device. If null, use the currect
689 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800690 * @hide
691 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100692 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800693 public void disableOptionalCodecs(BluetoothDevice device) {
694 if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
695 enableDisableOptionalCodecs(device, false);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800696 }
697
698 /**
699 * Enables or disables the optional codecs.
700 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800701 * @param device the remote Bluetooth device. If null, use the currect
702 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800703 * @param enable if true, enable the optional codecs, other disable them
704 */
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800705 private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800706 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800707 final IBluetoothA2dp service = getService();
708 if (service != null && isEnabled()) {
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800709 if (enable) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800710 service.enableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800711 } else {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800712 service.disableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800713 }
714 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800715 if (service == null) Log.w(TAG, "Proxy not attached to service");
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800716 return;
717 } catch (RemoteException e) {
718 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
719 return;
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800720 }
721 }
722
723 /**
Antony Sargentf5772c62017-04-26 16:37:53 -0700724 * Returns whether this device supports optional codecs.
725 *
726 * @param device The device to check
727 * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700728 * OPTIONAL_CODECS_SUPPORTED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700729 * @hide
730 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100731 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700732 public int supportsOptionalCodecs(BluetoothDevice device) {
733 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800734 final IBluetoothA2dp service = getService();
735 if (service != null && isEnabled() && isValidDevice(device)) {
736 return service.supportsOptionalCodecs(device);
Antony Sargentf5772c62017-04-26 16:37:53 -0700737 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800738 if (service == null) Log.w(TAG, "Proxy not attached to service");
Antony Sargentf5772c62017-04-26 16:37:53 -0700739 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
740 } catch (RemoteException e) {
741 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
742 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
Antony Sargentf5772c62017-04-26 16:37:53 -0700743 }
744 }
745
746 /**
747 * Returns whether this device should have optional codecs enabled.
748 *
749 * @param device The device in question.
750 * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700751 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700752 * @hide
753 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100754 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700755 public int getOptionalCodecsEnabled(BluetoothDevice device) {
756 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800757 final IBluetoothA2dp service = getService();
758 if (service != null && isEnabled() && isValidDevice(device)) {
759 return service.getOptionalCodecsEnabled(device);
Antony Sargentf5772c62017-04-26 16:37:53 -0700760 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800761 if (service == null) Log.w(TAG, "Proxy not attached to service");
Antony Sargentf5772c62017-04-26 16:37:53 -0700762 return OPTIONAL_CODECS_PREF_UNKNOWN;
763 } catch (RemoteException e) {
764 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
765 return OPTIONAL_CODECS_PREF_UNKNOWN;
Antony Sargentf5772c62017-04-26 16:37:53 -0700766 }
767 }
768
769 /**
770 * Sets a persistent preference for whether a given device should have optional codecs enabled.
771 *
772 * @param device The device to set this preference for.
773 * @param value Whether the optional codecs should be enabled for this device. This should be
Jack Hea355e5e2017-08-22 16:06:54 -0700774 * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
775 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700776 * @hide
777 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100778 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700779 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
780 try {
Jack He2992cd02017-08-22 21:21:23 -0700781 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
782 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
783 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
Antony Sargentf5772c62017-04-26 16:37:53 -0700784 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
785 return;
786 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800787 final IBluetoothA2dp service = getService();
788 if (service != null && isEnabled()
Antony Sargentf5772c62017-04-26 16:37:53 -0700789 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800790 service.setOptionalCodecsEnabled(device, value);
Antony Sargentf5772c62017-04-26 16:37:53 -0700791 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800792 if (service == null) Log.w(TAG, "Proxy not attached to service");
Antony Sargentf5772c62017-04-26 16:37:53 -0700793 return;
794 } catch (RemoteException e) {
795 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
796 return;
Antony Sargentf5772c62017-04-26 16:37:53 -0700797 }
798 }
799
800 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700801 * Helper for converting a state to a string.
802 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 * For debug use only - strings are not internationalized.
Jack Hea355e5e2017-08-22 16:06:54 -0700804 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 * @hide
806 */
Mathew Inwood31755f92018-12-20 13:53:36 +0000807 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 public static String stateToString(int state) {
809 switch (state) {
Jack Hea355e5e2017-08-22 16:06:54 -0700810 case STATE_DISCONNECTED:
811 return "disconnected";
812 case STATE_CONNECTING:
813 return "connecting";
814 case STATE_CONNECTED:
815 return "connected";
816 case STATE_DISCONNECTING:
817 return "disconnecting";
818 case STATE_PLAYING:
819 return "playing";
820 case STATE_NOT_PLAYING:
821 return "not playing";
822 default:
823 return "<unknown state " + state + ">";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
825 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800826
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700827 private boolean isEnabled() {
Jack Hea355e5e2017-08-22 16:06:54 -0700828 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
829 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700830 }
831
832 private boolean isValidDevice(BluetoothDevice device) {
Jack Hea355e5e2017-08-22 16:06:54 -0700833 if (device == null) return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700834
Jack Hea355e5e2017-08-22 16:06:54 -0700835 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
836 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700837 }
838
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800839 private static void log(String msg) {
Jack Hea355e5e2017-08-22 16:06:54 -0700840 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800841 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842}