blob: 798e00d789c5a2ffd9f0ad023b34f3f116f81920 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.Log;
34
Calvin Ond7d16b92016-06-20 15:59:48 -070035import com.android.internal.annotations.GuardedBy;
36
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070037import java.util.ArrayList;
38import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070039import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070040
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070043 * This class provides the public APIs to control the Bluetooth A2DP
44 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 *
Jack Hea355e5e2017-08-22 16:06:54 -070046 * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070047 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
48 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070050 * <p> Android only supports one connected Bluetooth A2dp device at a time.
51 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070053public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080055 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070056 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070058 /**
59 * Intent used to broadcast the change in connection state of the A2DP
60 * profile.
61 *
62 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080063 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070064 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
65 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
66 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080067 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070068 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080069 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070070 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
71 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
72 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080073 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
74 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 */
76 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070077 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070078 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070080 /**
81 * Intent used to broadcast the change in the Playing state of the A2DP
82 * profile.
83 *
84 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080085 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070086 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
87 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
88 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080089 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070090 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080091 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070092 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
93 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080094 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
95 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070096 */
97 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
98 public static final String ACTION_PLAYING_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070099 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530101 /** @hide */
102 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
103 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700104 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530105
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700106 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800107 * Intent used to broadcast the selection of a connected device as active.
108 *
109 * <p>This intent will have one extra:
110 * <ul>
111 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
112 * be null if no device is active. </li>
113 * </ul>
114 *
115 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
116 * receive.
117 *
118 * @hide
119 */
120 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100121 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800122 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
123 "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
124
125 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800126 * Intent used to broadcast the change in the Audio Codec state of the
127 * A2DP Source profile.
128 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800129 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800130 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700131 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
132 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
133 * connected, otherwise it is not included.</li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800134 * </ul>
135 *
136 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
137 * receive.
138 *
139 * @hide
140 */
141 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100142 @UnsupportedAppUsage
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800143 public static final String ACTION_CODEC_CONFIG_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700144 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800145
146 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700147 * A2DP sink device is streaming music. This state can be one of
148 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
149 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
150 */
Jack Hea355e5e2017-08-22 16:06:54 -0700151 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700152
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700153 /**
154 * A2DP sink device is NOT streaming music. This state can be one of
155 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
156 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
157 */
Jack Hea355e5e2017-08-22 16:06:54 -0700158 public static final int STATE_NOT_PLAYING = 11;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700159
Antony Sargentf5772c62017-04-26 16:37:53 -0700160 /**
161 * We don't have a stored preference for whether or not the given A2DP sink device supports
162 * optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700163 *
164 * @hide
165 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100166 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700167 public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
168
169 /**
170 * The given A2DP sink device does not support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700171 *
172 * @hide
173 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100174 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700175 public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
176
177 /**
178 * The given A2DP sink device does support optional codecs.
Jack Hea355e5e2017-08-22 16:06:54 -0700179 *
180 * @hide
181 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100182 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700183 public static final int OPTIONAL_CODECS_SUPPORTED = 1;
184
185 /**
186 * We don't have a stored preference for whether optional codecs should be enabled or disabled
187 * for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700188 *
189 * @hide
190 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100191 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700192 public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
193
194 /**
195 * Optional codecs should be disabled for the given A2DP device.
Jack Hea355e5e2017-08-22 16:06:54 -0700196 *
197 * @hide
198 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100199 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700200 public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
201
202 /**
Jack Hea355e5e2017-08-22 16:06:54 -0700203 * Optional codecs should be enabled for the given A2DP device.
204 *
205 * @hide
206 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100207 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700208 public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
209
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800210 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700211 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700212 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
Jack Hea355e5e2017-08-22 16:06:54 -0700213 @GuardedBy("mServiceLock")
214 private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700215 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216
Jack He2992cd02017-08-22 21:21:23 -0700217 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700218 new IBluetoothStateChangeCallback.Stub() {
219 public void onBluetoothStateChange(boolean up) {
220 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
221 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700222 if (VDBG) Log.d(TAG, "Unbinding service...");
223 try {
224 mServiceLock.writeLock().lock();
225 mService = null;
226 mContext.unbindService(mConnection);
227 } catch (Exception re) {
228 Log.e(TAG, "", re);
229 } finally {
230 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700231 }
232 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700233 try {
234 mServiceLock.readLock().lock();
235 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700236 if (VDBG) Log.d(TAG, "Binding service...");
Calvin Ond7d16b92016-06-20 15:59:48 -0700237 doBind();
fredc0f420372012-04-12 00:02:00 -0700238 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700239 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700240 Log.e(TAG, "", re);
Calvin Ond7d16b92016-06-20 15:59:48 -0700241 } finally {
242 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700243 }
244 }
245 }
Jack Hea355e5e2017-08-22 16:06:54 -0700246 };
247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 /**
249 * Create a BluetoothA2dp proxy object for interacting with the local
250 * Bluetooth A2DP service.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800252 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
253 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700254 mServiceListener = l;
255 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700256 IBluetoothManager mgr = mAdapter.getBluetoothManager();
257 if (mgr != null) {
258 try {
259 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
260 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700261 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700262 }
263 }
264
Dianne Hackborn221ea892013-08-04 16:50:16 -0700265 doBind();
266 }
267
268 boolean doBind() {
269 Intent intent = new Intent(IBluetoothA2dp.class.getName());
270 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
271 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700272 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
273 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700274 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
275 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700277 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 }
279
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100280 @UnsupportedAppUsage
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800281 /*package*/ void close() {
282 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700283 IBluetoothManager mgr = mAdapter.getBluetoothManager();
284 if (mgr != null) {
285 try {
286 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
287 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700288 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700289 }
290 }
291
Calvin Ond7d16b92016-06-20 15:59:48 -0700292 try {
293 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700294 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700295 mService = null;
296 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700297 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700298 } catch (Exception re) {
299 Log.e(TAG, "", re);
300 } finally {
301 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700302 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800303 }
304
Jack He2992cd02017-08-22 21:21:23 -0700305 @Override
fredc0f420372012-04-12 00:02:00 -0700306 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200307 // The empty finalize needs to be kept or the
308 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700309 }
Jack Hea355e5e2017-08-22 16:06:54 -0700310
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700311 /**
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800312 * Initiate connection to a profile of the remote Bluetooth device.
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700313 *
314 * <p> This API returns false in scenarios like the profile on the
315 * device is already connected or Bluetooth is not turned on.
316 * When this API returns true, it is guaranteed that
317 * connection state intent for the profile will be broadcasted with
318 * the state. Users can get the connection state of the profile
319 * from this intent.
320 *
321 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
322 * permission.
323 *
324 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700325 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700326 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100328 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700329 public boolean connect(BluetoothDevice device) {
330 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700331 try {
332 mServiceLock.readLock().lock();
Jack He2992cd02017-08-22 21:21:23 -0700333 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700334 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700335 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700336 if (mService == null) Log.w(TAG, "Proxy not attached to service");
337 return false;
338 } catch (RemoteException e) {
339 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
340 return false;
341 } finally {
342 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344 }
345
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700346 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700347 * Initiate disconnection from a profile
348 *
349 * <p> This API will return false in scenarios like the profile on the
350 * Bluetooth device is not in connected state etc. When this API returns,
351 * true, it is guaranteed that the connection state change
352 * intent will be broadcasted with the state. Users can get the
353 * disconnection state of the profile from this intent.
354 *
355 * <p> If the disconnection is initiated by a remote device, the state
356 * will transition from {@link #STATE_CONNECTED} to
357 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
358 * host (local) device the state will transition from
359 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
360 * state {@link #STATE_DISCONNECTED}. The transition to
361 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
362 * two scenarios.
363 *
364 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
365 * permission.
366 *
367 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700368 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700369 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100371 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700372 public boolean disconnect(BluetoothDevice device) {
373 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700374 try {
375 mServiceLock.readLock().lock();
Jack He2992cd02017-08-22 21:21:23 -0700376 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700377 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700378 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700379 if (mService == null) Log.w(TAG, "Proxy not attached to service");
380 return false;
381 } catch (RemoteException e) {
382 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
383 return false;
384 } finally {
385 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 }
387 }
388
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700389 /**
390 * {@inheritDoc}
391 */
Jack He2992cd02017-08-22 21:21:23 -0700392 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700393 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700394 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700395 try {
396 mServiceLock.readLock().lock();
397 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700398 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700399 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700400 if (mService == null) Log.w(TAG, "Proxy not attached to service");
401 return new ArrayList<BluetoothDevice>();
402 } catch (RemoteException e) {
403 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
404 return new ArrayList<BluetoothDevice>();
405 } finally {
406 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700407 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700408 }
409
410 /**
411 * {@inheritDoc}
412 */
Jack He2992cd02017-08-22 21:21:23 -0700413 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700414 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700415 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700416 try {
417 mServiceLock.readLock().lock();
418 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700419 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700420 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700421 if (mService == null) Log.w(TAG, "Proxy not attached to service");
422 return new ArrayList<BluetoothDevice>();
423 } catch (RemoteException e) {
424 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
425 return new ArrayList<BluetoothDevice>();
426 } finally {
427 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700428 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700429 }
430
431 /**
432 * {@inheritDoc}
433 */
Jack He2992cd02017-08-22 21:21:23 -0700434 @Override
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700435 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700436 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700437 try {
438 mServiceLock.readLock().lock();
439 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700440 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700441 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700442 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700443 if (mService == null) Log.w(TAG, "Proxy not attached to service");
444 return BluetoothProfile.STATE_DISCONNECTED;
445 } catch (RemoteException e) {
446 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
447 return BluetoothProfile.STATE_DISCONNECTED;
448 } finally {
449 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700450 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700451 }
452
453 /**
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800454 * Select a connected device as active.
455 *
456 * The active device selection is per profile. An active device's
457 * purpose is profile-specific. For example, A2DP audio streaming
458 * is to the active A2DP Sink device. If a remote device is not
459 * connected, it cannot be selected as active.
460 *
461 * <p> This API returns false in scenarios like the profile on the
462 * device is not connected or Bluetooth is not turned on.
463 * When this API returns true, it is guaranteed that the
464 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
465 * with the active device.
466 *
467 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
468 * permission.
469 *
470 * @param device the remote Bluetooth device. Could be null to clear
471 * the active device and stop streaming audio to a Bluetooth device.
472 * @return false on immediate error, true otherwise
473 * @hide
474 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100475 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800476 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
477 if (DBG) log("setActiveDevice(" + device + ")");
478 try {
479 mServiceLock.readLock().lock();
480 if (mService != null && isEnabled()
481 && ((device == null) || isValidDevice(device))) {
482 return mService.setActiveDevice(device);
483 }
484 if (mService == null) Log.w(TAG, "Proxy not attached to service");
485 return false;
486 } catch (RemoteException e) {
487 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
488 return false;
489 } finally {
490 mServiceLock.readLock().unlock();
491 }
492 }
493
494 /**
495 * Get the connected device that is active.
496 *
497 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
498 * permission.
499 *
500 * @return the connected device that is active or null if no device
501 * is active
502 * @hide
503 */
504 @RequiresPermission(Manifest.permission.BLUETOOTH)
505 @Nullable
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100506 @UnsupportedAppUsage
Pavlin Radoslavov64a94352017-12-19 13:20:06 -0800507 public BluetoothDevice getActiveDevice() {
508 if (VDBG) log("getActiveDevice()");
509 try {
510 mServiceLock.readLock().lock();
511 if (mService != null && isEnabled()) {
512 return mService.getActiveDevice();
513 }
514 if (mService == null) Log.w(TAG, "Proxy not attached to service");
515 return null;
516 } catch (RemoteException e) {
517 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
518 return null;
519 } finally {
520 mServiceLock.readLock().unlock();
521 }
522 }
523
524 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700525 * Set priority of the profile
526 *
527 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700528 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700529 * {@link #PRIORITY_OFF},
530 *
531 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
532 * permission.
533 *
534 * @param device Paired bluetooth device
535 * @param priority
536 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700537 * @hide
538 */
539 public boolean setPriority(BluetoothDevice device, int priority) {
540 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700541 try {
542 mServiceLock.readLock().lock();
543 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700544 && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700545 if (priority != BluetoothProfile.PRIORITY_OFF
546 && priority != BluetoothProfile.PRIORITY_ON) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700547 return false;
548 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700549 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700550 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700551 if (mService == null) Log.w(TAG, "Proxy not attached to service");
552 return false;
553 } catch (RemoteException e) {
554 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
555 return false;
556 } finally {
557 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700558 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700559 }
560
561 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700562 * Get the priority of the profile.
563 *
564 * <p> The priority can be any of:
565 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
566 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
567 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700568 * @param device Bluetooth device
569 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700570 * @hide
571 */
Tor Norbye2d497522015-04-23 17:10:21 -0700572 @RequiresPermission(Manifest.permission.BLUETOOTH)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100573 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700574 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700575 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700576 try {
577 mServiceLock.readLock().lock();
578 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700579 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700580 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700581 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700582 if (mService == null) Log.w(TAG, "Proxy not attached to service");
583 return BluetoothProfile.PRIORITY_OFF;
584 } catch (RemoteException e) {
585 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
586 return BluetoothProfile.PRIORITY_OFF;
587 } finally {
588 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700589 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700590 }
591
592 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700593 * Checks if Avrcp device supports the absolute volume feature.
594 *
595 * @return true if device supports absolute volume
596 * @hide
597 */
598 public boolean isAvrcpAbsoluteVolumeSupported() {
599 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700600 try {
601 mServiceLock.readLock().lock();
602 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700603 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700604 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700605 if (mService == null) Log.w(TAG, "Proxy not attached to service");
606 return false;
607 } catch (RemoteException e) {
608 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
609 return false;
610 } finally {
611 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700612 }
John Du5a0cf7a2013-07-19 11:30:34 -0700613 }
614
615 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700616 * Tells remote device to set an absolute volume. Only if absolute volume is supported
617 *
618 * @param volume Absolute volume to be set on AVRCP side
619 * @hide
620 */
621 public void setAvrcpAbsoluteVolume(int volume) {
622 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700623 try {
624 mServiceLock.readLock().lock();
625 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700626 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700627 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700628 if (mService == null) Log.w(TAG, "Proxy not attached to service");
629 } catch (RemoteException e) {
630 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
631 } finally {
632 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700633 }
John Du5a0cf7a2013-07-19 11:30:34 -0700634 }
635
636 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700637 * Check if A2DP profile is streaming music.
638 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800639 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700640 *
641 * @param device BluetoothDevice device
642 */
643 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700644 try {
645 mServiceLock.readLock().lock();
646 if (mService != null && isEnabled()
Jack Hea355e5e2017-08-22 16:06:54 -0700647 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700648 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700649 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700650 if (mService == null) Log.w(TAG, "Proxy not attached to service");
651 return false;
652 } catch (RemoteException e) {
653 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
654 return false;
655 } finally {
656 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700657 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700658 }
659
660 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700661 * This function checks if the remote device is an AVCRP
662 * target and thus whether we should send volume keys
663 * changes or not.
Jack Hea355e5e2017-08-22 16:06:54 -0700664 *
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700665 * @hide
666 */
667 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
668 if (isEnabled() && isValidDevice(device)) {
669 ParcelUuid[] uuids = device.getUuids();
670 if (uuids == null) return false;
671
Jack Hea355e5e2017-08-22 16:06:54 -0700672 for (ParcelUuid uuid : uuids) {
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700673 if (BluetoothUuid.isAvrcpTarget(uuid)) {
674 return true;
675 }
676 }
677 }
678 return false;
679 }
680
Matthew Xiea0c68032011-06-25 21:47:07 -0700681 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800682 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800683 *
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800684 * @param device the remote Bluetooth device. If null, use the current
685 * active A2DP Bluetooth device.
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800686 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800687 * @hide
688 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100689 @UnsupportedAppUsage
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800690 public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
691 if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800692 try {
693 mServiceLock.readLock().lock();
694 if (mService != null && isEnabled()) {
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800695 return mService.getCodecStatus(device);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800696 }
697 if (mService == null) {
698 Log.w(TAG, "Proxy not attached to service");
699 }
700 return null;
701 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800702 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800703 return null;
704 } finally {
705 mServiceLock.readLock().unlock();
706 }
707 }
708
709 /**
710 * Sets the codec configuration preference.
711 *
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800712 * @param device the remote Bluetooth device. If null, use the current
713 * active A2DP Bluetooth device.
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800714 * @param codecConfig the codec configuration preference
715 * @hide
716 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100717 @UnsupportedAppUsage
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800718 public void setCodecConfigPreference(BluetoothDevice device,
719 BluetoothCodecConfig codecConfig) {
720 if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800721 try {
722 mServiceLock.readLock().lock();
723 if (mService != null && isEnabled()) {
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800724 mService.setCodecConfigPreference(device, codecConfig);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800725 }
726 if (mService == null) Log.w(TAG, "Proxy not attached to service");
727 return;
728 } catch (RemoteException e) {
729 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
730 return;
731 } finally {
732 mServiceLock.readLock().unlock();
733 }
734 }
735
736 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800737 * Enables the optional codecs.
738 *
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800739 * @param device the remote Bluetooth device. If null, use the currect
740 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800741 * @hide
742 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100743 @UnsupportedAppUsage
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800744 public void enableOptionalCodecs(BluetoothDevice device) {
745 if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
746 enableDisableOptionalCodecs(device, true);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800747 }
748
749 /**
750 * Disables the optional codecs.
751 *
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800752 * @param device the remote Bluetooth device. If null, use the currect
753 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800754 * @hide
755 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100756 @UnsupportedAppUsage
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800757 public void disableOptionalCodecs(BluetoothDevice device) {
758 if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
759 enableDisableOptionalCodecs(device, false);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800760 }
761
762 /**
763 * Enables or disables the optional codecs.
764 *
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800765 * @param device the remote Bluetooth device. If null, use the currect
766 * active A2DP Bluetooth device.
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800767 * @param enable if true, enable the optional codecs, other disable them
768 */
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800769 private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800770 try {
771 mServiceLock.readLock().lock();
772 if (mService != null && isEnabled()) {
773 if (enable) {
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800774 mService.enableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800775 } else {
Pavlin Radoslavov080a0e72018-01-03 19:38:39 -0800776 mService.disableOptionalCodecs(device);
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800777 }
778 }
779 if (mService == null) Log.w(TAG, "Proxy not attached to service");
780 return;
781 } catch (RemoteException e) {
782 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
783 return;
784 } finally {
785 mServiceLock.readLock().unlock();
786 }
787 }
788
789 /**
Antony Sargentf5772c62017-04-26 16:37:53 -0700790 * Returns whether this device supports optional codecs.
791 *
792 * @param device The device to check
793 * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700794 * OPTIONAL_CODECS_SUPPORTED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700795 * @hide
796 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100797 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700798 public int supportsOptionalCodecs(BluetoothDevice device) {
799 try {
800 mServiceLock.readLock().lock();
801 if (mService != null && isEnabled() && isValidDevice(device)) {
802 return mService.supportsOptionalCodecs(device);
803 }
804 if (mService == null) Log.w(TAG, "Proxy not attached to service");
805 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
806 } catch (RemoteException e) {
807 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
808 return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
809 } finally {
810 mServiceLock.readLock().unlock();
811 }
812 }
813
814 /**
815 * Returns whether this device should have optional codecs enabled.
816 *
817 * @param device The device in question.
818 * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
Jack Hea355e5e2017-08-22 16:06:54 -0700819 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700820 * @hide
821 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100822 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700823 public int getOptionalCodecsEnabled(BluetoothDevice device) {
824 try {
825 mServiceLock.readLock().lock();
826 if (mService != null && isEnabled() && isValidDevice(device)) {
827 return mService.getOptionalCodecsEnabled(device);
828 }
829 if (mService == null) Log.w(TAG, "Proxy not attached to service");
830 return OPTIONAL_CODECS_PREF_UNKNOWN;
831 } catch (RemoteException e) {
832 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
833 return OPTIONAL_CODECS_PREF_UNKNOWN;
834 } finally {
835 mServiceLock.readLock().unlock();
836 }
837 }
838
839 /**
840 * Sets a persistent preference for whether a given device should have optional codecs enabled.
841 *
842 * @param device The device to set this preference for.
843 * @param value Whether the optional codecs should be enabled for this device. This should be
Jack Hea355e5e2017-08-22 16:06:54 -0700844 * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
845 * OPTIONAL_CODECS_PREF_DISABLED.
Antony Sargentf5772c62017-04-26 16:37:53 -0700846 * @hide
847 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100848 @UnsupportedAppUsage
Antony Sargentf5772c62017-04-26 16:37:53 -0700849 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
850 try {
Jack He2992cd02017-08-22 21:21:23 -0700851 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
852 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
853 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
Antony Sargentf5772c62017-04-26 16:37:53 -0700854 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
855 return;
856 }
857 mServiceLock.readLock().lock();
858 if (mService != null && isEnabled()
859 && isValidDevice(device)) {
860 mService.setOptionalCodecsEnabled(device, value);
861 }
862 if (mService == null) Log.w(TAG, "Proxy not attached to service");
863 return;
864 } catch (RemoteException e) {
865 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
866 return;
867 } finally {
868 mServiceLock.readLock().unlock();
869 }
870 }
871
872 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700873 * Helper for converting a state to a string.
874 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 * For debug use only - strings are not internationalized.
Jack Hea355e5e2017-08-22 16:06:54 -0700876 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 * @hide
878 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100879 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 public static String stateToString(int state) {
881 switch (state) {
Jack Hea355e5e2017-08-22 16:06:54 -0700882 case STATE_DISCONNECTED:
883 return "disconnected";
884 case STATE_CONNECTING:
885 return "connecting";
886 case STATE_CONNECTED:
887 return "connected";
888 case STATE_DISCONNECTING:
889 return "disconnecting";
890 case STATE_PLAYING:
891 return "playing";
892 case STATE_NOT_PLAYING:
893 return "not playing";
894 default:
895 return "<unknown state " + state + ">";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 }
897 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800898
Matthew Xie9b693992013-10-10 11:21:40 -0700899 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800900 public void onServiceConnected(ComponentName className, IBinder service) {
901 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700902 try {
903 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600904 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700905 } finally {
906 mServiceLock.writeLock().unlock();
907 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800908
909 if (mServiceListener != null) {
910 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
911 }
912 }
Jack Hea355e5e2017-08-22 16:06:54 -0700913
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800914 public void onServiceDisconnected(ComponentName className) {
915 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700916 try {
917 mServiceLock.writeLock().lock();
918 mService = null;
919 } finally {
920 mServiceLock.writeLock().unlock();
921 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800922 if (mServiceListener != null) {
923 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
924 }
925 }
926 };
927
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700928 private boolean isEnabled() {
Jack Hea355e5e2017-08-22 16:06:54 -0700929 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
930 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700931 }
932
933 private boolean isValidDevice(BluetoothDevice device) {
Jack Hea355e5e2017-08-22 16:06:54 -0700934 if (device == null) return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700935
Jack Hea355e5e2017-08-22 16:06:54 -0700936 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
937 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700938 }
939
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800940 private static void log(String msg) {
Jack Hea355e5e2017-08-22 16:06:54 -0700941 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800942 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943}