blob: d1ad8de0b11325e74e5a7414b5d76b731f89d204 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Tor Norbye2d497522015-04-23 17:10:21 -070019import android.Manifest;
20import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080023import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080025import android.content.Intent;
26import android.content.ServiceConnection;
RoboErik4197cb62015-01-21 15:45:32 -080027import android.media.AudioManager;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060028import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070030import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070031import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.Log;
33
Calvin Ond7d16b92016-06-20 15:59:48 -070034import com.android.internal.annotations.GuardedBy;
35
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070036import java.util.ArrayList;
37import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070038import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070042 * This class provides the public APIs to control the Bluetooth A2DP
43 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070045 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
46 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
47 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070049 * <p> Android only supports one connected Bluetooth A2dp device at a time.
50 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070052public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080054 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070055 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070057 /**
58 * Intent used to broadcast the change in connection state of the A2DP
59 * profile.
60 *
61 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080062 * <ul>
63 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
64 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
65 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
66 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070067 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080068 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070069 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
70 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
71 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080072 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
73 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 */
75 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070076 public static final String ACTION_CONNECTION_STATE_CHANGED =
77 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070079 /**
80 * Intent used to broadcast the change in the Playing state of the A2DP
81 * profile.
82 *
83 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080084 * <ul>
85 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
86 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080087 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080088 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070089 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080090 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070091 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
92 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080093 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
94 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070095 */
96 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
97 public static final String ACTION_PLAYING_STATE_CHANGED =
98 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530100 /** @hide */
101 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
102 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
103 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
104
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700105 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800106 * Intent used to broadcast the change in the Audio Codec state of the
107 * A2DP Source profile.
108 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800109 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800110 * <ul>
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800111 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800112 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
113 * connected, otherwise it is not included.</li>
114 * </ul>
115 *
116 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
117 * receive.
118 *
119 * @hide
120 */
121 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
122 public static final String ACTION_CODEC_CONFIG_CHANGED =
123 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
124
125 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700126 * A2DP sink device is streaming music. This state can be one of
127 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
128 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
129 */
130 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700131
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700132 /**
133 * A2DP sink device is NOT streaming music. This state can be one of
134 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
135 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
136 */
137 public static final int STATE_NOT_PLAYING = 11;
138
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800139 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700140 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700141 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
142 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700143 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144
fredc0f420372012-04-12 00:02:00 -0700145 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
146 new IBluetoothStateChangeCallback.Stub() {
147 public void onBluetoothStateChange(boolean up) {
148 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
149 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700150 if (VDBG) Log.d(TAG, "Unbinding service...");
151 try {
152 mServiceLock.writeLock().lock();
153 mService = null;
154 mContext.unbindService(mConnection);
155 } catch (Exception re) {
156 Log.e(TAG, "", re);
157 } finally {
158 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700159 }
160 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700161 try {
162 mServiceLock.readLock().lock();
163 if (mService == null) {
164 if (VDBG) Log.d(TAG,"Binding service...");
165 doBind();
fredc0f420372012-04-12 00:02:00 -0700166 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700167 } catch (Exception re) {
168 Log.e(TAG,"",re);
169 } finally {
170 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700171 }
172 }
173 }
174 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 /**
176 * Create a BluetoothA2dp proxy object for interacting with the local
177 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700178 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800180 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
181 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700182 mServiceListener = l;
183 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700184 IBluetoothManager mgr = mAdapter.getBluetoothManager();
185 if (mgr != null) {
186 try {
187 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
188 } catch (RemoteException e) {
189 Log.e(TAG,"",e);
190 }
191 }
192
Dianne Hackborn221ea892013-08-04 16:50:16 -0700193 doBind();
194 }
195
196 boolean doBind() {
197 Intent intent = new Intent(IBluetoothA2dp.class.getName());
198 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
199 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700200 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
201 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700202 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
203 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700205 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800208 /*package*/ void close() {
209 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700210 IBluetoothManager mgr = mAdapter.getBluetoothManager();
211 if (mgr != null) {
212 try {
213 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
214 } catch (Exception e) {
215 Log.e(TAG,"",e);
216 }
217 }
218
Calvin Ond7d16b92016-06-20 15:59:48 -0700219 try {
220 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700221 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700222 mService = null;
223 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700224 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700225 } catch (Exception re) {
226 Log.e(TAG, "", re);
227 } finally {
228 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700229 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800230 }
231
fredc0f420372012-04-12 00:02:00 -0700232 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200233 // The empty finalize needs to be kept or the
234 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700235 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700236 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700237 * Initiate connection to a profile of the remote bluetooth device.
238 *
239 * <p> Currently, the system supports only 1 connection to the
240 * A2DP profile. The API will automatically disconnect connected
241 * devices before connecting.
242 *
243 * <p> This API returns false in scenarios like the profile on the
244 * device is already connected or Bluetooth is not turned on.
245 * When this API returns true, it is guaranteed that
246 * connection state intent for the profile will be broadcasted with
247 * the state. Users can get the connection state of the profile
248 * from this intent.
249 *
250 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
251 * permission.
252 *
253 * @param device Remote Bluetooth Device
254 * @return false on immediate error,
255 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700256 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700258 public boolean connect(BluetoothDevice device) {
259 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700260 try {
261 mServiceLock.readLock().lock();
262 if (mService != null && isEnabled() &&
263 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700264 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700265 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700266 if (mService == null) Log.w(TAG, "Proxy not attached to service");
267 return false;
268 } catch (RemoteException e) {
269 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
270 return false;
271 } finally {
272 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
274 }
275
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700276 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700277 * Initiate disconnection from a profile
278 *
279 * <p> This API will return false in scenarios like the profile on the
280 * Bluetooth device is not in connected state etc. When this API returns,
281 * true, it is guaranteed that the connection state change
282 * intent will be broadcasted with the state. Users can get the
283 * disconnection state of the profile from this intent.
284 *
285 * <p> If the disconnection is initiated by a remote device, the state
286 * will transition from {@link #STATE_CONNECTED} to
287 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
288 * host (local) device the state will transition from
289 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
290 * state {@link #STATE_DISCONNECTED}. The transition to
291 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
292 * two scenarios.
293 *
294 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
295 * permission.
296 *
297 * @param device Remote Bluetooth Device
298 * @return false on immediate error,
299 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700300 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700302 public boolean disconnect(BluetoothDevice device) {
303 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700304 try {
305 mServiceLock.readLock().lock();
306 if (mService != null && isEnabled() &&
307 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700308 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700309 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700310 if (mService == null) Log.w(TAG, "Proxy not attached to service");
311 return false;
312 } catch (RemoteException e) {
313 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
314 return false;
315 } finally {
316 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 }
318 }
319
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700320 /**
321 * {@inheritDoc}
322 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700323 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700324 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700325 try {
326 mServiceLock.readLock().lock();
327 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700328 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700329 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700330 if (mService == null) Log.w(TAG, "Proxy not attached to service");
331 return new ArrayList<BluetoothDevice>();
332 } catch (RemoteException e) {
333 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
334 return new ArrayList<BluetoothDevice>();
335 } finally {
336 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700337 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700338 }
339
340 /**
341 * {@inheritDoc}
342 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700343 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700344 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700345 try {
346 mServiceLock.readLock().lock();
347 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700348 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700349 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700350 if (mService == null) Log.w(TAG, "Proxy not attached to service");
351 return new ArrayList<BluetoothDevice>();
352 } catch (RemoteException e) {
353 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
354 return new ArrayList<BluetoothDevice>();
355 } finally {
356 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700357 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700358 }
359
360 /**
361 * {@inheritDoc}
362 */
363 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700364 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700365 try {
366 mServiceLock.readLock().lock();
367 if (mService != null && isEnabled()
368 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700369 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700370 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700371 if (mService == null) Log.w(TAG, "Proxy not attached to service");
372 return BluetoothProfile.STATE_DISCONNECTED;
373 } catch (RemoteException e) {
374 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
375 return BluetoothProfile.STATE_DISCONNECTED;
376 } finally {
377 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700378 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700379 }
380
381 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700382 * Set priority of the profile
383 *
384 * <p> The device should already be paired.
fredc0f420372012-04-12 00:02:00 -0700385 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700386 * {@link #PRIORITY_OFF},
387 *
388 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
389 * permission.
390 *
391 * @param device Paired bluetooth device
392 * @param priority
393 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700394 * @hide
395 */
396 public boolean setPriority(BluetoothDevice device, int priority) {
397 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700398 try {
399 mServiceLock.readLock().lock();
400 if (mService != null && isEnabled()
401 && isValidDevice(device)) {
402 if (priority != BluetoothProfile.PRIORITY_OFF &&
403 priority != BluetoothProfile.PRIORITY_ON) {
404 return false;
405 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700406 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700407 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700408 if (mService == null) Log.w(TAG, "Proxy not attached to service");
409 return false;
410 } catch (RemoteException e) {
411 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
412 return false;
413 } finally {
414 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700415 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700416 }
417
418 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700419 * Get the priority of the profile.
420 *
421 * <p> The priority can be any of:
422 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
423 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
424 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700425 * @param device Bluetooth device
426 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700427 * @hide
428 */
Tor Norbye2d497522015-04-23 17:10:21 -0700429 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700430 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700431 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700432 try {
433 mServiceLock.readLock().lock();
434 if (mService != null && isEnabled()
435 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700436 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700437 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700438 if (mService == null) Log.w(TAG, "Proxy not attached to service");
439 return BluetoothProfile.PRIORITY_OFF;
440 } catch (RemoteException e) {
441 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
442 return BluetoothProfile.PRIORITY_OFF;
443 } finally {
444 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700445 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700446 }
447
448 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700449 * Checks if Avrcp device supports the absolute volume feature.
450 *
451 * @return true if device supports absolute volume
452 * @hide
453 */
454 public boolean isAvrcpAbsoluteVolumeSupported() {
455 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700456 try {
457 mServiceLock.readLock().lock();
458 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700459 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700460 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700461 if (mService == null) Log.w(TAG, "Proxy not attached to service");
462 return false;
463 } catch (RemoteException e) {
464 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
465 return false;
466 } finally {
467 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700468 }
John Du5a0cf7a2013-07-19 11:30:34 -0700469 }
470
471 /**
RoboErik4197cb62015-01-21 15:45:32 -0800472 * Tells remote device to adjust volume. Only if absolute volume is
473 * supported. Uses the following values:
474 * <ul>
475 * <li>{@link AudioManager#ADJUST_LOWER}</li>
476 * <li>{@link AudioManager#ADJUST_RAISE}</li>
477 * <li>{@link AudioManager#ADJUST_MUTE}</li>
478 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
479 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700480 *
RoboErik4197cb62015-01-21 15:45:32 -0800481 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700482 * @hide
483 */
484 public void adjustAvrcpAbsoluteVolume(int direction) {
485 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700486 try {
487 mServiceLock.readLock().lock();
488 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700489 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700490 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700491 if (mService == null) Log.w(TAG, "Proxy not attached to service");
492 } catch (RemoteException e) {
493 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
494 } finally {
495 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700496 }
John Du5a0cf7a2013-07-19 11:30:34 -0700497 }
498
499 /**
500 * Tells remote device to set an absolute volume. Only if absolute volume is supported
501 *
502 * @param volume Absolute volume to be set on AVRCP side
503 * @hide
504 */
505 public void setAvrcpAbsoluteVolume(int volume) {
506 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700507 try {
508 mServiceLock.readLock().lock();
509 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700510 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700511 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700512 if (mService == null) Log.w(TAG, "Proxy not attached to service");
513 } catch (RemoteException e) {
514 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
515 } finally {
516 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700517 }
John Du5a0cf7a2013-07-19 11:30:34 -0700518 }
519
520 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700521 * Check if A2DP profile is streaming music.
522 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800523 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700524 *
525 * @param device BluetoothDevice device
526 */
527 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700528 try {
529 mServiceLock.readLock().lock();
530 if (mService != null && isEnabled()
531 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700532 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700533 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700534 if (mService == null) Log.w(TAG, "Proxy not attached to service");
535 return false;
536 } catch (RemoteException e) {
537 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
538 return false;
539 } finally {
540 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700541 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700542 }
543
544 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700545 * This function checks if the remote device is an AVCRP
546 * target and thus whether we should send volume keys
547 * changes or not.
548 * @hide
549 */
550 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
551 if (isEnabled() && isValidDevice(device)) {
552 ParcelUuid[] uuids = device.getUuids();
553 if (uuids == null) return false;
554
555 for (ParcelUuid uuid: uuids) {
556 if (BluetoothUuid.isAvrcpTarget(uuid)) {
557 return true;
558 }
559 }
560 }
561 return false;
562 }
563
Matthew Xiea0c68032011-06-25 21:47:07 -0700564 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800565 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800566 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800567 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800568 * @hide
569 */
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800570 public BluetoothCodecStatus getCodecStatus() {
571 if (DBG) Log.d(TAG, "getCodecStatus");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800572 try {
573 mServiceLock.readLock().lock();
574 if (mService != null && isEnabled()) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800575 return mService.getCodecStatus();
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800576 }
577 if (mService == null) {
578 Log.w(TAG, "Proxy not attached to service");
579 }
580 return null;
581 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800582 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800583 return null;
584 } finally {
585 mServiceLock.readLock().unlock();
586 }
587 }
588
589 /**
590 * Sets the codec configuration preference.
591 *
592 * @param codecConfig the codec configuration preference
593 * @hide
594 */
595 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
596 if (DBG) Log.d(TAG, "setCodecConfigPreference");
597 try {
598 mServiceLock.readLock().lock();
599 if (mService != null && isEnabled()) {
600 mService.setCodecConfigPreference(codecConfig);
601 }
602 if (mService == null) Log.w(TAG, "Proxy not attached to service");
603 return;
604 } catch (RemoteException e) {
605 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
606 return;
607 } finally {
608 mServiceLock.readLock().unlock();
609 }
610 }
611
612 /**
Pavlin Radoslavov011597b2017-02-24 10:19:14 -0800613 * Enables the optional codecs.
614 *
615 * @hide
616 */
617 public void enableOptionalCodecs() {
618 if (DBG) Log.d(TAG, "enableOptionalCodecs");
619 enableDisableOptionalCodecs(true);
620 }
621
622 /**
623 * Disables the optional codecs.
624 *
625 * @hide
626 */
627 public void disableOptionalCodecs() {
628 if (DBG) Log.d(TAG, "disableOptionalCodecs");
629 enableDisableOptionalCodecs(false);
630 }
631
632 /**
633 * Enables or disables the optional codecs.
634 *
635 * @param enable if true, enable the optional codecs, other disable them
636 */
637 private void enableDisableOptionalCodecs(boolean enable) {
638 try {
639 mServiceLock.readLock().lock();
640 if (mService != null && isEnabled()) {
641 if (enable) {
642 mService.enableOptionalCodecs();
643 } else {
644 mService.disableOptionalCodecs();
645 }
646 }
647 if (mService == null) Log.w(TAG, "Proxy not attached to service");
648 return;
649 } catch (RemoteException e) {
650 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
651 return;
652 } finally {
653 mServiceLock.readLock().unlock();
654 }
655 }
656
657 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700658 * Helper for converting a state to a string.
659 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 * For debug use only - strings are not internationalized.
661 * @hide
662 */
663 public static String stateToString(int state) {
664 switch (state) {
665 case STATE_DISCONNECTED:
666 return "disconnected";
667 case STATE_CONNECTING:
668 return "connecting";
669 case STATE_CONNECTED:
670 return "connected";
671 case STATE_DISCONNECTING:
672 return "disconnecting";
673 case STATE_PLAYING:
674 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700675 case STATE_NOT_PLAYING:
676 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 default:
678 return "<unknown state " + state + ">";
679 }
680 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800681
Matthew Xie9b693992013-10-10 11:21:40 -0700682 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800683 public void onServiceConnected(ComponentName className, IBinder service) {
684 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700685 try {
686 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600687 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700688 } finally {
689 mServiceLock.writeLock().unlock();
690 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800691
692 if (mServiceListener != null) {
693 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
694 }
695 }
696 public void onServiceDisconnected(ComponentName className) {
697 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700698 try {
699 mServiceLock.writeLock().lock();
700 mService = null;
701 } finally {
702 mServiceLock.writeLock().unlock();
703 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800704 if (mServiceListener != null) {
705 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
706 }
707 }
708 };
709
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700710 private boolean isEnabled() {
711 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
712 return false;
713 }
714
715 private boolean isValidDevice(BluetoothDevice device) {
716 if (device == null) return false;
717
718 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
719 return false;
720 }
721
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800722 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700723 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800724 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725}