blob: 1165fce3ce0a2e4d886358af1e465bd00314bc1d [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070029import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070030import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.util.Log;
32
Calvin Ond7d16b92016-06-20 15:59:48 -070033import com.android.internal.annotations.GuardedBy;
34
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070035import java.util.ArrayList;
36import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070037import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070041 * This class provides the public APIs to control the Bluetooth A2DP
42 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070044 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
45 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
46 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070048 * <p> Android only supports one connected Bluetooth A2dp device at a time.
49 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070051public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080053 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070054 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070056 /**
57 * Intent used to broadcast the change in connection state of the A2DP
58 * profile.
59 *
60 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080061 * <ul>
62 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
63 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
64 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
65 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070066 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080067 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070068 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
69 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
70 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080071 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
72 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 */
74 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070075 public static final String ACTION_CONNECTION_STATE_CHANGED =
76 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070078 /**
79 * Intent used to broadcast the change in the Playing state of the A2DP
80 * profile.
81 *
82 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080083 * <ul>
84 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
85 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080086 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080087 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070088 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080089 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070090 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
91 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080092 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
93 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070094 */
95 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
96 public static final String ACTION_PLAYING_STATE_CHANGED =
97 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098
Hemant Gupta3b76a4b2014-02-14 19:53:31 +053099 /** @hide */
100 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
101 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
102 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
103
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700104 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800105 * Intent used to broadcast the change in the Audio Codec state of the
106 * A2DP Source profile.
107 *
108 * <p>This intent will have 3 extras:
109 * <ul>
110 * <li> {@link #EXTRA_CODEC_CONFIG} - The current codec configuration. </li>
111 * <li> {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration. </li>
112 * <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 *
425 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
426 *
427 * @param device Bluetooth device
428 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700429 * @hide
430 */
Tor Norbye2d497522015-04-23 17:10:21 -0700431 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700432 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700433 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700434 try {
435 mServiceLock.readLock().lock();
436 if (mService != null && isEnabled()
437 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700438 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700439 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700440 if (mService == null) Log.w(TAG, "Proxy not attached to service");
441 return BluetoothProfile.PRIORITY_OFF;
442 } catch (RemoteException e) {
443 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
444 return BluetoothProfile.PRIORITY_OFF;
445 } finally {
446 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700447 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700448 }
449
450 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700451 * Checks if Avrcp device supports the absolute volume feature.
452 *
453 * @return true if device supports absolute volume
454 * @hide
455 */
456 public boolean isAvrcpAbsoluteVolumeSupported() {
457 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700458 try {
459 mServiceLock.readLock().lock();
460 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700461 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700462 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700463 if (mService == null) Log.w(TAG, "Proxy not attached to service");
464 return false;
465 } catch (RemoteException e) {
466 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
467 return false;
468 } finally {
469 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700470 }
John Du5a0cf7a2013-07-19 11:30:34 -0700471 }
472
473 /**
RoboErik4197cb62015-01-21 15:45:32 -0800474 * Tells remote device to adjust volume. Only if absolute volume is
475 * supported. Uses the following values:
476 * <ul>
477 * <li>{@link AudioManager#ADJUST_LOWER}</li>
478 * <li>{@link AudioManager#ADJUST_RAISE}</li>
479 * <li>{@link AudioManager#ADJUST_MUTE}</li>
480 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
481 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700482 *
RoboErik4197cb62015-01-21 15:45:32 -0800483 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700484 * @hide
485 */
486 public void adjustAvrcpAbsoluteVolume(int direction) {
487 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700488 try {
489 mServiceLock.readLock().lock();
490 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700491 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700492 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700493 if (mService == null) Log.w(TAG, "Proxy not attached to service");
494 } catch (RemoteException e) {
495 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
496 } finally {
497 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700498 }
John Du5a0cf7a2013-07-19 11:30:34 -0700499 }
500
501 /**
502 * Tells remote device to set an absolute volume. Only if absolute volume is supported
503 *
504 * @param volume Absolute volume to be set on AVRCP side
505 * @hide
506 */
507 public void setAvrcpAbsoluteVolume(int volume) {
508 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700509 try {
510 mServiceLock.readLock().lock();
511 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700512 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700513 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700514 if (mService == null) Log.w(TAG, "Proxy not attached to service");
515 } catch (RemoteException e) {
516 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
517 } finally {
518 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700519 }
John Du5a0cf7a2013-07-19 11:30:34 -0700520 }
521
522 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700523 * Check if A2DP profile is streaming music.
524 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800525 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700526 *
527 * @param device BluetoothDevice device
528 */
529 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700530 try {
531 mServiceLock.readLock().lock();
532 if (mService != null && isEnabled()
533 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700534 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700535 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700536 if (mService == null) Log.w(TAG, "Proxy not attached to service");
537 return false;
538 } catch (RemoteException e) {
539 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
540 return false;
541 } finally {
542 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700543 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700544 }
545
546 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700547 * This function checks if the remote device is an AVCRP
548 * target and thus whether we should send volume keys
549 * changes or not.
550 * @hide
551 */
552 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
553 if (isEnabled() && isValidDevice(device)) {
554 ParcelUuid[] uuids = device.getUuids();
555 if (uuids == null) return false;
556
557 for (ParcelUuid uuid: uuids) {
558 if (BluetoothUuid.isAvrcpTarget(uuid)) {
559 return true;
560 }
561 }
562 }
563 return false;
564 }
565
Matthew Xiea0c68032011-06-25 21:47:07 -0700566 /**
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800567 * Gets the current codec configuration.
568 *
569 * @return the current codec configuration
570 * @hide
571 */
572 public BluetoothCodecConfig getCodecConfig() {
573 if (DBG) Log.d(TAG, "getCodecConfig");
574 try {
575 mServiceLock.readLock().lock();
576 if (mService != null && isEnabled()) {
577 return mService.getCodecConfig();
578 }
579 if (mService == null) {
580 Log.w(TAG, "Proxy not attached to service");
581 }
582 return null;
583 } catch (RemoteException e) {
584 Log.e(TAG, "Error talking to BT service in getCodecConfig()", e);
585 return null;
586 } finally {
587 mServiceLock.readLock().unlock();
588 }
589 }
590
591 /**
592 * Sets the codec configuration preference.
593 *
594 * @param codecConfig the codec configuration preference
595 * @hide
596 */
597 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
598 if (DBG) Log.d(TAG, "setCodecConfigPreference");
599 try {
600 mServiceLock.readLock().lock();
601 if (mService != null && isEnabled()) {
602 mService.setCodecConfigPreference(codecConfig);
603 }
604 if (mService == null) Log.w(TAG, "Proxy not attached to service");
605 return;
606 } catch (RemoteException e) {
607 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
608 return;
609 } finally {
610 mServiceLock.readLock().unlock();
611 }
612 }
613
614 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700615 * Helper for converting a state to a string.
616 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 * For debug use only - strings are not internationalized.
618 * @hide
619 */
620 public static String stateToString(int state) {
621 switch (state) {
622 case STATE_DISCONNECTED:
623 return "disconnected";
624 case STATE_CONNECTING:
625 return "connecting";
626 case STATE_CONNECTED:
627 return "connected";
628 case STATE_DISCONNECTING:
629 return "disconnecting";
630 case STATE_PLAYING:
631 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700632 case STATE_NOT_PLAYING:
633 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 default:
635 return "<unknown state " + state + ">";
636 }
637 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800638
Matthew Xie9b693992013-10-10 11:21:40 -0700639 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800640 public void onServiceConnected(ComponentName className, IBinder service) {
641 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700642 try {
643 mServiceLock.writeLock().lock();
644 mService = IBluetoothA2dp.Stub.asInterface(service);
645 } finally {
646 mServiceLock.writeLock().unlock();
647 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800648
649 if (mServiceListener != null) {
650 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
651 }
652 }
653 public void onServiceDisconnected(ComponentName className) {
654 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700655 try {
656 mServiceLock.writeLock().lock();
657 mService = null;
658 } finally {
659 mServiceLock.writeLock().unlock();
660 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800661 if (mServiceListener != null) {
662 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
663 }
664 }
665 };
666
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700667 private boolean isEnabled() {
668 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
669 return false;
670 }
671
672 private boolean isValidDevice(BluetoothDevice device) {
673 if (device == null) return false;
674
675 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
676 return false;
677 }
678
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800679 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700680 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682}