blob: 5148d5b431d76d2770af9df210c11f2361875ba3 [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;
20import android.annotation.SuppressLint;
21import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
22import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
Mike Lockwood517b04f2014-06-02 16:20:37 -070023import android.content.Context;
Jeff Sharkey73458a82016-11-04 11:23:46 -060024import android.os.Binder;
Mike Lockwood517b04f2014-06-02 16:20:37 -070025import android.os.IBinder;
26import android.os.RemoteException;
27import android.util.Log;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
Sanket Agarwal25e84d42015-10-21 18:23:27 -070033 * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
34 * supports player information, playback support and track metadata.
Mike Lockwood517b04f2014-06-02 16:20:37 -070035 *
Jack He910201b2017-08-22 16:06:54 -070036 * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
Mike Lockwood517b04f2014-06-02 16:20:37 -070037 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
38 * the BluetoothAvrcpController proxy object.
39 *
40 * {@hide}
41 */
42public final class BluetoothAvrcpController implements BluetoothProfile {
43 private static final String TAG = "BluetoothAvrcpController";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070044 private static final boolean DBG = false;
Mike Lockwood517b04f2014-06-02 16:20:37 -070045 private static final boolean VDBG = false;
46
47 /**
48 * Intent used to broadcast the change in connection state of the AVRCP Controller
49 * profile.
50 *
51 * <p>This intent will have 3 extras:
52 * <ul>
Jack He910201b2017-08-22 16:06:54 -070053 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
54 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
55 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Mike Lockwood517b04f2014-06-02 16:20:37 -070056 * </ul>
57 *
58 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
59 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
60 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
Mike Lockwood517b04f2014-06-02 16:20:37 -070061 */
Jeff Sharkey8f80e4a2021-04-02 08:06:09 -060062 @RequiresLegacyBluetoothPermission
63 @RequiresBluetoothConnectPermission
64 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -070065 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070066 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070067
68 /**
Sanket Agarwal25e84d42015-10-21 18:23:27 -070069 * Intent used to broadcast the change in player application setting state on AVRCP AG.
70 *
71 * <p>This intent will have the following extras:
72 * <ul>
Jack He910201b2017-08-22 16:06:54 -070073 * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
74 * most recent player setting. </li>
Sanket Agarwal25e84d42015-10-21 18:23:27 -070075 * </ul>
76 */
77 public static final String ACTION_PLAYER_SETTING =
Jack He910201b2017-08-22 16:06:54 -070078 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070079
Sanket Agarwal25e84d42015-10-21 18:23:27 -070080 public static final String EXTRA_PLAYER_SETTING =
81 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
82
Mike Lockwood517b04f2014-06-02 16:20:37 -070083 private BluetoothAdapter mAdapter;
Ugo Yu70d76592019-03-26 21:38:08 +080084 private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
85 new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
86 "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
87 @Override
88 public IBluetoothAvrcpController getServiceInterface(IBinder service) {
89 return IBluetoothAvrcpController.Stub.asInterface(
90 Binder.allowBlocking(service));
Mike Lockwood517b04f2014-06-02 16:20:37 -070091 }
Ugo Yu70d76592019-03-26 21:38:08 +080092 };
Mike Lockwood517b04f2014-06-02 16:20:37 -070093
94 /**
95 * Create a BluetoothAvrcpController proxy object for interacting with the local
96 * Bluetooth AVRCP service.
Mike Lockwood517b04f2014-06-02 16:20:37 -070097 */
Ugo Yu70d76592019-03-26 21:38:08 +080098 /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) {
Mike Lockwood517b04f2014-06-02 16:20:37 -070099 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yu70d76592019-03-26 21:38:08 +0800100 mProfileConnector.connect(context, listener);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700101 }
102
103 /*package*/ void close() {
Ugo Yu70d76592019-03-26 21:38:08 +0800104 mProfileConnector.disconnect();
105 }
Mike Lockwood517b04f2014-06-02 16:20:37 -0700106
Ugo Yu70d76592019-03-26 21:38:08 +0800107 private IBluetoothAvrcpController getService() {
108 return mProfileConnector.getService();
Mike Lockwood517b04f2014-06-02 16:20:37 -0700109 }
110
Jack He9e045d22017-08-22 21:21:23 -0700111 @Override
Mike Lockwood517b04f2014-06-02 16:20:37 -0700112 public void finalize() {
113 close();
114 }
115
116 /**
117 * {@inheritDoc}
118 */
Jack He9e045d22017-08-22 21:21:23 -0700119 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600120 @RequiresBluetoothConnectPermission
121 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700122 public List<BluetoothDevice> getConnectedDevices() {
123 if (VDBG) log("getConnectedDevices()");
Ugo Yu70d76592019-03-26 21:38:08 +0800124 final IBluetoothAvrcpController service =
125 getService();
Jack He1f686f62017-08-17 12:11:18 -0700126 if (service != null && isEnabled()) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700127 try {
Jack He1f686f62017-08-17 12:11:18 -0700128 return service.getConnectedDevices();
Mike Lockwood517b04f2014-06-02 16:20:37 -0700129 } catch (RemoteException e) {
130 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
131 return new ArrayList<BluetoothDevice>();
132 }
133 }
Jack He1f686f62017-08-17 12:11:18 -0700134 if (service == null) Log.w(TAG, "Proxy not attached to service");
Mike Lockwood517b04f2014-06-02 16:20:37 -0700135 return new ArrayList<BluetoothDevice>();
136 }
137
138 /**
139 * {@inheritDoc}
140 */
Jack He9e045d22017-08-22 21:21:23 -0700141 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600142 @RequiresBluetoothConnectPermission
143 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700144 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
145 if (VDBG) log("getDevicesMatchingStates()");
Ugo Yu70d76592019-03-26 21:38:08 +0800146 final IBluetoothAvrcpController service =
147 getService();
Jack He1f686f62017-08-17 12:11:18 -0700148 if (service != null && isEnabled()) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700149 try {
Jack He1f686f62017-08-17 12:11:18 -0700150 return service.getDevicesMatchingConnectionStates(states);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700151 } catch (RemoteException e) {
152 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
153 return new ArrayList<BluetoothDevice>();
154 }
155 }
Jack He1f686f62017-08-17 12:11:18 -0700156 if (service == null) Log.w(TAG, "Proxy not attached to service");
Mike Lockwood517b04f2014-06-02 16:20:37 -0700157 return new ArrayList<BluetoothDevice>();
158 }
159
160 /**
161 * {@inheritDoc}
162 */
Jack He9e045d22017-08-22 21:21:23 -0700163 @Override
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600164 @RequiresBluetoothConnectPermission
165 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Mike Lockwood517b04f2014-06-02 16:20:37 -0700166 public int getConnectionState(BluetoothDevice device) {
167 if (VDBG) log("getState(" + device + ")");
Ugo Yu70d76592019-03-26 21:38:08 +0800168 final IBluetoothAvrcpController service =
169 getService();
Jack He1f686f62017-08-17 12:11:18 -0700170 if (service != null && isEnabled() && isValidDevice(device)) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700171 try {
Jack He1f686f62017-08-17 12:11:18 -0700172 return service.getConnectionState(device);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700173 } catch (RemoteException e) {
174 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
175 return BluetoothProfile.STATE_DISCONNECTED;
176 }
177 }
Jack He1f686f62017-08-17 12:11:18 -0700178 if (service == null) Log.w(TAG, "Proxy not attached to service");
Mike Lockwood517b04f2014-06-02 16:20:37 -0700179 return BluetoothProfile.STATE_DISCONNECTED;
180 }
181
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700182 /**
183 * Gets the player application settings.
184 *
185 * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
186 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600187 @RequiresBluetoothConnectPermission
188 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700189 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
190 if (DBG) Log.d(TAG, "getPlayerSettings");
191 BluetoothAvrcpPlayerSettings settings = null;
Ugo Yu70d76592019-03-26 21:38:08 +0800192 final IBluetoothAvrcpController service =
193 getService();
Jack He1f686f62017-08-17 12:11:18 -0700194 if (service != null && isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700195 try {
Jack He1f686f62017-08-17 12:11:18 -0700196 settings = service.getPlayerSettings(device);
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700197 } catch (RemoteException e) {
198 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
199 return null;
200 }
201 }
202 return settings;
203 }
204
205 /**
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700206 * Sets the player app setting for current player.
207 * returns true in case setting is supported by remote, false otherwise
208 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600209 @RequiresBluetoothConnectPermission
210 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700211 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
212 if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
Ugo Yu70d76592019-03-26 21:38:08 +0800213 final IBluetoothAvrcpController service =
214 getService();
Jack He1f686f62017-08-17 12:11:18 -0700215 if (service != null && isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700216 try {
Jack He1f686f62017-08-17 12:11:18 -0700217 return service.setPlayerApplicationSetting(plAppSetting);
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700218 } catch (RemoteException e) {
219 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
220 return false;
221 }
222 }
Jack He1f686f62017-08-17 12:11:18 -0700223 if (service == null) Log.w(TAG, "Proxy not attached to service");
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700224 return false;
225 }
226
Jack He9e045d22017-08-22 21:21:23 -0700227 /**
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700228 * Send Group Navigation Command to Remote.
229 * possible keycode values: next_grp, previous_grp defined above
230 */
Jeff Sharkey3614e0a2021-04-16 15:34:54 -0600231 @RequiresBluetoothConnectPermission
232 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700233 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Jack He910201b2017-08-22 16:06:54 -0700234 Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
235 + keyState);
Ugo Yu70d76592019-03-26 21:38:08 +0800236 final IBluetoothAvrcpController service =
237 getService();
Jack He1f686f62017-08-17 12:11:18 -0700238 if (service != null && isEnabled()) {
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700239 try {
Jack He1f686f62017-08-17 12:11:18 -0700240 service.sendGroupNavigationCmd(device, keyCode, keyState);
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700241 return;
242 } catch (RemoteException e) {
243 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
244 return;
245 }
246 }
Jack He1f686f62017-08-17 12:11:18 -0700247 if (service == null) Log.w(TAG, "Proxy not attached to service");
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700248 }
249
Mike Lockwood517b04f2014-06-02 16:20:37 -0700250 private boolean isEnabled() {
Jack He1f686f62017-08-17 12:11:18 -0700251 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700252 }
253
Jack He1f686f62017-08-17 12:11:18 -0700254 private static boolean isValidDevice(BluetoothDevice device) {
255 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Mike Lockwood517b04f2014-06-02 16:20:37 -0700256 }
257
258 private static void log(String msg) {
Jack He910201b2017-08-22 16:06:54 -0700259 Log.d(TAG, msg);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700260 }
261}