blob: 3de159a6cff26fbe55f1bcbd609ebb12b4185108 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Tor Norbye2d497522015-04-23 17:10:21 -070019import android.Manifest;
20import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080023import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Matthew Xie3e8c82e2012-02-16 16:57:18 -080025import android.content.Intent;
26import android.content.ServiceConnection;
RoboErik4197cb62015-01-21 15:45:32 -080027import android.media.AudioManager;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060028import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.IBinder;
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -070030import android.os.ParcelUuid;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070031import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.Log;
33
Calvin Ond7d16b92016-06-20 15:59:48 -070034import com.android.internal.annotations.GuardedBy;
35
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070036import java.util.ArrayList;
37import java.util.List;
Calvin Ond7d16b92016-06-20 15:59:48 -070038import java.util.concurrent.locks.ReentrantReadWriteLock;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41/**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070042 * This class provides the public APIs to control the Bluetooth A2DP
43 * profile.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070045 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
46 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
47 * the BluetoothA2dp proxy object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070049 * <p> Android only supports one connected Bluetooth A2dp device at a time.
50 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070052public final class BluetoothA2dp implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 private static final String TAG = "BluetoothA2dp";
Matthew Xie3e8c82e2012-02-16 16:57:18 -080054 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070055 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070057 /**
58 * Intent used to broadcast the change in connection state of the A2DP
59 * profile.
60 *
61 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080062 * <ul>
63 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
64 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
65 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
66 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070067 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080068 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070069 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
70 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
71 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080072 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
73 * receive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 */
75 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070076 public static final String ACTION_CONNECTION_STATE_CHANGED =
77 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070079 /**
80 * Intent used to broadcast the change in the Playing state of the A2DP
81 * profile.
82 *
83 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080084 * <ul>
85 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
86 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080087 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080088 * </ul>
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070089 *
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080090 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070091 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
92 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080093 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
94 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070095 */
96 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
97 public static final String ACTION_PLAYING_STATE_CHANGED =
98 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
Hemant Gupta3b76a4b2014-02-14 19:53:31 +0530100 /** @hide */
101 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
102 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
103 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
104
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700105 /**
106 * A2DP sink device is streaming music. This state can be one of
107 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
108 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
109 */
110 public static final int STATE_PLAYING = 10;
Nick Pellybd022f42009-08-14 18:33:38 -0700111
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700112 /**
113 * A2DP sink device is NOT streaming music. This state can be one of
114 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
115 * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
116 */
117 public static final int STATE_NOT_PLAYING = 11;
118
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800119 private Context mContext;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700120 private ServiceListener mServiceListener;
Calvin Ond7d16b92016-06-20 15:59:48 -0700121 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
122 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700123 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
fredc0f420372012-04-12 00:02:00 -0700125 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
126 new IBluetoothStateChangeCallback.Stub() {
127 public void onBluetoothStateChange(boolean up) {
128 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
129 if (!up) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700130 if (VDBG) Log.d(TAG, "Unbinding service...");
131 try {
132 mServiceLock.writeLock().lock();
133 mService = null;
134 mContext.unbindService(mConnection);
135 } catch (Exception re) {
136 Log.e(TAG, "", re);
137 } finally {
138 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700139 }
140 } else {
Calvin Ond7d16b92016-06-20 15:59:48 -0700141 try {
142 mServiceLock.readLock().lock();
143 if (mService == null) {
144 if (VDBG) Log.d(TAG,"Binding service...");
145 doBind();
fredc0f420372012-04-12 00:02:00 -0700146 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700147 } catch (Exception re) {
148 Log.e(TAG,"",re);
149 } finally {
150 mServiceLock.readLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700151 }
152 }
153 }
154 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 /**
156 * Create a BluetoothA2dp proxy object for interacting with the local
157 * Bluetooth A2DP service.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700158 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 */
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800160 /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
161 mContext = context;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700162 mServiceListener = l;
163 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700164 IBluetoothManager mgr = mAdapter.getBluetoothManager();
165 if (mgr != null) {
166 try {
167 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
168 } catch (RemoteException e) {
169 Log.e(TAG,"",e);
170 }
171 }
172
Dianne Hackborn221ea892013-08-04 16:50:16 -0700173 doBind();
174 }
175
176 boolean doBind() {
177 Intent intent = new Intent(IBluetoothA2dp.class.getName());
178 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
179 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700180 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
181 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700182 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
183 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700185 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 }
187
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800188 /*package*/ void close() {
189 mServiceListener = null;
fredc0f420372012-04-12 00:02:00 -0700190 IBluetoothManager mgr = mAdapter.getBluetoothManager();
191 if (mgr != null) {
192 try {
193 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
194 } catch (Exception e) {
195 Log.e(TAG,"",e);
196 }
197 }
198
Calvin Ond7d16b92016-06-20 15:59:48 -0700199 try {
200 mServiceLock.writeLock().lock();
fredc0f420372012-04-12 00:02:00 -0700201 if (mService != null) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700202 mService = null;
203 mContext.unbindService(mConnection);
fredc0f420372012-04-12 00:02:00 -0700204 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700205 } catch (Exception re) {
206 Log.e(TAG, "", re);
207 } finally {
208 mServiceLock.writeLock().unlock();
fredc0f420372012-04-12 00:02:00 -0700209 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800210 }
211
fredc0f420372012-04-12 00:02:00 -0700212 public void finalize() {
Mathias Jeppsson2d2d8c22013-08-06 11:41:55 +0200213 // The empty finalize needs to be kept or the
214 // cts signature tests would fail.
fredc0f420372012-04-12 00:02:00 -0700215 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700216 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700217 * Initiate connection to a profile of the remote bluetooth device.
218 *
219 * <p> Currently, the system supports only 1 connection to the
220 * A2DP profile. The API will automatically disconnect connected
221 * devices before connecting.
222 *
223 * <p> This API returns false in scenarios like the profile on the
224 * device is already connected or Bluetooth is not turned on.
225 * When this API returns true, it is guaranteed that
226 * connection state intent for the profile will be broadcasted with
227 * the state. Users can get the connection state of the profile
228 * from this intent.
229 *
230 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
231 * permission.
232 *
233 * @param device Remote Bluetooth Device
234 * @return false on immediate error,
235 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700236 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700238 public boolean connect(BluetoothDevice device) {
239 if (DBG) log("connect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700240 try {
241 mServiceLock.readLock().lock();
242 if (mService != null && isEnabled() &&
243 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700244 return mService.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700245 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700246 if (mService == null) Log.w(TAG, "Proxy not attached to service");
247 return false;
248 } catch (RemoteException e) {
249 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
250 return false;
251 } finally {
252 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 }
254 }
255
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700256 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700257 * Initiate disconnection from a profile
258 *
259 * <p> This API will return false in scenarios like the profile on the
260 * Bluetooth device is not in connected state etc. When this API returns,
261 * true, it is guaranteed that the connection state change
262 * intent will be broadcasted with the state. Users can get the
263 * disconnection state of the profile from this intent.
264 *
265 * <p> If the disconnection is initiated by a remote device, the state
266 * will transition from {@link #STATE_CONNECTED} to
267 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
268 * host (local) device the state will transition from
269 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
270 * state {@link #STATE_DISCONNECTED}. The transition to
271 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
272 * two scenarios.
273 *
274 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
275 * permission.
276 *
277 * @param device Remote Bluetooth Device
278 * @return false on immediate error,
279 * true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700280 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700282 public boolean disconnect(BluetoothDevice device) {
283 if (DBG) log("disconnect(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700284 try {
285 mServiceLock.readLock().lock();
286 if (mService != null && isEnabled() &&
287 isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700288 return mService.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700289 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700290 if (mService == null) Log.w(TAG, "Proxy not attached to service");
291 return false;
292 } catch (RemoteException e) {
293 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
294 return false;
295 } finally {
296 mServiceLock.readLock().unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
298 }
299
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700300 /**
301 * {@inheritDoc}
302 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700303 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700304 if (VDBG) log("getConnectedDevices()");
Calvin Ond7d16b92016-06-20 15:59:48 -0700305 try {
306 mServiceLock.readLock().lock();
307 if (mService != null && isEnabled()) {
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700308 return mService.getConnectedDevices();
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 new ArrayList<BluetoothDevice>();
312 } catch (RemoteException e) {
313 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
314 return new ArrayList<BluetoothDevice>();
315 } finally {
316 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700317 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700318 }
319
320 /**
321 * {@inheritDoc}
322 */
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700323 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700324 if (VDBG) log("getDevicesMatchingStates()");
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.getDevicesMatchingConnectionStates(states);
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 */
343 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700344 if (VDBG) log("getState(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700345 try {
346 mServiceLock.readLock().lock();
347 if (mService != null && isEnabled()
348 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700349 return mService.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700350 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700351 if (mService == null) Log.w(TAG, "Proxy not attached to service");
352 return BluetoothProfile.STATE_DISCONNECTED;
353 } catch (RemoteException e) {
354 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
355 return BluetoothProfile.STATE_DISCONNECTED;
356 } finally {
357 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700358 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700359 }
360
361 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700362 * Set priority of the profile
363 *
364 * <p> The device should already be paired.
fredc0f420372012-04-12 00:02:00 -0700365 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700366 * {@link #PRIORITY_OFF},
367 *
368 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
369 * permission.
370 *
371 * @param device Paired bluetooth device
372 * @param priority
373 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700374 * @hide
375 */
376 public boolean setPriority(BluetoothDevice device, int priority) {
377 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700378 try {
379 mServiceLock.readLock().lock();
380 if (mService != null && isEnabled()
381 && isValidDevice(device)) {
382 if (priority != BluetoothProfile.PRIORITY_OFF &&
383 priority != BluetoothProfile.PRIORITY_ON) {
384 return false;
385 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700386 return mService.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700387 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700388 if (mService == null) Log.w(TAG, "Proxy not attached to service");
389 return false;
390 } catch (RemoteException e) {
391 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
392 return false;
393 } finally {
394 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700395 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700396 }
397
398 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700399 * Get the priority of the profile.
400 *
401 * <p> The priority can be any of:
402 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
403 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
404 *
405 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
406 *
407 * @param device Bluetooth device
408 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700409 * @hide
410 */
Tor Norbye2d497522015-04-23 17:10:21 -0700411 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700412 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700413 if (VDBG) log("getPriority(" + device + ")");
Calvin Ond7d16b92016-06-20 15:59:48 -0700414 try {
415 mServiceLock.readLock().lock();
416 if (mService != null && isEnabled()
417 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700418 return mService.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700419 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700420 if (mService == null) Log.w(TAG, "Proxy not attached to service");
421 return BluetoothProfile.PRIORITY_OFF;
422 } catch (RemoteException e) {
423 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
424 return BluetoothProfile.PRIORITY_OFF;
425 } finally {
426 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700427 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700428 }
429
430 /**
John Du5a0cf7a2013-07-19 11:30:34 -0700431 * Checks if Avrcp device supports the absolute volume feature.
432 *
433 * @return true if device supports absolute volume
434 * @hide
435 */
436 public boolean isAvrcpAbsoluteVolumeSupported() {
437 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
Calvin Ond7d16b92016-06-20 15:59:48 -0700438 try {
439 mServiceLock.readLock().lock();
440 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700441 return mService.isAvrcpAbsoluteVolumeSupported();
John Du5a0cf7a2013-07-19 11:30:34 -0700442 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700443 if (mService == null) Log.w(TAG, "Proxy not attached to service");
444 return false;
445 } catch (RemoteException e) {
446 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
447 return false;
448 } finally {
449 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700450 }
John Du5a0cf7a2013-07-19 11:30:34 -0700451 }
452
453 /**
RoboErik4197cb62015-01-21 15:45:32 -0800454 * Tells remote device to adjust volume. Only if absolute volume is
455 * supported. Uses the following values:
456 * <ul>
457 * <li>{@link AudioManager#ADJUST_LOWER}</li>
458 * <li>{@link AudioManager#ADJUST_RAISE}</li>
459 * <li>{@link AudioManager#ADJUST_MUTE}</li>
460 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
461 * </ul>
John Du5a0cf7a2013-07-19 11:30:34 -0700462 *
RoboErik4197cb62015-01-21 15:45:32 -0800463 * @param direction One of the supported adjust values.
John Du5a0cf7a2013-07-19 11:30:34 -0700464 * @hide
465 */
466 public void adjustAvrcpAbsoluteVolume(int direction) {
467 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700468 try {
469 mServiceLock.readLock().lock();
470 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700471 mService.adjustAvrcpAbsoluteVolume(direction);
John Du5a0cf7a2013-07-19 11:30:34 -0700472 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700473 if (mService == null) Log.w(TAG, "Proxy not attached to service");
474 } catch (RemoteException e) {
475 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
476 } finally {
477 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700478 }
John Du5a0cf7a2013-07-19 11:30:34 -0700479 }
480
481 /**
482 * Tells remote device to set an absolute volume. Only if absolute volume is supported
483 *
484 * @param volume Absolute volume to be set on AVRCP side
485 * @hide
486 */
487 public void setAvrcpAbsoluteVolume(int volume) {
488 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
Calvin Ond7d16b92016-06-20 15:59:48 -0700489 try {
490 mServiceLock.readLock().lock();
491 if (mService != null && isEnabled()) {
John Du5a0cf7a2013-07-19 11:30:34 -0700492 mService.setAvrcpAbsoluteVolume(volume);
John Du5a0cf7a2013-07-19 11:30:34 -0700493 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700494 if (mService == null) Log.w(TAG, "Proxy not attached to service");
495 } catch (RemoteException e) {
496 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
497 } finally {
498 mServiceLock.readLock().unlock();
John Du5a0cf7a2013-07-19 11:30:34 -0700499 }
John Du5a0cf7a2013-07-19 11:30:34 -0700500 }
501
502 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700503 * Check if A2DP profile is streaming music.
504 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800505 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700506 *
507 * @param device BluetoothDevice device
508 */
509 public boolean isA2dpPlaying(BluetoothDevice device) {
Calvin Ond7d16b92016-06-20 15:59:48 -0700510 try {
511 mServiceLock.readLock().lock();
512 if (mService != null && isEnabled()
513 && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700514 return mService.isA2dpPlaying(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700515 }
Calvin Ond7d16b92016-06-20 15:59:48 -0700516 if (mService == null) Log.w(TAG, "Proxy not attached to service");
517 return false;
518 } catch (RemoteException e) {
519 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
520 return false;
521 } finally {
522 mServiceLock.readLock().unlock();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700523 }
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700524 }
525
526 /**
Jaikumar Ganesh41d5c802010-10-13 19:11:28 -0700527 * This function checks if the remote device is an AVCRP
528 * target and thus whether we should send volume keys
529 * changes or not.
530 * @hide
531 */
532 public boolean shouldSendVolumeKeys(BluetoothDevice device) {
533 if (isEnabled() && isValidDevice(device)) {
534 ParcelUuid[] uuids = device.getUuids();
535 if (uuids == null) return false;
536
537 for (ParcelUuid uuid: uuids) {
538 if (BluetoothUuid.isAvrcpTarget(uuid)) {
539 return true;
540 }
541 }
542 }
543 return false;
544 }
545
Matthew Xiea0c68032011-06-25 21:47:07 -0700546 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700547 * Helper for converting a state to a string.
548 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 * For debug use only - strings are not internationalized.
550 * @hide
551 */
552 public static String stateToString(int state) {
553 switch (state) {
554 case STATE_DISCONNECTED:
555 return "disconnected";
556 case STATE_CONNECTING:
557 return "connecting";
558 case STATE_CONNECTED:
559 return "connected";
560 case STATE_DISCONNECTING:
561 return "disconnecting";
562 case STATE_PLAYING:
563 return "playing";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700564 case STATE_NOT_PLAYING:
565 return "not playing";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 default:
567 return "<unknown state " + state + ">";
568 }
569 }
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800570
Matthew Xie9b693992013-10-10 11:21:40 -0700571 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800572 public void onServiceConnected(ComponentName className, IBinder service) {
573 if (DBG) Log.d(TAG, "Proxy object connected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700574 try {
575 mServiceLock.writeLock().lock();
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600576 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
Calvin Ond7d16b92016-06-20 15:59:48 -0700577 } finally {
578 mServiceLock.writeLock().unlock();
579 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800580
581 if (mServiceListener != null) {
582 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
583 }
584 }
585 public void onServiceDisconnected(ComponentName className) {
586 if (DBG) Log.d(TAG, "Proxy object disconnected");
Calvin Ond7d16b92016-06-20 15:59:48 -0700587 try {
588 mServiceLock.writeLock().lock();
589 mService = null;
590 } finally {
591 mServiceLock.writeLock().unlock();
592 }
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800593 if (mServiceListener != null) {
594 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
595 }
596 }
597 };
598
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700599 private boolean isEnabled() {
600 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
601 return false;
602 }
603
604 private boolean isValidDevice(BluetoothDevice device) {
605 if (device == null) return false;
606
607 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
608 return false;
609 }
610
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800611 private static void log(String msg) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700612 Log.d(TAG, msg);
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800613 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614}