blob: 353c6400ffd74d8ee1446df58660b7eb7979b290 [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 /**
105 * A2DP sink device is streaming music. This state can be one of
106 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
107 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
108 */
109 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700110
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700111 /**
112 * A2DP sink device is NOT streaming music. This state can be one of
113 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
114 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
115 */
116 public static final int STATE_NOT_PLAYING = 11;
117
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800118 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700119 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700120 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
121 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700122 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
fredc0f420372012-04-12 00:02:00 -0700124 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
125 new IBluetoothStateChangeCallback.Stub() {
126 public void onBluetoothStateChange(boolean up) {
127 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
128 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700129 if (VDBG) Log.d(TAG, "Unbinding service...");
130 try {
131 mServiceLock.writeLock().lock();
132 mService = null;
133 mContext.unbindService(mConnection);
134 } catch (Exception re) {
135 Log.e(TAG, "", re);
136 } finally {
137 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700138 }
139 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700140 try {
141 mServiceLock.readLock().lock();
142 if (mService == null) {
143 if (VDBG) Log.d(TAG,"Binding service...");
144 doBind();
fredc0f420372012-04-12 00:02:00 -0700145 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700146 } catch (Exception re) {
147 Log.e(TAG,"",re);
148 } finally {
149 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700150 }
151 }
152 }
153 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 /**
155 * Create a BluetoothA2dp proxy object for interacting with the local
156 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700157 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800159 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
160 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700161 mServiceListener = l;
162 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700163 IBluetoothManager mgr = mAdapter.getBluetoothManager();
164 if (mgr != null) {
165 try {
166 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
167 } catch (RemoteException e) {
168 Log.e(TAG,"",e);
169 }
170 }
171
Dianne Hackborn221ea892013-08-04 16:50:16 -0700172 doBind();
173 }
174
175 boolean doBind() {
176 Intent intent = new Intent(IBluetoothA2dp.class.getName());
177 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
178 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700179 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
180 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700181 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
182 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700184 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 }
186
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800187 /*package*/ void close() {
188 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700189 IBluetoothManager mgr = mAdapter.getBluetoothManager();
190 if (mgr != null) {
191 try {
192 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
193 } catch (Exception e) {
194 Log.e(TAG,"",e);
195 }
196 }
197
Calvin Ond7d16b92016-06-20 15:59:48 -0700198 try {
199 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700200 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700201 mService = null;
202 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700203 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700204 } catch (Exception re) {
205 Log.e(TAG, "", re);
206 } finally {
207 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700208 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800209 }
210
fredc0f420372012-04-12 00:02:00 -0700211 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200212 // The empty finalize needs to be kept or the
213 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700214 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700215 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700216 * Initiate connection to a profile of the remote bluetooth device.
217 *
218 * <p> Currently, the system supports only 1 connection to the
219 * A2DP profile. The API will automatically disconnect connected
220 * devices before connecting.
221 *
222 * <p> This API returns false in scenarios like the profile on the
223 * device is already connected or Bluetooth is not turned on.
224 * When this API returns true, it is guaranteed that
225 * connection state intent for the profile will be broadcasted with
226 * the state. Users can get the connection state of the profile
227 * from this intent.
228 *
229 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
230 * permission.
231 *
232 * @param device Remote Bluetooth Device
233 * @return false on immediate error,
234 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700235 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700237 public boolean connect(BluetoothDevice device) {
238 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700239 try {
240 mServiceLock.readLock().lock();
241 if (mService != null && isEnabled() &&
242 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700243 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700244 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700245 if (mService == null) Log.w(TAG, "Proxy not attached to service");
246 return false;
247 } catch (RemoteException e) {
248 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
249 return false;
250 } finally {
251 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 }
253 }
254
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700255 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700256 * Initiate disconnection from a profile
257 *
258 * <p> This API will return false in scenarios like the profile on the
259 * Bluetooth device is not in connected state etc. When this API returns,
260 * true, it is guaranteed that the connection state change
261 * intent will be broadcasted with the state. Users can get the
262 * disconnection state of the profile from this intent.
263 *
264 * <p> If the disconnection is initiated by a remote device, the state
265 * will transition from {@link #STATE_CONNECTED} to
266 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
267 * host (local) device the state will transition from
268 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
269 * state {@link #STATE_DISCONNECTED}. The transition to
270 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
271 * two scenarios.
272 *
273 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
274 * permission.
275 *
276 * @param device Remote Bluetooth Device
277 * @return false on immediate error,
278 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700279 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700281 public boolean disconnect(BluetoothDevice device) {
282 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700283 try {
284 mServiceLock.readLock().lock();
285 if (mService != null && isEnabled() &&
286 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700287 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700288 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700289 if (mService == null) Log.w(TAG, "Proxy not attached to service");
290 return false;
291 } catch (RemoteException e) {
292 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
293 return false;
294 } finally {
295 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 }
297 }
298
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700299 /**
300 * {@inheritDoc}
301 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700302 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700303 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700304 try {
305 mServiceLock.readLock().lock();
306 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700307 return mService.getConnectedDevices();
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 new ArrayList<BluetoothDevice>();
311 } catch (RemoteException e) {
312 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
313 return new ArrayList<BluetoothDevice>();
314 } finally {
315 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700316 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700317 }
318
319 /**
320 * {@inheritDoc}
321 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700322 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700323 if (VDBG) log("getDevicesMatchingStates()");
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.getDevicesMatchingConnectionStates(states);
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 */
342 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700343 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700344 try {
345 mServiceLock.readLock().lock();
346 if (mService != null && isEnabled()
347 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700348 return mService.getConnectionState(device);
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 BluetoothProfile.STATE_DISCONNECTED;
352 } catch (RemoteException e) {
353 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
354 return BluetoothProfile.STATE_DISCONNECTED;
355 } finally {
356 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700357 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700358 }
359
360 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700361 * Set priority of the profile
362 *
363 * <p> The device should already be paired.
fredc0f420372012-04-12 00:02:00 -0700364 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700365 * {@link #PRIORITY_OFF},
366 *
367 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
368 * permission.
369 *
370 * @param device Paired bluetooth device
371 * @param priority
372 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700373 * @hide
374 */
375 public boolean setPriority(BluetoothDevice device, int priority) {
376 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700377 try {
378 mServiceLock.readLock().lock();
379 if (mService != null && isEnabled()
380 && isValidDevice(device)) {
381 if (priority != BluetoothProfile.PRIORITY_OFF &&
382 priority != BluetoothProfile.PRIORITY_ON) {
383 return false;
384 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700385 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700386 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700387 if (mService == null) Log.w(TAG, "Proxy not attached to service");
388 return false;
389 } catch (RemoteException e) {
390 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
391 return false;
392 } finally {
393 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700394 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700395 }
396
397 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700398 * Get the priority of the profile.
399 *
400 * <p> The priority can be any of:
401 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
402 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
403 *
404 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
405 *
406 * @param device Bluetooth device
407 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700408 * @hide
409 */
Tor Norbye2d497522015-04-23 17:10:21 -0700410 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700411 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700412 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700413 try {
414 mServiceLock.readLock().lock();
415 if (mService != null && isEnabled()
416 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700417 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700418 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700419 if (mService == null) Log.w(TAG, "Proxy not attached to service");
420 return BluetoothProfile.PRIORITY_OFF;
421 } catch (RemoteException e) {
422 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
423 return BluetoothProfile.PRIORITY_OFF;
424 } finally {
425 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700426 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700427 }
428
429 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700430 * Checks if Avrcp device supports the absolute volume feature.
431 *
432 * @return true if device supports absolute volume
433 * @hide
434 */
435 public boolean isAvrcpAbsoluteVolumeSupported() {
436 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700437 try {
438 mServiceLock.readLock().lock();
439 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700440 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700441 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700442 if (mService == null) Log.w(TAG, "Proxy not attached to service");
443 return false;
444 } catch (RemoteException e) {
445 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
446 return false;
447 } finally {
448 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700449 }
John Du5a0cf7a2013-07-19 11:30:34 -0700450 }
451
452 /**
RoboErik4197cb62015-01-21 15:45:32 -0800453 * Tells remote device to adjust volume. Only if absolute volume is
454 * supported. Uses the following values:
455 * <ul>
456 * <li>{@link AudioManager#ADJUST_LOWER}</li>
457 * <li>{@link AudioManager#ADJUST_RAISE}</li>
458 * <li>{@link AudioManager#ADJUST_MUTE}</li>
459 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
460 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700461 *
RoboErik4197cb62015-01-21 15:45:32 -0800462 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700463 * @hide
464 */
465 public void adjustAvrcpAbsoluteVolume(int direction) {
466 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700467 try {
468 mServiceLock.readLock().lock();
469 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700470 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700471 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700472 if (mService == null) Log.w(TAG, "Proxy not attached to service");
473 } catch (RemoteException e) {
474 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
475 } finally {
476 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700477 }
John Du5a0cf7a2013-07-19 11:30:34 -0700478 }
479
480 /**
481 * Tells remote device to set an absolute volume. Only if absolute volume is supported
482 *
483 * @param volume Absolute volume to be set on AVRCP side
484 * @hide
485 */
486 public void setAvrcpAbsoluteVolume(int volume) {
487 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
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.setAvrcpAbsoluteVolume(volume);
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 setAvrcpAbsoluteVolume()", 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 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700502 * Check if A2DP profile is streaming music.
503 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800504 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700505 *
506 * @param device BluetoothDevice device
507 */
508 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700509 try {
510 mServiceLock.readLock().lock();
511 if (mService != null && isEnabled()
512 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700513 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700514 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700515 if (mService == null) Log.w(TAG, "Proxy not attached to service");
516 return false;
517 } catch (RemoteException e) {
518 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
519 return false;
520 } finally {
521 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700522 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700523 }
524
525 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700526 * This function checks if the remote device is an AVCRP
527 * target and thus whether we should send volume keys
528 * changes or not.
529 * @hide
530 */
531 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
532 if (isEnabled() && isValidDevice(device)) {
533 ParcelUuid[] uuids = device.getUuids();
534 if (uuids == null) return false;
535
536 for (ParcelUuid uuid: uuids) {
537 if (BluetoothUuid.isAvrcpTarget(uuid)) {
538 return true;
539 }
540 }
541 }
542 return false;
543 }
544
Matthew Xiea0c68032011-06-25 21:47:07 -0700545 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700546 * Helper for converting a state to a string.
547 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 * For debug use only - strings are not internationalized.
549 * @hide
550 */
551 public static String stateToString(int state) {
552 switch (state) {
553 case STATE_DISCONNECTED:
554 return "disconnected";
555 case STATE_CONNECTING:
556 return "connecting";
557 case STATE_CONNECTED:
558 return "connected";
559 case STATE_DISCONNECTING:
560 return "disconnecting";
561 case STATE_PLAYING:
562 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700563 case STATE_NOT_PLAYING:
564 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 default:
566 return "<unknown state " + state + ">";
567 }
568 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800569
Matthew Xie9b693992013-10-10 11:21:40 -0700570 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800571 public void onServiceConnected(ComponentName className, IBinder service) {
572 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700573 try {
574 mServiceLock.writeLock().lock();
575 mService = IBluetoothA2dp.Stub.asInterface(service);
576 } finally {
577 mServiceLock.writeLock().unlock();
578 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800579
580 if (mServiceListener != null) {
581 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
582 }
583 }
584 public void onServiceDisconnected(ComponentName className) {
585 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700586 try {
587 mServiceLock.writeLock().lock();
588 mService = null;
589 } finally {
590 mServiceLock.writeLock().unlock();
591 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800592 if (mServiceListener != null) {
593 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
594 }
595 }
596 };
597
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700598 private boolean isEnabled() {
599 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
600 return false;
601 }
602
603 private boolean isValidDevice(BluetoothDevice device) {
604 if (device == null) return false;
605
606 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
607 return false;
608 }
609
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800610 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700611 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800612 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613}