blob: d21f76d435e67ab53f9ad06ebb33a03033a87ad4 [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;
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;
Cheney Ni4b6d6c22018-08-21 12:17:42 +080033import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.util.Log;
35
Calvin Ond7d16b92016-06-20 15:59:48 -070036import com.android.internal.annotations.GuardedBy;
37
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070038import java.util.ArrayList;
39import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070040import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070044 * This class provides the public APIs to control the Bluetooth A2DP
45 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 *
Jack Hea355e5e2017-08-22 16:06:54 -070047 * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070048 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
49 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070051 * <p> Android only supports one connected Bluetooth A2dp device at a time.
52 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070054public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080056 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070057 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070059 /**
60 * Intent used to broadcast the change in connection state of the A2DP
61 * profile.
62 *
63 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080064 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070065 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
66 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
67 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080068 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070069 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080070 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070071 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
72 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
73 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080074 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
75 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 */
77 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070078 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070079 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070081 /**
82 * Intent used to broadcast the change in the Playing state of the A2DP
83 * profile.
84 *
85 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080086 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070087 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
88 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
89 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080090 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070091 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080092 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070093 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
94 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080095 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
96 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070097 */
98 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
99 public static final String ACTION_PLAYING_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700100 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530102 /** @hide */
103 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
104 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700105 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530106
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700107 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800108 * Intent used to broadcast the selection of a connected device as active.
109 *
110 * <p>This intent will have one extra:
111 * <ul>
112 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
113 * be null if no device is active. </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)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100122 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800123 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
124 "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
125
126 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800127 * Intent used to broadcast the change in the Audio Codec state of the
128 * A2DP Source profile.
129 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800130 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800131 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700132 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
133 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
134 * connected, otherwise it is not included.</li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800135 * </ul>
136 *
137 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
138 * receive.
139 *
140 * @hide
141 */
142 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100143 @UnsupportedAppUsage
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800144 public static final String ACTION_CODEC_CONFIG_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700145 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800146
147 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700148 * A2DP sink device is streaming music. This state can be one of
149 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
150 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
151 */
Jack Hea355e5e2017-08-22 16:06:54 -0700152 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700153
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700154 /**
155 * A2DP sink device is NOT streaming music. This state can be one of
156 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
157 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
158 */
Jack Hea355e5e2017-08-22 16:06:54 -0700159 public static final int STATE_NOT_PLAYING = 11;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700160
Antony Sargentf5772c62017-04-26 16:37:53 -0700161 /**
162 * We don't have a stored preference for whether or not the given A2DP sink device supports
163 * optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700164 *
165 * @hide
166 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100167 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700168 public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
169
170 /**
171 * The given A2DP sink device does not support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700172 *
173 * @hide
174 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100175 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700176 public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
177
178 /**
179 * The given A2DP sink device does support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700180 *
181 * @hide
182 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100183 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700184 public static final int OPTIONAL_CODECS_SUPPORTED = 1;
185
186 /**
187 * We don't have a stored preference for whether optional codecs should be enabled or disabled
188 * for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700189 *
190 * @hide
191 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100192 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700193 public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
194
195 /**
196 * Optional codecs should be disabled for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700197 *
198 * @hide
199 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100200 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700201 public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
202
203 /**
Jack Hea355e5e2017-08-22 16:06:54 -0700204 * Optional codecs should be enabled for the given A2DP device.
205 *
206 * @hide
207 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100208 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700209 public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
210
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800211 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700212 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700213 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
Jack Hea355e5e2017-08-22 16:06:54 -0700214 @GuardedBy("mServiceLock")
215 private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700216 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217
Jack He2992cd02017-08-22 21:21:23 -0700218 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700219 new IBluetoothStateChangeCallback.Stub() {
220 public void onBluetoothStateChange(boolean up) {
221 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
222 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700223 if (VDBG) Log.d(TAG, "Unbinding service...");
224 try {
225 mServiceLock.writeLock().lock();
226 mService = null;
227 mContext.unbindService(mConnection);
228 } catch (Exception re) {
229 Log.e(TAG, "", re);
230 } finally {
231 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700232 }
233 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700234 try {
235 mServiceLock.readLock().lock();
236 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700237 if (VDBG) Log.d(TAG, "Binding service...");
Calvin Ond7d16b92016-06-20 15:59:48 -0700238 doBind();
fredc0f420372012-04-12 00:02:00 -0700239 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700240 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700241 Log.e(TAG, "", re);
Calvin Ond7d16b92016-06-20 15:59:48 -0700242 } finally {
243 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700244 }
245 }
246 }
Jack Hea355e5e2017-08-22 16:06:54 -0700247 };
248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 /**
250 * Create a BluetoothA2dp proxy object for interacting with the local
251 * Bluetooth A2DP service.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800253 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
254 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700255 mServiceListener = l;
256 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700257 IBluetoothManager mgr = mAdapter.getBluetoothManager();
258 if (mgr != null) {
259 try {
260 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
261 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700262 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700263 }
264 }
265
Dianne Hackborn221ea892013-08-04 16:50:16 -0700266 doBind();
267 }
268
269 boolean doBind() {
270 Intent intent = new Intent(IBluetoothA2dp.class.getName());
271 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
272 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700273 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Cheney Ni4b6d6c22018-08-21 12:17:42 +0800274 UserHandle.CURRENT_OR_SELF)) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700275 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
276 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700278 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 }
280
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100281 @UnsupportedAppUsage
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800282 /*package*/ void close() {
283 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700284 IBluetoothManager mgr = mAdapter.getBluetoothManager();
285 if (mgr != null) {
286 try {
287 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
288 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700289 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700290 }
291 }
292
Calvin Ond7d16b92016-06-20 15:59:48 -0700293 try {
294 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700295 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700296 mService = null;
297 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700298 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700299 } catch (Exception re) {
300 Log.e(TAG, "", re);
301 } finally {
302 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700303 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800304 }
305
Jack He2992cd02017-08-22 21:21:23 -0700306 @Override
fredc0f420372012-04-12 00:02:00 -0700307 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200308 // The empty finalize needs to be kept or the
309 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700310 }
Jack Hea355e5e2017-08-22 16:06:54 -0700311
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700312 /**
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800313 * Initiate connection to a profile of the remote Bluetooth device.
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700314 *
315 * <p> This API returns false in scenarios like the profile on the
316 * device is already connected or Bluetooth is not turned on.
317 * When this API returns true, it is guaranteed that
318 * connection state intent for the profile will be broadcasted with
319 * the state. Users can get the connection state of the profile
320 * from this intent.
321 *
322 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
323 * permission.
324 *
325 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700326 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700327 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100329 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700330 public boolean connect(BluetoothDevice device) {
331 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700332 try {
333 mServiceLock.readLock().lock();
Jack He2992cd02017-08-22 21:21:23 -0700334 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700335 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700336 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700337 if (mService == null) Log.w(TAG, "Proxy not attached to service");
338 return false;
339 } catch (RemoteException e) {
340 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
341 return false;
342 } finally {
343 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 }
345 }
346
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700347 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700348 * Initiate disconnection from a profile
349 *
350 * <p> This API will return false in scenarios like the profile on the
351 * Bluetooth device is not in connected state etc. When this API returns,
352 * true, it is guaranteed that the connection state change
353 * intent will be broadcasted with the state. Users can get the
354 * disconnection state of the profile from this intent.
355 *
356 * <p> If the disconnection is initiated by a remote device, the state
357 * will transition from {@link #STATE_CONNECTED} to
358 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
359 * host (local) device the state will transition from
360 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
361 * state {@link #STATE_DISCONNECTED}. The transition to
362 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
363 * two scenarios.
364 *
365 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
366 * permission.
367 *
368 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700369 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700370 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100372 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700373 public boolean disconnect(BluetoothDevice device) {
374 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700375 try {
376 mServiceLock.readLock().lock();
Jack He2992cd02017-08-22 21:21:23 -0700377 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700378 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700379 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700380 if (mService == null) Log.w(TAG, "Proxy not attached to service");
381 return false;
382 } catch (RemoteException e) {
383 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
384 return false;
385 } finally {
386 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 }
388 }
389
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700390 /**
391 * {@inheritDoc}
392 */
Jack He2992cd02017-08-22 21:21:23 -0700393 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700394 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700395 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700396 try {
397 mServiceLock.readLock().lock();
398 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700399 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700400 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700401 if (mService == null) Log.w(TAG, "Proxy not attached to service");
402 return new ArrayList<BluetoothDevice>();
403 } catch (RemoteException e) {
404 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
405 return new ArrayList<BluetoothDevice>();
406 } finally {
407 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700408 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700409 }
410
411 /**
412 * {@inheritDoc}
413 */
Jack He2992cd02017-08-22 21:21:23 -0700414 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700415 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700416 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700417 try {
418 mServiceLock.readLock().lock();
419 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700420 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700421 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700422 if (mService == null) Log.w(TAG, "Proxy not attached to service");
423 return new ArrayList<BluetoothDevice>();
424 } catch (RemoteException e) {
425 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
426 return new ArrayList<BluetoothDevice>();
427 } finally {
428 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700429 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700430 }
431
432 /**
433 * {@inheritDoc}
434 */
Jack He2992cd02017-08-22 21:21:23 -0700435 @Override
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700436 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700437 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700438 try {
439 mServiceLock.readLock().lock();
440 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700441 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700442 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700443 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700444 if (mService == null) Log.w(TAG, "Proxy not attached to service");
445 return BluetoothProfile.STATE_DISCONNECTED;
446 } catch (RemoteException e) {
447 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
448 return BluetoothProfile.STATE_DISCONNECTED;
449 } finally {
450 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700451 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700452 }
453
454 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800455 * Select a connected device as active.
456 *
457 * The active device selection is per profile. An active device's
458 * purpose is profile-specific. For example, A2DP audio streaming
459 * is to the active A2DP Sink device. If a remote device is not
460 * connected, it cannot be selected as active.
461 *
462 * <p> This API returns false in scenarios like the profile on the
463 * device is not connected or Bluetooth is not turned on.
464 * When this API returns true, it is guaranteed that the
465 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
466 * with the active device.
467 *
468 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
469 * permission.
470 *
471 * @param device the remote Bluetooth device. Could be null to clear
472 * the active device and stop streaming audio to a Bluetooth device.
473 * @return false on immediate error, true otherwise
474 * @hide
475 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100476 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800477 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
478 if (DBG) log("setActiveDevice(" + device + ")");
479 try {
480 mServiceLock.readLock().lock();
481 if (mService != null && isEnabled()
482 && ((device == null) || isValidDevice(device))) {
483 return mService.setActiveDevice(device);
484 }
485 if (mService == null) Log.w(TAG, "Proxy not attached to service");
486 return false;
487 } catch (RemoteException e) {
488 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
489 return false;
490 } finally {
491 mServiceLock.readLock().unlock();
492 }
493 }
494
495 /**
496 * Get the connected device that is active.
497 *
498 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
499 * permission.
500 *
501 * @return the connected device that is active or null if no device
502 * is active
503 * @hide
504 */
505 @RequiresPermission(Manifest.permission.BLUETOOTH)
506 @Nullable
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100507 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800508 public BluetoothDevice getActiveDevice() {
509 if (VDBG) log("getActiveDevice()");
510 try {
511 mServiceLock.readLock().lock();
512 if (mService != null && isEnabled()) {
513 return mService.getActiveDevice();
514 }
515 if (mService == null) Log.w(TAG, "Proxy not attached to service");
516 return null;
517 } catch (RemoteException e) {
518 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
519 return null;
520 } finally {
521 mServiceLock.readLock().unlock();
522 }
523 }
524
525 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700526 * Set priority of the profile
527 *
528 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700529 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700530 * {@link #PRIORITY_OFF},
531 *
532 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
533 * permission.
534 *
535 * @param device Paired bluetooth device
536 * @param priority
537 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700538 * @hide
539 */
540 public boolean setPriority(BluetoothDevice device, int priority) {
541 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700542 try {
543 mServiceLock.readLock().lock();
544 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700545 && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700546 if (priority != BluetoothProfile.PRIORITY_OFF
547 && priority != BluetoothProfile.PRIORITY_ON) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700548 return false;
549 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700550 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700551 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700552 if (mService == null) Log.w(TAG, "Proxy not attached to service");
553 return false;
554 } catch (RemoteException e) {
555 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
556 return false;
557 } finally {
558 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700559 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700560 }
561
562 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700563 * Get the priority of the profile.
564 *
565 * <p> The priority can be any of:
566 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
567 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
568 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700569 * @param device Bluetooth device
570 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700571 * @hide
572 */
Tor Norbye2d497522015-04-23 17:10:21 -0700573 @RequiresPermission(Manifest.permission.BLUETOOTH)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100574 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700575 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700576 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700577 try {
578 mServiceLock.readLock().lock();
579 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700580 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700581 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700582 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700583 if (mService == null) Log.w(TAG, "Proxy not attached to service");
584 return BluetoothProfile.PRIORITY_OFF;
585 } catch (RemoteException e) {
586 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
587 return BluetoothProfile.PRIORITY_OFF;
588 } finally {
589 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700590 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700591 }
592
593 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700594 * Checks if Avrcp device supports the absolute volume feature.
595 *
596 * @return true if device supports absolute volume
597 * @hide
598 */
599 public boolean isAvrcpAbsoluteVolumeSupported() {
600 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700601 try {
602 mServiceLock.readLock().lock();
603 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700604 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700605 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700606 if (mService == null) Log.w(TAG, "Proxy not attached to service");
607 return false;
608 } catch (RemoteException e) {
609 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
610 return false;
611 } finally {
612 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700613 }
John Du5a0cf7a2013-07-19 11:30:34 -0700614 }
615
616 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700617 * Tells remote device to set an absolute volume. Only if absolute volume is supported
618 *
619 * @param volume Absolute volume to be set on AVRCP side
620 * @hide
621 */
622 public void setAvrcpAbsoluteVolume(int volume) {
623 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700624 try {
625 mServiceLock.readLock().lock();
626 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700627 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700628 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700629 if (mService == null) Log.w(TAG, "Proxy not attached to service");
630 } catch (RemoteException e) {
631 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
632 } finally {
633 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700634 }
John Du5a0cf7a2013-07-19 11:30:34 -0700635 }
636
637 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700638 * Check if A2DP profile is streaming music.
639 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800640 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700641 *
642 * @param device BluetoothDevice device
643 */
644 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700645 try {
646 mServiceLock.readLock().lock();
647 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700648 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700649 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700650 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700651 if (mService == null) Log.w(TAG, "Proxy not attached to service");
652 return false;
653 } catch (RemoteException e) {
654 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
655 return false;
656 } finally {
657 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700658 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700659 }
660
661 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700662 * This function checks if the remote device is an AVCRP
663 * target and thus whether we should send volume keys
664 * changes or not.
Jack Hea355e5e2017-08-22 16:06:54 -0700665 *
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700666 * @hide
667 */
668 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
669 if (isEnabled() && isValidDevice(device)) {
670 ParcelUuid[] uuids = device.getUuids();
671 if (uuids == null) return false;
672
Jack Hea355e5e2017-08-22 16:06:54 -0700673 for (ParcelUuid uuid : uuids) {
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700674 if (BluetoothUuid.isAvrcpTarget(uuid)) {
675 return true;
676 }
677 }
678 }
679 return false;
680 }
681
Matthew Xiea0c68032011-06-25 21:47:07 -0700682 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800683 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800684 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800685 * @param device the remote Bluetooth device. If null, use the current
686 * active A2DP Bluetooth device.
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800687 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800688 * @hide
689 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100690 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800691 public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
692 if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800693 try {
694 mServiceLock.readLock().lock();
695 if (mService != null && isEnabled()) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800696 return mService.getCodecStatus(device);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800697 }
698 if (mService == null) {
699 Log.w(TAG, "Proxy not attached to service");
700 }
701 return null;
702 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800703 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800704 return null;
705 } finally {
706 mServiceLock.readLock().unlock();
707 }
708 }
709
710 /**
711 * Sets the codec configuration preference.
712 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800713 * @param device the remote Bluetooth device. If null, use the current
714 * active A2DP Bluetooth device.
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800715 * @param codecConfig the codec configuration preference
716 * @hide
717 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100718 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800719 public void setCodecConfigPreference(BluetoothDevice device,
720 BluetoothCodecConfig codecConfig) {
721 if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800722 try {
723 mServiceLock.readLock().lock();
724 if (mService != null && isEnabled()) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800725 mService.setCodecConfigPreference(device, codecConfig);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800726 }
727 if (mService == null) Log.w(TAG, "Proxy not attached to service");
728 return;
729 } catch (RemoteException e) {
730 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
731 return;
732 } finally {
733 mServiceLock.readLock().unlock();
734 }
735 }
736
737 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800738 * Enables the optional codecs.
739 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800740 * @param device the remote Bluetooth device. If null, use the currect
741 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800742 * @hide
743 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100744 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800745 public void enableOptionalCodecs(BluetoothDevice device) {
746 if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
747 enableDisableOptionalCodecs(device, true);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800748 }
749
750 /**
751 * Disables the optional codecs.
752 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800753 * @param device the remote Bluetooth device. If null, use the currect
754 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800755 * @hide
756 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100757 @UnsupportedAppUsage
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800758 public void disableOptionalCodecs(BluetoothDevice device) {
759 if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
760 enableDisableOptionalCodecs(device, false);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800761 }
762
763 /**
764 * Enables or disables the optional codecs.
765 *
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800766 * @param device the remote Bluetooth device. If null, use the currect
767 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800768 * @param enable if true, enable the optional codecs, other disable them
769 */
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800770 private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800771 try {
772 mServiceLock.readLock().lock();
773 if (mService != null && isEnabled()) {
774 if (enable) {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800775 mService.enableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800776 } else {
Pavlin Radoslavov502af212018-01-03 19:38:39 -0800777 mService.disableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800778 }
779 }
780 if (mService == null) Log.w(TAG, "Proxy not attached to service");
781 return;
782 } catch (RemoteException e) {
783 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
784 return;
785 } finally {
786 mServiceLock.readLock().unlock();
787 }
788 }
789
790 /**
Antony Sargentf5772c62017-04-26 16:37:53 -0700791 * Returns whether this device supports optional codecs.
792 *
793 * @param device The device to check
794 * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700795 * OPTIONAL_CODECS_SUPPORTED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700796 * @hide
797 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100798 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700799 public int supportsOptionalCodecs(BluetoothDevice device) {
800 try {
801 mServiceLock.readLock().lock();
802 if (mService != null && isEnabled() && isValidDevice(device)) {
803 return mService.supportsOptionalCodecs(device);
804 }
805 if (mService == null) Log.w(TAG, "Proxy not attached to service");
806 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
807 } catch (RemoteException e) {
808 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
809 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
810 } finally {
811 mServiceLock.readLock().unlock();
812 }
813 }
814
815 /**
816 * Returns whether this device should have optional codecs enabled.
817 *
818 * @param device The device in question.
819 * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700820 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700821 * @hide
822 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100823 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700824 public int getOptionalCodecsEnabled(BluetoothDevice device) {
825 try {
826 mServiceLock.readLock().lock();
827 if (mService != null && isEnabled() && isValidDevice(device)) {
828 return mService.getOptionalCodecsEnabled(device);
829 }
830 if (mService == null) Log.w(TAG, "Proxy not attached to service");
831 return OPTIONAL_CODECS_PREF_UNKNOWN;
832 } catch (RemoteException e) {
833 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
834 return OPTIONAL_CODECS_PREF_UNKNOWN;
835 } finally {
836 mServiceLock.readLock().unlock();
837 }
838 }
839
840 /**
841 * Sets a persistent preference for whether a given device should have optional codecs enabled.
842 *
843 * @param device The device to set this preference for.
844 * @param value Whether the optional codecs should be enabled for this device. This should be
Jack Hea355e5e2017-08-22 16:06:54 -0700845 * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
846 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700847 * @hide
848 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100849 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700850 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
851 try {
Jack He2992cd02017-08-22 21:21:23 -0700852 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
853 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
854 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
Antony Sargentf5772c62017-04-26 16:37:53 -0700855 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
856 return;
857 }
858 mServiceLock.readLock().lock();
859 if (mService != null && isEnabled()
860 && isValidDevice(device)) {
861 mService.setOptionalCodecsEnabled(device, value);
862 }
863 if (mService == null) Log.w(TAG, "Proxy not attached to service");
864 return;
865 } catch (RemoteException e) {
866 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
867 return;
868 } finally {
869 mServiceLock.readLock().unlock();
870 }
871 }
872
873 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700874 * Helper for converting a state to a string.
875 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 * For debug use only - strings are not internationalized.
Jack Hea355e5e2017-08-22 16:06:54 -0700877 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 * @hide
879 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100880 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 public static String stateToString(int state) {
882 switch (state) {
Jack Hea355e5e2017-08-22 16:06:54 -0700883 case STATE_DISCONNECTED:
884 return "disconnected";
885 case STATE_CONNECTING:
886 return "connecting";
887 case STATE_CONNECTED:
888 return "connected";
889 case STATE_DISCONNECTING:
890 return "disconnecting";
891 case STATE_PLAYING:
892 return "playing";
893 case STATE_NOT_PLAYING:
894 return "not playing";
895 default:
896 return "<unknown state " + state + ">";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 }
898 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800899
Matthew Xie9b693992013-10-10 11:21:40 -0700900 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800901 public void onServiceConnected(ComponentName className, IBinder service) {
902 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700903 try {
904 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600905 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700906 } finally {
907 mServiceLock.writeLock().unlock();
908 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800909
910 if (mServiceListener != null) {
911 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
912 }
913 }
Jack Hea355e5e2017-08-22 16:06:54 -0700914
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800915 public void onServiceDisconnected(ComponentName className) {
916 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700917 try {
918 mServiceLock.writeLock().lock();
919 mService = null;
920 } finally {
921 mServiceLock.writeLock().unlock();
922 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800923 if (mServiceListener != null) {
924 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
925 }
926 }
927 };
928
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700929 private boolean isEnabled() {
Jack Hea355e5e2017-08-22 16:06:54 -0700930 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
931 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700932 }
933
934 private boolean isValidDevice(BluetoothDevice device) {
Jack Hea355e5e2017-08-22 16:06:54 -0700935 if (device == null) return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700936
Jack Hea355e5e2017-08-22 16:06:54 -0700937 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
938 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700939 }
940
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800941 private static void log(String msg) {
Jack Hea355e5e2017-08-22 16:06:54 -0700942 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800943 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944}