blob: d9fb00bc1c47e73faa1e094f7ff1881976efb580 [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
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060019import android.annotation.RequiresPermission;
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060020import android.annotation.SdkConstant;
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060021import android.annotation.SdkConstant.SdkConstantType;
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060022import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
23import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
Jeff Sharkeyf9e176c2021-04-22 16:01:29 -060024import android.content.AttributionSource;
Mike Lockwood517b04f2014-06-02 16:20:37 -070025import android.content.Context;
David Duartef5b3bc52023-10-17 00:55:06 +000026import android.os.IBinder;
Mike Lockwood517b04f2014-06-02 16:20:37 -070027import android.os.RemoteException;
28import android.util.Log;
29
William Escande5e8e1532024-03-27 18:02:23 +000030import java.util.Collections;
Mike Lockwood517b04f2014-06-02 16:20:37 -070031import java.util.List;
32
33/**
Sanket Agarwal25e84d42015-10-21 18:23:27 -070034 * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
35 * supports player information, playback support and track metadata.
Mike Lockwood517b04f2014-06-02 16:20:37 -070036 *
David Duarteee52b7e2023-12-02 01:32:11 +000037 * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP Service via
38 * IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothAvrcpController proxy
39 * object.
Mike Lockwood517b04f2014-06-02 16:20:37 -070040 *
David Duarte5a02bb42023-12-04 23:07:42 +000041 * @hide
Mike Lockwood517b04f2014-06-02 16:20:37 -070042 */
43public final class BluetoothAvrcpController implements BluetoothProfile {
44 private static final String TAG = "BluetoothAvrcpController";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070045 private static final boolean DBG = false;
Mike Lockwood517b04f2014-06-02 16:20:37 -070046 private static final boolean VDBG = false;
47
48 /**
David Duarteee52b7e2023-12-02 01:32:11 +000049 * Intent used to broadcast the change in connection state of the AVRCP Controller profile.
Mike Lockwood517b04f2014-06-02 16:20:37 -070050 *
51 * <p>This intent will have 3 extras:
David Duarteee52b7e2023-12-02 01:32:11 +000052 *
Mike Lockwood517b04f2014-06-02 16:20:37 -070053 * <ul>
David Duarteee52b7e2023-12-02 01:32:11 +000054 * <li>{@link #EXTRA_STATE} - The current state of the profile.
55 * <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
56 * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
Mike Lockwood517b04f2014-06-02 16:20:37 -070057 * </ul>
58 *
David Duarteee52b7e2023-12-02 01:32:11 +000059 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link
60 * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link
61 * #STATE_DISCONNECTING}.
Mike Lockwood517b04f2014-06-02 16:20:37 -070062 */
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060063 @RequiresLegacyBluetoothPermission
64 @RequiresBluetoothConnectPermission
65 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060066 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mike Lockwood517b04f2014-06-02 16:20:37 -070067 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070068 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070069
70 /**
Sanket Agarwal25e84d42015-10-21 18:23:27 -070071 * Intent used to broadcast the change in player application setting state on AVRCP AG.
72 *
73 * <p>This intent will have the following extras:
David Duarteee52b7e2023-12-02 01:32:11 +000074 *
Sanket Agarwal25e84d42015-10-21 18:23:27 -070075 * <ul>
David Duarteee52b7e2023-12-02 01:32:11 +000076 * <li>{@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
77 * most recent player setting.
Sanket Agarwal25e84d42015-10-21 18:23:27 -070078 * </ul>
79 */
Jeff Sharkeyd7c55662021-04-20 12:30:37 -060080 @RequiresBluetoothConnectPermission
81 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
82 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Sanket Agarwal25e84d42015-10-21 18:23:27 -070083 public static final String ACTION_PLAYER_SETTING =
Jack He910201b2017-08-22 16:06:54 -070084 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070085
Sanket Agarwal25e84d42015-10-21 18:23:27 -070086 public static final String EXTRA_PLAYER_SETTING =
87 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
88
Jeff Sharkeyf9e176c2021-04-22 16:01:29 -060089 private final BluetoothAdapter mAdapter;
90 private final AttributionSource mAttributionSource;
David Duartef5b3bc52023-10-17 00:55:06 +000091
92 private IBluetoothAvrcpController mService;
Mike Lockwood517b04f2014-06-02 16:20:37 -070093
94 /**
David Duarteee52b7e2023-12-02 01:32:11 +000095 * Create a BluetoothAvrcpController proxy object for interacting with the local Bluetooth AVRCP
96 * service.
Mike Lockwood517b04f2014-06-02 16:20:37 -070097 */
David Duartef5b3bc52023-10-17 00:55:06 +000098 /* package */ BluetoothAvrcpController(Context context, BluetoothAdapter adapter) {
Jeff Sharkeyf9e176c2021-04-22 16:01:29 -060099 mAdapter = adapter;
100 mAttributionSource = adapter.getAttributionSource();
David Duartef5b3bc52023-10-17 00:55:06 +0000101 mService = null;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700102 }
103
Santiago Seifertefe40712023-01-06 13:54:17 +0000104 /** @hide */
105 @Override
David Duartef5b3bc52023-10-17 00:55:06 +0000106 public void onServiceConnected(IBinder service) {
107 mService = IBluetoothAvrcpController.Stub.asInterface(service);
108 }
109
110 /** @hide */
111 @Override
112 public void onServiceDisconnected() {
113 mService = null;
Ugo Yu70d76592019-03-26 21:38:08 +0800114 }
Mike Lockwood517b04f2014-06-02 16:20:37 -0700115
Ugo Yu70d76592019-03-26 21:38:08 +0800116 private IBluetoothAvrcpController getService() {
David Duartef5b3bc52023-10-17 00:55:06 +0000117 return mService;
118 }
119
120 /** @hide */
121 @Override
122 public BluetoothAdapter getAdapter() {
123 return mAdapter;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700124 }
125
Jack He9e045d22017-08-22 21:21:23 -0700126 @Override
David Duarte5a02bb42023-12-04 23:07:42 +0000127 @SuppressWarnings("Finalize") // empty finalize for api signature
David Duartef5b3bc52023-10-17 00:55:06 +0000128 public void finalize() {}
Mike Lockwood517b04f2014-06-02 16:20:37 -0700129
David Duarteee52b7e2023-12-02 01:32:11 +0000130 /** {@inheritDoc} */
Jack He9e045d22017-08-22 21:21:23 -0700131 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600132 @RequiresBluetoothConnectPermission
133 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700134 public List<BluetoothDevice> getConnectedDevices() {
135 if (VDBG) log("getConnectedDevices()");
William Escande9a4b7c12021-12-16 16:07:55 +0100136 final IBluetoothAvrcpController service = getService();
William Escande9a4b7c12021-12-16 16:07:55 +0100137 if (service == null) {
138 Log.w(TAG, "Proxy not attached to service");
139 if (DBG) log(Log.getStackTraceString(new Throwable()));
140 } else if (isEnabled()) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700141 try {
Jeff Sharkey98f30442021-06-03 09:26:53 -0600142 return Attributable.setAttributionSource(
William Escande879a70e2024-03-21 21:01:41 -0700143 service.getConnectedDevices(mAttributionSource), mAttributionSource);
144 } catch (RemoteException e) {
William Escande9a4b7c12021-12-16 16:07:55 +0100145 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700146 }
147 }
William Escande5e8e1532024-03-27 18:02:23 +0000148 return Collections.emptyList();
Mike Lockwood517b04f2014-06-02 16:20:37 -0700149 }
150
David Duarteee52b7e2023-12-02 01:32:11 +0000151 /** {@inheritDoc} */
Jack He9e045d22017-08-22 21:21:23 -0700152 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600153 @RequiresBluetoothConnectPermission
154 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700155 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
156 if (VDBG) log("getDevicesMatchingStates()");
William Escande9a4b7c12021-12-16 16:07:55 +0100157 final IBluetoothAvrcpController service = getService();
William Escande9a4b7c12021-12-16 16:07:55 +0100158 if (service == null) {
159 Log.w(TAG, "Proxy not attached to service");
160 if (DBG) log(Log.getStackTraceString(new Throwable()));
161 } else if (isEnabled()) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700162 try {
Jeff Sharkey98f30442021-06-03 09:26:53 -0600163 return Attributable.setAttributionSource(
William Escande879a70e2024-03-21 21:01:41 -0700164 service.getDevicesMatchingConnectionStates(states, mAttributionSource),
Jeff Sharkey43ee69e2021-04-23 14:13:57 -0600165 mAttributionSource);
William Escande879a70e2024-03-21 21:01:41 -0700166 } catch (RemoteException e) {
William Escande9a4b7c12021-12-16 16:07:55 +0100167 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700168 }
169 }
William Escande5e8e1532024-03-27 18:02:23 +0000170 return Collections.emptyList();
Mike Lockwood517b04f2014-06-02 16:20:37 -0700171 }
172
David Duarteee52b7e2023-12-02 01:32:11 +0000173 /** {@inheritDoc} */
Jack He9e045d22017-08-22 21:21:23 -0700174 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600175 @RequiresBluetoothConnectPermission
176 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700177 public int getConnectionState(BluetoothDevice device) {
178 if (VDBG) log("getState(" + device + ")");
William Escande9a4b7c12021-12-16 16:07:55 +0100179 final IBluetoothAvrcpController service = getService();
William Escande9a4b7c12021-12-16 16:07:55 +0100180 if (service == null) {
181 Log.w(TAG, "Proxy not attached to service");
182 if (DBG) log(Log.getStackTraceString(new Throwable()));
183 } else if (isEnabled() && isValidDevice(device)) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700184 try {
William Escande879a70e2024-03-21 21:01:41 -0700185 return service.getConnectionState(device, mAttributionSource);
186 } catch (RemoteException e) {
William Escande9a4b7c12021-12-16 16:07:55 +0100187 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700188 }
189 }
William Escande879a70e2024-03-21 21:01:41 -0700190 return BluetoothProfile.STATE_DISCONNECTED;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700191 }
192
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700193 /**
194 * Gets the player application settings.
195 *
David Duarte5a02bb42023-12-04 23:07:42 +0000196 * @return the {@link BluetoothAvrcpPlayerSettings} or {@code null} if there is an error.
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700197 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600198 @RequiresBluetoothConnectPermission
199 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700200 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
201 if (DBG) Log.d(TAG, "getPlayerSettings");
William Escande9a4b7c12021-12-16 16:07:55 +0100202 final IBluetoothAvrcpController service = getService();
William Escande9a4b7c12021-12-16 16:07:55 +0100203 if (service == null) {
204 Log.w(TAG, "Proxy not attached to service");
205 if (DBG) log(Log.getStackTraceString(new Throwable()));
206 } else if (isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700207 try {
William Escande879a70e2024-03-21 21:01:41 -0700208 return service.getPlayerSettings(device, mAttributionSource);
209 } catch (RemoteException e) {
William Escande9a4b7c12021-12-16 16:07:55 +0100210 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700211 }
212 }
William Escande879a70e2024-03-21 21:01:41 -0700213 return null;
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700214 }
215
216 /**
David Duarteee52b7e2023-12-02 01:32:11 +0000217 * Sets the player app setting for current player. returns true in case setting is supported by
218 * remote, false otherwise
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700219 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600220 @RequiresBluetoothConnectPermission
221 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700222 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
223 if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
William Escande9a4b7c12021-12-16 16:07:55 +0100224 final IBluetoothAvrcpController service = getService();
William Escande9a4b7c12021-12-16 16:07:55 +0100225 if (service == null) {
226 Log.w(TAG, "Proxy not attached to service");
227 if (DBG) log(Log.getStackTraceString(new Throwable()));
228 } else if (isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700229 try {
William Escande879a70e2024-03-21 21:01:41 -0700230 service.setPlayerApplicationSetting(plAppSetting, mAttributionSource);
231 } catch (RemoteException e) {
William Escande9a4b7c12021-12-16 16:07:55 +0100232 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700233 }
234 }
William Escande879a70e2024-03-21 21:01:41 -0700235 return false;
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700236 }
237
Jack He9e045d22017-08-22 21:21:23 -0700238 /**
David Duarteee52b7e2023-12-02 01:32:11 +0000239 * Send Group Navigation Command to Remote. possible keycode values: next_grp, previous_grp
240 * defined above
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700241 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600242 @RequiresBluetoothConnectPermission
243 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700244 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
David Duarteee52b7e2023-12-02 01:32:11 +0000245 Log.d(
246 TAG,
247 "sendGroupNavigationCmd dev = "
248 + device
249 + " key "
250 + keyCode
251 + " State = "
252 + keyState);
William Escande9a4b7c12021-12-16 16:07:55 +0100253 final IBluetoothAvrcpController service = getService();
254 if (service == null) {
255 Log.w(TAG, "Proxy not attached to service");
256 if (DBG) log(Log.getStackTraceString(new Throwable()));
257 } else if (isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700258 try {
William Escande879a70e2024-03-21 21:01:41 -0700259 service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource);
260 } catch (RemoteException e) {
William Escande9a4b7c12021-12-16 16:07:55 +0100261 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700262 }
263 }
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700264 }
265
Mike Lockwood517b04f2014-06-02 16:20:37 -0700266 private boolean isEnabled() {
Jack He1f686f62017-08-17 12:11:18 -0700267 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700268 }
269
Jack He1f686f62017-08-17 12:11:18 -0700270 private static boolean isValidDevice(BluetoothDevice device) {
271 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Mike Lockwood517b04f2014-06-02 16:20:37 -0700272 }
273
274 private static void log(String msg) {
Jack He910201b2017-08-22 16:06:54 -0700275 Log.d(TAG, msg);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700276 }
277}