blob: 9a3949b10566c731420d047fb496d8aba32d3637 [file] [log] [blame]
Mike Lockwood517b04f2014-06-02 16:20:37 -07001/*
2 * Copyright (C) 2014 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
William Escande9a4b7c12021-12-16 16:07:55 +010019import static android.bluetooth.BluetoothUtils.getSyncTimeout;
20
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060021import android.annotation.RequiresPermission;
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060022import android.annotation.SdkConstant;
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060023import android.annotation.SdkConstant.SdkConstantType;
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060024import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
25import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
Jeff Sharkeyf9e176c2021-04-22 16:01:29 -060026import android.content.AttributionSource;
Mike Lockwood517b04f2014-06-02 16:20:37 -070027import android.content.Context;
David Duartef5b3bc52023-10-17 00:55:06 +000028import android.os.IBinder;
Mike Lockwood517b04f2014-06-02 16:20:37 -070029import android.os.RemoteException;
30import android.util.Log;
31
William Escande9a4b7c12021-12-16 16:07:55 +010032import com.android.modules.utils.SynchronousResultReceiver;
33
Mike Lockwood517b04f2014-06-02 16:20:37 -070034import java.util.ArrayList;
35import java.util.List;
William Escande9a4b7c12021-12-16 16:07:55 +010036import java.util.concurrent.TimeoutException;
Mike Lockwood517b04f2014-06-02 16:20:37 -070037
38/**
Sanket Agarwal25e84d42015-10-21 18:23:27 -070039 * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
40 * supports player information, playback support and track metadata.
Mike Lockwood517b04f2014-06-02 16:20:37 -070041 *
David Duarteee52b7e2023-12-02 01:32:11 +000042 * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP Service via
43 * IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothAvrcpController proxy
44 * object.
Mike Lockwood517b04f2014-06-02 16:20:37 -070045 *
David Duarte5a02bb42023-12-04 23:07:42 +000046 * @hide
Mike Lockwood517b04f2014-06-02 16:20:37 -070047 */
48public final class BluetoothAvrcpController implements BluetoothProfile {
49 private static final String TAG = "BluetoothAvrcpController";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070050 private static final boolean DBG = false;
Mike Lockwood517b04f2014-06-02 16:20:37 -070051 private static final boolean VDBG = false;
52
53 /**
David Duarteee52b7e2023-12-02 01:32:11 +000054 * Intent used to broadcast the change in connection state of the AVRCP Controller profile.
Mike Lockwood517b04f2014-06-02 16:20:37 -070055 *
56 * <p>This intent will have 3 extras:
David Duarteee52b7e2023-12-02 01:32:11 +000057 *
Mike Lockwood517b04f2014-06-02 16:20:37 -070058 * <ul>
David Duarteee52b7e2023-12-02 01:32:11 +000059 * <li>{@link #EXTRA_STATE} - The current state of the profile.
60 * <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
61 * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
Mike Lockwood517b04f2014-06-02 16:20:37 -070062 * </ul>
63 *
David Duarteee52b7e2023-12-02 01:32:11 +000064 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link
65 * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link
66 * #STATE_DISCONNECTING}.
Mike Lockwood517b04f2014-06-02 16:20:37 -070067 */
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060068 @RequiresLegacyBluetoothPermission
69 @RequiresBluetoothConnectPermission
70 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060071 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mike Lockwood517b04f2014-06-02 16:20:37 -070072 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070073 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070074
75 /**
Sanket Agarwal25e84d42015-10-21 18:23:27 -070076 * Intent used to broadcast the change in player application setting state on AVRCP AG.
77 *
78 * <p>This intent will have the following extras:
David Duarteee52b7e2023-12-02 01:32:11 +000079 *
Sanket Agarwal25e84d42015-10-21 18:23:27 -070080 * <ul>
David Duarteee52b7e2023-12-02 01:32:11 +000081 * <li>{@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
82 * most recent player setting.
Sanket Agarwal25e84d42015-10-21 18:23:27 -070083 * </ul>
84 */
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060085 @RequiresBluetoothConnectPermission
86 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
87 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Sanket Agarwal25e84d42015-10-21 18:23:27 -070088 public static final String ACTION_PLAYER_SETTING =
Jack He910201b2017-08-22 16:06:54 -070089 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070090
Sanket Agarwal25e84d42015-10-21 18:23:27 -070091 public static final String EXTRA_PLAYER_SETTING =
92 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
93
Jeff Sharkeyf9e176c2021-04-22 16:01:29 -060094 private final BluetoothAdapter mAdapter;
95 private final AttributionSource mAttributionSource;
David Duartef5b3bc52023-10-17 00:55:06 +000096
97 private IBluetoothAvrcpController mService;
Mike Lockwood517b04f2014-06-02 16:20:37 -070098
99 /**
David Duarteee52b7e2023-12-02 01:32:11 +0000100 * Create a BluetoothAvrcpController proxy object for interacting with the local Bluetooth AVRCP
101 * service.
Mike Lockwood517b04f2014-06-02 16:20:37 -0700102 */
David Duartef5b3bc52023-10-17 00:55:06 +0000103 /* package */ BluetoothAvrcpController(Context context, BluetoothAdapter adapter) {
Jeff Sharkeyf9e176c2021-04-22 16:01:29 -0600104 mAdapter = adapter;
105 mAttributionSource = adapter.getAttributionSource();
David Duartef5b3bc52023-10-17 00:55:06 +0000106 mService = null;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700107 }
108
Santiago Seifertefe40712023-01-06 13:54:17 +0000109 /** @hide */
110 @Override
David Duartef5b3bc52023-10-17 00:55:06 +0000111 public void onServiceConnected(IBinder service) {
112 mService = IBluetoothAvrcpController.Stub.asInterface(service);
113 }
114
115 /** @hide */
116 @Override
117 public void onServiceDisconnected() {
118 mService = null;
Ugo Yu70d76592019-03-26 21:38:08 +0800119 }
Mike Lockwood517b04f2014-06-02 16:20:37 -0700120
Ugo Yu70d76592019-03-26 21:38:08 +0800121 private IBluetoothAvrcpController getService() {
David Duartef5b3bc52023-10-17 00:55:06 +0000122 return mService;
123 }
124
125 /** @hide */
126 @Override
127 public BluetoothAdapter getAdapter() {
128 return mAdapter;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700129 }
130
Jack He9e045d22017-08-22 21:21:23 -0700131 @Override
David Duarte5a02bb42023-12-04 23:07:42 +0000132 @SuppressWarnings("Finalize") // empty finalize for api signature
David Duartef5b3bc52023-10-17 00:55:06 +0000133 public void finalize() {}
Mike Lockwood517b04f2014-06-02 16:20:37 -0700134
David Duarteee52b7e2023-12-02 01:32:11 +0000135 /** {@inheritDoc} */
Jack He9e045d22017-08-22 21:21:23 -0700136 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600137 @RequiresBluetoothConnectPermission
138 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700139 public List<BluetoothDevice> getConnectedDevices() {
140 if (VDBG) log("getConnectedDevices()");
William Escande9a4b7c12021-12-16 16:07:55 +0100141 final IBluetoothAvrcpController service = getService();
142 final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
143 if (service == null) {
144 Log.w(TAG, "Proxy not attached to service");
145 if (DBG) log(Log.getStackTraceString(new Throwable()));
146 } else if (isEnabled()) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700147 try {
William Escande9a4b7c12021-12-16 16:07:55 +0100148 final SynchronousResultReceiver<List<BluetoothDevice>> recv =
Etienne Ruffieux02dcbe72022-06-22 13:54:25 -0700149 SynchronousResultReceiver.get();
William Escande9a4b7c12021-12-16 16:07:55 +0100150 service.getConnectedDevices(mAttributionSource, recv);
Jeff Sharkey98f30442021-06-03 09:26:53 -0600151 return Attributable.setAttributionSource(
William Escande9a4b7c12021-12-16 16:07:55 +0100152 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
153 mAttributionSource);
154 } catch (RemoteException | TimeoutException e) {
155 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700156 }
157 }
William Escande9a4b7c12021-12-16 16:07:55 +0100158 return defaultValue;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700159 }
160
David Duarteee52b7e2023-12-02 01:32:11 +0000161 /** {@inheritDoc} */
Jack He9e045d22017-08-22 21:21:23 -0700162 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600163 @RequiresBluetoothConnectPermission
164 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700165 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
166 if (VDBG) log("getDevicesMatchingStates()");
William Escande9a4b7c12021-12-16 16:07:55 +0100167 final IBluetoothAvrcpController service = getService();
168 final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
169 if (service == null) {
170 Log.w(TAG, "Proxy not attached to service");
171 if (DBG) log(Log.getStackTraceString(new Throwable()));
172 } else if (isEnabled()) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700173 try {
William Escande9a4b7c12021-12-16 16:07:55 +0100174 final SynchronousResultReceiver<List<BluetoothDevice>> recv =
Etienne Ruffieux02dcbe72022-06-22 13:54:25 -0700175 SynchronousResultReceiver.get();
William Escande9a4b7c12021-12-16 16:07:55 +0100176 service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
Jeff Sharkey98f30442021-06-03 09:26:53 -0600177 return Attributable.setAttributionSource(
William Escande9a4b7c12021-12-16 16:07:55 +0100178 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
Jeff Sharkey43ee69e2021-04-23 14:13:57 -0600179 mAttributionSource);
William Escande9a4b7c12021-12-16 16:07:55 +0100180 } catch (RemoteException | TimeoutException e) {
181 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700182 }
183 }
William Escande9a4b7c12021-12-16 16:07:55 +0100184 return defaultValue;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700185 }
186
David Duarteee52b7e2023-12-02 01:32:11 +0000187 /** {@inheritDoc} */
Jack He9e045d22017-08-22 21:21:23 -0700188 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600189 @RequiresBluetoothConnectPermission
190 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700191 public int getConnectionState(BluetoothDevice device) {
192 if (VDBG) log("getState(" + device + ")");
William Escande9a4b7c12021-12-16 16:07:55 +0100193 final IBluetoothAvrcpController service = getService();
194 final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
195 if (service == null) {
196 Log.w(TAG, "Proxy not attached to service");
197 if (DBG) log(Log.getStackTraceString(new Throwable()));
198 } else if (isEnabled() && isValidDevice(device)) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700199 try {
Etienne Ruffieux02dcbe72022-06-22 13:54:25 -0700200 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
William Escande9a4b7c12021-12-16 16:07:55 +0100201 service.getConnectionState(device, mAttributionSource, recv);
202 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
203 } catch (RemoteException | TimeoutException e) {
204 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700205 }
206 }
William Escande9a4b7c12021-12-16 16:07:55 +0100207 return defaultValue;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700208 }
209
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700210 /**
211 * Gets the player application settings.
212 *
David Duarte5a02bb42023-12-04 23:07:42 +0000213 * @return the {@link BluetoothAvrcpPlayerSettings} or {@code null} if there is an error.
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700214 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600215 @RequiresBluetoothConnectPermission
216 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700217 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
218 if (DBG) Log.d(TAG, "getPlayerSettings");
William Escande9a4b7c12021-12-16 16:07:55 +0100219 final IBluetoothAvrcpController service = getService();
220 final BluetoothAvrcpPlayerSettings defaultValue = null;
221 if (service == null) {
222 Log.w(TAG, "Proxy not attached to service");
223 if (DBG) log(Log.getStackTraceString(new Throwable()));
224 } else if (isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700225 try {
William Escande9a4b7c12021-12-16 16:07:55 +0100226 final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv =
Etienne Ruffieux02dcbe72022-06-22 13:54:25 -0700227 SynchronousResultReceiver.get();
William Escande9a4b7c12021-12-16 16:07:55 +0100228 service.getPlayerSettings(device, mAttributionSource, recv);
David Duartede2a05f2023-12-05 22:07:45 +0000229 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
William Escande9a4b7c12021-12-16 16:07:55 +0100230 } catch (RemoteException | TimeoutException e) {
231 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700232 }
233 }
William Escande9a4b7c12021-12-16 16:07:55 +0100234 return defaultValue;
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700235 }
236
237 /**
David Duarteee52b7e2023-12-02 01:32:11 +0000238 * Sets the player app setting for current player. returns true in case setting is supported by
239 * remote, false otherwise
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700240 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600241 @RequiresBluetoothConnectPermission
242 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700243 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
244 if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
William Escande9a4b7c12021-12-16 16:07:55 +0100245 final IBluetoothAvrcpController service = getService();
246 final boolean defaultValue = false;
247 if (service == null) {
248 Log.w(TAG, "Proxy not attached to service");
249 if (DBG) log(Log.getStackTraceString(new Throwable()));
250 } else if (isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700251 try {
Etienne Ruffieux02dcbe72022-06-22 13:54:25 -0700252 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
William Escande9a4b7c12021-12-16 16:07:55 +0100253 service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv);
254 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
255 } catch (RemoteException | TimeoutException e) {
256 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700257 }
258 }
William Escande9a4b7c12021-12-16 16:07:55 +0100259 return defaultValue;
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700260 }
261
Jack He9e045d22017-08-22 21:21:23 -0700262 /**
David Duarteee52b7e2023-12-02 01:32:11 +0000263 * Send Group Navigation Command to Remote. possible keycode values: next_grp, previous_grp
264 * defined above
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700265 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600266 @RequiresBluetoothConnectPermission
267 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700268 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
David Duarteee52b7e2023-12-02 01:32:11 +0000269 Log.d(
270 TAG,
271 "sendGroupNavigationCmd dev = "
272 + device
273 + " key "
274 + keyCode
275 + " State = "
276 + keyState);
William Escande9a4b7c12021-12-16 16:07:55 +0100277 final IBluetoothAvrcpController service = getService();
278 if (service == null) {
279 Log.w(TAG, "Proxy not attached to service");
280 if (DBG) log(Log.getStackTraceString(new Throwable()));
281 } else if (isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700282 try {
Etienne Ruffieux02dcbe72022-06-22 13:54:25 -0700283 final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
William Escande9a4b7c12021-12-16 16:07:55 +0100284 service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv);
285 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700286 return;
William Escande9a4b7c12021-12-16 16:07:55 +0100287 } catch (RemoteException | TimeoutException e) {
288 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700289 }
290 }
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700291 }
292
Mike Lockwood517b04f2014-06-02 16:20:37 -0700293 private boolean isEnabled() {
Jack He1f686f62017-08-17 12:11:18 -0700294 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700295 }
296
Jack He1f686f62017-08-17 12:11:18 -0700297 private static boolean isValidDevice(BluetoothDevice device) {
298 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Mike Lockwood517b04f2014-06-02 16:20:37 -0700299 }
300
301 private static void log(String msg) {
Jack He910201b2017-08-22 16:06:54 -0700302 Log.d(TAG, msg);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700303 }
304}