blob: e7c8944788fdad43f5db1bd52aec5dc7b598149e [file] [log] [blame]
Mike Lockwood94b59de2014-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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060023import android.os.Binder;
Mike Lockwood94b59de2014-06-02 16:20:37 -070024import android.os.IBinder;
25import android.os.RemoteException;
26import android.util.Log;
27
28import java.util.ArrayList;
29import java.util.List;
30
31/**
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070032 * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
33 * supports player information, playback support and track metadata.
Mike Lockwood94b59de2014-06-02 16:20:37 -070034 *
Jack Hea355e5e2017-08-22 16:06:54 -070035 * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
Mike Lockwood94b59de2014-06-02 16:20:37 -070036 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
37 * the BluetoothAvrcpController proxy object.
38 *
39 * {@hide}
40 */
41public final class BluetoothAvrcpController implements BluetoothProfile {
42 private static final String TAG = "BluetoothAvrcpController";
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070043 private static final boolean DBG = false;
Mike Lockwood94b59de2014-06-02 16:20:37 -070044 private static final boolean VDBG = false;
45
46 /**
47 * Intent used to broadcast the change in connection state of the AVRCP Controller
48 * profile.
49 *
50 * <p>This intent will have 3 extras:
51 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070052 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
53 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
54 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Mike Lockwood94b59de2014-06-02 16:20:37 -070055 * </ul>
56 *
57 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
58 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
59 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
60 *
61 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
62 * receive.
63 */
64 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070065 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070066
67 /**
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070068 * Intent used to broadcast the change in player application setting state on AVRCP AG.
69 *
70 * <p>This intent will have the following extras:
71 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070072 * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
73 * most recent player setting. </li>
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070074 * </ul>
75 */
76 public static final String ACTION_PLAYER_SETTING =
Jack Hea355e5e2017-08-22 16:06:54 -070077 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070078
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070079 public static final String EXTRA_PLAYER_SETTING =
80 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
81
Mike Lockwood94b59de2014-06-02 16:20:37 -070082 private Context mContext;
83 private ServiceListener mServiceListener;
Jack He16eeac32017-08-17 12:11:18 -070084 private volatile IBluetoothAvrcpController mService;
Mike Lockwood94b59de2014-06-02 16:20:37 -070085 private BluetoothAdapter mAdapter;
86
Jack He2992cd02017-08-22 21:21:23 -070087 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Jack Hea355e5e2017-08-22 16:06:54 -070088 new IBluetoothStateChangeCallback.Stub() {
89 public void onBluetoothStateChange(boolean up) {
90 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
91 if (!up) {
92 if (VDBG) Log.d(TAG, "Unbinding service...");
93 synchronized (mConnection) {
94 try {
95 mService = null;
96 mContext.unbindService(mConnection);
97 } catch (Exception re) {
98 Log.e(TAG, "", re);
Mike Lockwood94b59de2014-06-02 16:20:37 -070099 }
Jack Hea355e5e2017-08-22 16:06:54 -0700100 }
101 } else {
102 synchronized (mConnection) {
103 try {
104 if (mService == null) {
105 if (VDBG) Log.d(TAG, "Binding service...");
106 doBind();
107 }
108 } catch (Exception re) {
109 Log.e(TAG, "", re);
110 }
Mike Lockwood94b59de2014-06-02 16:20:37 -0700111 }
112 }
113 }
Jack Hea355e5e2017-08-22 16:06:54 -0700114 };
Mike Lockwood94b59de2014-06-02 16:20:37 -0700115
116 /**
117 * Create a BluetoothAvrcpController proxy object for interacting with the local
118 * Bluetooth AVRCP service.
Mike Lockwood94b59de2014-06-02 16:20:37 -0700119 */
120 /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
121 mContext = context;
122 mServiceListener = l;
123 mAdapter = BluetoothAdapter.getDefaultAdapter();
124 IBluetoothManager mgr = mAdapter.getBluetoothManager();
125 if (mgr != null) {
126 try {
127 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
128 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700129 Log.e(TAG, "", e);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700130 }
131 }
132
133 doBind();
134 }
135
136 boolean doBind() {
137 Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
138 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
139 intent.setComponent(comp);
140 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700141 mContext.getUser())) {
Mike Lockwood94b59de2014-06-02 16:20:37 -0700142 Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
143 return false;
144 }
145 return true;
146 }
147
148 /*package*/ void close() {
149 mServiceListener = null;
150 IBluetoothManager mgr = mAdapter.getBluetoothManager();
151 if (mgr != null) {
152 try {
153 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
154 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700155 Log.e(TAG, "", e);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700156 }
157 }
158
159 synchronized (mConnection) {
160 if (mService != null) {
161 try {
162 mService = null;
163 mContext.unbindService(mConnection);
164 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700165 Log.e(TAG, "", re);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700166 }
167 }
168 }
169 }
170
Jack He2992cd02017-08-22 21:21:23 -0700171 @Override
Mike Lockwood94b59de2014-06-02 16:20:37 -0700172 public void finalize() {
173 close();
174 }
175
176 /**
177 * {@inheritDoc}
178 */
Jack He2992cd02017-08-22 21:21:23 -0700179 @Override
Mike Lockwood94b59de2014-06-02 16:20:37 -0700180 public List<BluetoothDevice> getConnectedDevices() {
181 if (VDBG) log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700182 final IBluetoothAvrcpController service = mService;
183 if (service != null && isEnabled()) {
Mike Lockwood94b59de2014-06-02 16:20:37 -0700184 try {
Jack He16eeac32017-08-17 12:11:18 -0700185 return service.getConnectedDevices();
Mike Lockwood94b59de2014-06-02 16:20:37 -0700186 } catch (RemoteException e) {
187 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
188 return new ArrayList<BluetoothDevice>();
189 }
190 }
Jack He16eeac32017-08-17 12:11:18 -0700191 if (service == null) Log.w(TAG, "Proxy not attached to service");
Mike Lockwood94b59de2014-06-02 16:20:37 -0700192 return new ArrayList<BluetoothDevice>();
193 }
194
195 /**
196 * {@inheritDoc}
197 */
Jack He2992cd02017-08-22 21:21:23 -0700198 @Override
Mike Lockwood94b59de2014-06-02 16:20:37 -0700199 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
200 if (VDBG) log("getDevicesMatchingStates()");
Jack He16eeac32017-08-17 12:11:18 -0700201 final IBluetoothAvrcpController service = mService;
202 if (service != null && isEnabled()) {
Mike Lockwood94b59de2014-06-02 16:20:37 -0700203 try {
Jack He16eeac32017-08-17 12:11:18 -0700204 return service.getDevicesMatchingConnectionStates(states);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700205 } catch (RemoteException e) {
206 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
207 return new ArrayList<BluetoothDevice>();
208 }
209 }
Jack He16eeac32017-08-17 12:11:18 -0700210 if (service == null) Log.w(TAG, "Proxy not attached to service");
Mike Lockwood94b59de2014-06-02 16:20:37 -0700211 return new ArrayList<BluetoothDevice>();
212 }
213
214 /**
215 * {@inheritDoc}
216 */
Jack He2992cd02017-08-22 21:21:23 -0700217 @Override
Mike Lockwood94b59de2014-06-02 16:20:37 -0700218 public int getConnectionState(BluetoothDevice device) {
219 if (VDBG) log("getState(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700220 final IBluetoothAvrcpController service = mService;
221 if (service != null && isEnabled() && isValidDevice(device)) {
Mike Lockwood94b59de2014-06-02 16:20:37 -0700222 try {
Jack He16eeac32017-08-17 12:11:18 -0700223 return service.getConnectionState(device);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700224 } catch (RemoteException e) {
225 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
226 return BluetoothProfile.STATE_DISCONNECTED;
227 }
228 }
Jack He16eeac32017-08-17 12:11:18 -0700229 if (service == null) Log.w(TAG, "Proxy not attached to service");
Mike Lockwood94b59de2014-06-02 16:20:37 -0700230 return BluetoothProfile.STATE_DISCONNECTED;
231 }
232
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700233 /**
234 * Gets the player application settings.
235 *
236 * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
237 */
238 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
239 if (DBG) Log.d(TAG, "getPlayerSettings");
240 BluetoothAvrcpPlayerSettings settings = null;
Jack He16eeac32017-08-17 12:11:18 -0700241 final IBluetoothAvrcpController service = mService;
242 if (service != null && isEnabled()) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700243 try {
Jack He16eeac32017-08-17 12:11:18 -0700244 settings = service.getPlayerSettings(device);
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700245 } catch (RemoteException e) {
246 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
247 return null;
248 }
249 }
250 return settings;
251 }
252
253 /**
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700254 * Sets the player app setting for current player.
255 * returns true in case setting is supported by remote, false otherwise
256 */
257 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
258 if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
Jack He16eeac32017-08-17 12:11:18 -0700259 final IBluetoothAvrcpController service = mService;
260 if (service != null && isEnabled()) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700261 try {
Jack He16eeac32017-08-17 12:11:18 -0700262 return service.setPlayerApplicationSetting(plAppSetting);
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700263 } catch (RemoteException e) {
264 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
265 return false;
266 }
267 }
Jack He16eeac32017-08-17 12:11:18 -0700268 if (service == null) Log.w(TAG, "Proxy not attached to service");
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700269 return false;
270 }
271
Jack He2992cd02017-08-22 21:21:23 -0700272 /**
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700273 * Send Group Navigation Command to Remote.
274 * possible keycode values: next_grp, previous_grp defined above
275 */
276 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Jack Hea355e5e2017-08-22 16:06:54 -0700277 Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
278 + keyState);
Jack He16eeac32017-08-17 12:11:18 -0700279 final IBluetoothAvrcpController service = mService;
280 if (service != null && isEnabled()) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700281 try {
Jack He16eeac32017-08-17 12:11:18 -0700282 service.sendGroupNavigationCmd(device, keyCode, keyState);
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700283 return;
284 } catch (RemoteException e) {
285 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
286 return;
287 }
288 }
Jack He16eeac32017-08-17 12:11:18 -0700289 if (service == null) Log.w(TAG, "Proxy not attached to service");
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700290 }
291
Mike Lockwood94b59de2014-06-02 16:20:37 -0700292 private final ServiceConnection mConnection = new ServiceConnection() {
293 public void onServiceConnected(ComponentName className, IBinder service) {
294 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600295 mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
Mike Lockwood94b59de2014-06-02 16:20:37 -0700296 if (mServiceListener != null) {
297 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
298 BluetoothAvrcpController.this);
299 }
300 }
Jack Hea355e5e2017-08-22 16:06:54 -0700301
Mike Lockwood94b59de2014-06-02 16:20:37 -0700302 public void onServiceDisconnected(ComponentName className) {
303 if (DBG) Log.d(TAG, "Proxy object disconnected");
304 mService = null;
305 if (mServiceListener != null) {
306 mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
307 }
308 }
309 };
310
311 private boolean isEnabled() {
Jack He16eeac32017-08-17 12:11:18 -0700312 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Mike Lockwood94b59de2014-06-02 16:20:37 -0700313 }
314
Jack He16eeac32017-08-17 12:11:18 -0700315 private static boolean isValidDevice(BluetoothDevice device) {
316 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Mike Lockwood94b59de2014-06-02 16:20:37 -0700317 }
318
319 private static void log(String msg) {
Jack Hea355e5e2017-08-22 16:06:54 -0700320 Log.d(TAG, msg);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700321 }
322}