blob: 4960159db212ca381e92fffbd8fae106bbbd0f4b [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 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800108 * <p>This intent will have 2 extras:
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800109 * <ul>
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800110 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800111 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
112 * connected, otherwise it is not included.</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)
121 public static final String ACTION_CODEC_CONFIG_CHANGED =
122 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
123
124 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700125 * A2DP sink device is streaming music. This state can be one of
126 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
127 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
128 */
129 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700130
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700131 /**
132 * A2DP sink device is NOT streaming music. This state can be one of
133 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
134 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
135 */
136 public static final int STATE_NOT_PLAYING = 11;
137
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800138 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700139 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700140 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
141 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700142 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
fredc0f420372012-04-12 00:02:00 -0700144 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
145 new IBluetoothStateChangeCallback.Stub() {
146 public void onBluetoothStateChange(boolean up) {
147 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
148 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700149 if (VDBG) Log.d(TAG, "Unbinding service...");
150 try {
151 mServiceLock.writeLock().lock();
152 mService = null;
153 mContext.unbindService(mConnection);
154 } catch (Exception re) {
155 Log.e(TAG, "", re);
156 } finally {
157 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700158 }
159 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700160 try {
161 mServiceLock.readLock().lock();
162 if (mService == null) {
163 if (VDBG) Log.d(TAG,"Binding service...");
164 doBind();
fredc0f420372012-04-12 00:02:00 -0700165 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700166 } catch (Exception re) {
167 Log.e(TAG,"",re);
168 } finally {
169 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700170 }
171 }
172 }
173 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 /**
175 * Create a BluetoothA2dp proxy object for interacting with the local
176 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700177 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800179 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
180 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700181 mServiceListener = l;
182 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700183 IBluetoothManager mgr = mAdapter.getBluetoothManager();
184 if (mgr != null) {
185 try {
186 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
187 } catch (RemoteException e) {
188 Log.e(TAG,"",e);
189 }
190 }
191
Dianne Hackborn221ea892013-08-04 16:50:16 -0700192 doBind();
193 }
194
195 boolean doBind() {
196 Intent intent = new Intent(IBluetoothA2dp.class.getName());
197 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
198 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700199 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
200 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700201 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
202 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700204 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
206
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800207 /*package*/ void close() {
208 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700209 IBluetoothManager mgr = mAdapter.getBluetoothManager();
210 if (mgr != null) {
211 try {
212 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
213 } catch (Exception e) {
214 Log.e(TAG,"",e);
215 }
216 }
217
Calvin Ond7d16b92016-06-20 15:59:48 -0700218 try {
219 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700220 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700221 mService = null;
222 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700223 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700224 } catch (Exception re) {
225 Log.e(TAG, "", re);
226 } finally {
227 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700228 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800229 }
230
fredc0f420372012-04-12 00:02:00 -0700231 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200232 // The empty finalize needs to be kept or the
233 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700234 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700235 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700236 * Initiate connection to a profile of the remote bluetooth device.
237 *
238 * <p> Currently, the system supports only 1 connection to the
239 * A2DP profile. The API will automatically disconnect connected
240 * devices before connecting.
241 *
242 * <p> This API returns false in scenarios like the profile on the
243 * device is already connected or Bluetooth is not turned on.
244 * When this API returns true, it is guaranteed that
245 * connection state intent for the profile will be broadcasted with
246 * the state. Users can get the connection state of the profile
247 * from this intent.
248 *
249 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
250 * permission.
251 *
252 * @param device Remote Bluetooth Device
253 * @return false on immediate error,
254 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700255 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700257 public boolean connect(BluetoothDevice device) {
258 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700259 try {
260 mServiceLock.readLock().lock();
261 if (mService != null && isEnabled() &&
262 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700263 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700264 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700265 if (mService == null) Log.w(TAG, "Proxy not attached to service");
266 return false;
267 } catch (RemoteException e) {
268 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
269 return false;
270 } finally {
271 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 }
273 }
274
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700275 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700276 * Initiate disconnection from a profile
277 *
278 * <p> This API will return false in scenarios like the profile on the
279 * Bluetooth device is not in connected state etc. When this API returns,
280 * true, it is guaranteed that the connection state change
281 * intent will be broadcasted with the state. Users can get the
282 * disconnection state of the profile from this intent.
283 *
284 * <p> If the disconnection is initiated by a remote device, the state
285 * will transition from {@link #STATE_CONNECTED} to
286 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
287 * host (local) device the state will transition from
288 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
289 * state {@link #STATE_DISCONNECTED}. The transition to
290 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
291 * two scenarios.
292 *
293 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
294 * permission.
295 *
296 * @param device Remote Bluetooth Device
297 * @return false on immediate error,
298 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700299 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700301 public boolean disconnect(BluetoothDevice device) {
302 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700303 try {
304 mServiceLock.readLock().lock();
305 if (mService != null && isEnabled() &&
306 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700307 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700308 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700309 if (mService == null) Log.w(TAG, "Proxy not attached to service");
310 return false;
311 } catch (RemoteException e) {
312 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
313 return false;
314 } finally {
315 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317 }
318
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700319 /**
320 * {@inheritDoc}
321 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700322 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700323 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700324 try {
325 mServiceLock.readLock().lock();
326 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700327 return mService.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700328 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700329 if (mService == null) Log.w(TAG, "Proxy not attached to service");
330 return new ArrayList<BluetoothDevice>();
331 } catch (RemoteException e) {
332 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
333 return new ArrayList<BluetoothDevice>();
334 } finally {
335 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700336 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700337 }
338
339 /**
340 * {@inheritDoc}
341 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700342 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700343 if (VDBG) log("getDevicesMatchingStates()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700344 try {
345 mServiceLock.readLock().lock();
346 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700347 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700348 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700349 if (mService == null) Log.w(TAG, "Proxy not attached to service");
350 return new ArrayList<BluetoothDevice>();
351 } catch (RemoteException e) {
352 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
353 return new ArrayList<BluetoothDevice>();
354 } finally {
355 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700356 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700357 }
358
359 /**
360 * {@inheritDoc}
361 */
362 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700363 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700364 try {
365 mServiceLock.readLock().lock();
366 if (mService != null && isEnabled()
367 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700368 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700369 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700370 if (mService == null) Log.w(TAG, "Proxy not attached to service");
371 return BluetoothProfile.STATE_DISCONNECTED;
372 } catch (RemoteException e) {
373 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
374 return BluetoothProfile.STATE_DISCONNECTED;
375 } finally {
376 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700377 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700378 }
379
380 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700381 * Set priority of the profile
382 *
383 * <p> The device should already be paired.
fredc0f420372012-04-12 00:02:00 -0700384 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700385 * {@link #PRIORITY_OFF},
386 *
387 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
388 * permission.
389 *
390 * @param device Paired bluetooth device
391 * @param priority
392 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700393 * @hide
394 */
395 public boolean setPriority(BluetoothDevice device, int priority) {
396 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700397 try {
398 mServiceLock.readLock().lock();
399 if (mService != null && isEnabled()
400 && isValidDevice(device)) {
401 if (priority != BluetoothProfile.PRIORITY_OFF &&
402 priority != BluetoothProfile.PRIORITY_ON) {
403 return false;
404 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700405 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700406 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700407 if (mService == null) Log.w(TAG, "Proxy not attached to service");
408 return false;
409 } catch (RemoteException e) {
410 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
411 return false;
412 } finally {
413 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700414 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700415 }
416
417 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700418 * Get the priority of the profile.
419 *
420 * <p> The priority can be any of:
421 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
422 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
423 *
424 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
425 *
426 * @param device Bluetooth device
427 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700428 * @hide
429 */
Tor Norbye2d497522015-04-23 17:10:21 -0700430 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700431 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700432 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700433 try {
434 mServiceLock.readLock().lock();
435 if (mService != null && isEnabled()
436 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700437 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700438 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700439 if (mService == null) Log.w(TAG, "Proxy not attached to service");
440 return BluetoothProfile.PRIORITY_OFF;
441 } catch (RemoteException e) {
442 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
443 return BluetoothProfile.PRIORITY_OFF;
444 } finally {
445 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700446 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700447 }
448
449 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700450 * Checks if Avrcp device supports the absolute volume feature.
451 *
452 * @return true if device supports absolute volume
453 * @hide
454 */
455 public boolean isAvrcpAbsoluteVolumeSupported() {
456 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700457 try {
458 mServiceLock.readLock().lock();
459 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700460 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700461 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700462 if (mService == null) Log.w(TAG, "Proxy not attached to service");
463 return false;
464 } catch (RemoteException e) {
465 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
466 return false;
467 } finally {
468 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700469 }
John Du5a0cf7a2013-07-19 11:30:34 -0700470 }
471
472 /**
RoboErik4197cb62015-01-21 15:45:32 -0800473 * Tells remote device to adjust volume. Only if absolute volume is
474 * supported. Uses the following values:
475 * <ul>
476 * <li>{@link AudioManager#ADJUST_LOWER}</li>
477 * <li>{@link AudioManager#ADJUST_RAISE}</li>
478 * <li>{@link AudioManager#ADJUST_MUTE}</li>
479 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
480 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700481 *
RoboErik4197cb62015-01-21 15:45:32 -0800482 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700483 * @hide
484 */
485 public void adjustAvrcpAbsoluteVolume(int direction) {
486 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700487 try {
488 mServiceLock.readLock().lock();
489 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700490 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700491 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700492 if (mService == null) Log.w(TAG, "Proxy not attached to service");
493 } catch (RemoteException e) {
494 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
495 } finally {
496 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700497 }
John Du5a0cf7a2013-07-19 11:30:34 -0700498 }
499
500 /**
501 * Tells remote device to set an absolute volume. Only if absolute volume is supported
502 *
503 * @param volume Absolute volume to be set on AVRCP side
504 * @hide
505 */
506 public void setAvrcpAbsoluteVolume(int volume) {
507 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700508 try {
509 mServiceLock.readLock().lock();
510 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700511 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700512 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700513 if (mService == null) Log.w(TAG, "Proxy not attached to service");
514 } catch (RemoteException e) {
515 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
516 } finally {
517 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700518 }
John Du5a0cf7a2013-07-19 11:30:34 -0700519 }
520
521 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700522 * Check if A2DP profile is streaming music.
523 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800524 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700525 *
526 * @param device BluetoothDevice device
527 */
528 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700529 try {
530 mServiceLock.readLock().lock();
531 if (mService != null && isEnabled()
532 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700533 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700534 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700535 if (mService == null) Log.w(TAG, "Proxy not attached to service");
536 return false;
537 } catch (RemoteException e) {
538 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
539 return false;
540 } finally {
541 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700542 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700543 }
544
545 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700546 * This function checks if the remote device is an AVCRP
547 * target and thus whether we should send volume keys
548 * changes or not.
549 * @hide
550 */
551 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
552 if (isEnabled() && isValidDevice(device)) {
553 ParcelUuid[] uuids = device.getUuids();
554 if (uuids == null) return false;
555
556 for (ParcelUuid uuid: uuids) {
557 if (BluetoothUuid.isAvrcpTarget(uuid)) {
558 return true;
559 }
560 }
561 }
562 return false;
563 }
564
Matthew Xiea0c68032011-06-25 21:47:07 -0700565 /**
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800566 * Gets the current codec status (configuration and capability).
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800567 *
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800568 * @return the current codec status
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800569 * @hide
570 */
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800571 public BluetoothCodecStatus getCodecStatus() {
572 if (DBG) Log.d(TAG, "getCodecStatus");
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800573 try {
574 mServiceLock.readLock().lock();
575 if (mService != null && isEnabled()) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800576 return mService.getCodecStatus();
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800577 }
578 if (mService == null) {
579 Log.w(TAG, "Proxy not attached to service");
580 }
581 return null;
582 } catch (RemoteException e) {
Pavlin Radoslavovb37f1812017-01-25 16:54:07 -0800583 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -0800584 return null;
585 } finally {
586 mServiceLock.readLock().unlock();
587 }
588 }
589
590 /**
591 * Sets the codec configuration preference.
592 *
593 * @param codecConfig the codec configuration preference
594 * @hide
595 */
596 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
597 if (DBG) Log.d(TAG, "setCodecConfigPreference");
598 try {
599 mServiceLock.readLock().lock();
600 if (mService != null && isEnabled()) {
601 mService.setCodecConfigPreference(codecConfig);
602 }
603 if (mService == null) Log.w(TAG, "Proxy not attached to service");
604 return;
605 } catch (RemoteException e) {
606 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
607 return;
608 } finally {
609 mServiceLock.readLock().unlock();
610 }
611 }
612
613 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700614 * Helper for converting a state to a string.
615 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 * For debug use only - strings are not internationalized.
617 * @hide
618 */
619 public static String stateToString(int state) {
620 switch (state) {
621 case STATE_DISCONNECTED:
622 return "disconnected";
623 case STATE_CONNECTING:
624 return "connecting";
625 case STATE_CONNECTED:
626 return "connected";
627 case STATE_DISCONNECTING:
628 return "disconnecting";
629 case STATE_PLAYING:
630 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700631 case STATE_NOT_PLAYING:
632 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 default:
634 return "<unknown state " + state + ">";
635 }
636 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800637
Matthew Xie9b693992013-10-10 11:21:40 -0700638 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800639 public void onServiceConnected(ComponentName className, IBinder service) {
640 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700641 try {
642 mServiceLock.writeLock().lock();
643 mService = IBluetoothA2dp.Stub.asInterface(service);
644 } finally {
645 mServiceLock.writeLock().unlock();
646 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800647
648 if (mServiceListener != null) {
649 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
650 }
651 }
652 public void onServiceDisconnected(ComponentName className) {
653 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700654 try {
655 mServiceLock.writeLock().lock();
656 mService = null;
657 } finally {
658 mServiceLock.writeLock().unlock();
659 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800660 if (mServiceListener != null) {
661 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
662 }
663 }
664 };
665
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700666 private boolean isEnabled() {
667 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
668 return false;
669 }
670
671 private boolean isValidDevice(BluetoothDevice device) {
672 if (device == null) return false;
673
674 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
675 return false;
676 }
677
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800678 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700679 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800680 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681}