blob: 1a0ae149f1cbd9979add7a2f6d748d9c2338ee8d [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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
Jeff Sharkey73458a82016-11-04 11:23:46 -060023import android.os.Binder;
Mike Lockwood517b04f2014-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 Agarwal25e84d42015-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 Lockwood517b04f2014-06-02 16:20:37 -070034 *
Jack He910201b2017-08-22 16:06:54 -070035 * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
Mike Lockwood517b04f2014-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 Agarwal25e84d42015-10-21 18:23:27 -070043 private static final boolean DBG = false;
Mike Lockwood517b04f2014-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 He910201b2017-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 Lockwood517b04f2014-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 He910201b2017-08-22 16:06:54 -070065 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070066
67 /**
Sanket Agarwal25e84d42015-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 He910201b2017-08-22 16:06:54 -070072 * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
73 * most recent player setting. </li>
Sanket Agarwal25e84d42015-10-21 18:23:27 -070074 * </ul>
75 */
76 public static final String ACTION_PLAYER_SETTING =
Jack He910201b2017-08-22 16:06:54 -070077 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
Sanket Agarwal25e84d42015-10-21 18:23:27 -070078
Sanket Agarwal25e84d42015-10-21 18:23:27 -070079 public static final String EXTRA_PLAYER_SETTING =
80 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
81
Mike Lockwood517b04f2014-06-02 16:20:37 -070082 private Context mContext;
83 private ServiceListener mServiceListener;
84 private IBluetoothAvrcpController mService;
85 private BluetoothAdapter mAdapter;
86
87 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Jack He910201b2017-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 Lockwood517b04f2014-06-02 16:20:37 -070099 }
Jack He910201b2017-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 Lockwood517b04f2014-06-02 16:20:37 -0700111 }
112 }
113 }
Jack He910201b2017-08-22 16:06:54 -0700114 };
Mike Lockwood517b04f2014-06-02 16:20:37 -0700115
116 /**
117 * Create a BluetoothAvrcpController proxy object for interacting with the local
118 * Bluetooth AVRCP service.
Mike Lockwood517b04f2014-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 He910201b2017-08-22 16:06:54 -0700129 Log.e(TAG, "", e);
Mike Lockwood517b04f2014-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,
141 android.os.Process.myUserHandle())) {
142 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 He910201b2017-08-22 16:06:54 -0700155 Log.e(TAG, "", e);
Mike Lockwood517b04f2014-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 He910201b2017-08-22 16:06:54 -0700165 Log.e(TAG, "", re);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700166 }
167 }
168 }
169 }
170
171 public void finalize() {
172 close();
173 }
174
175 /**
176 * {@inheritDoc}
177 */
178 public List<BluetoothDevice> getConnectedDevices() {
179 if (VDBG) log("getConnectedDevices()");
180 if (mService != null && isEnabled()) {
181 try {
182 return mService.getConnectedDevices();
183 } catch (RemoteException e) {
184 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
185 return new ArrayList<BluetoothDevice>();
186 }
187 }
188 if (mService == null) Log.w(TAG, "Proxy not attached to service");
189 return new ArrayList<BluetoothDevice>();
190 }
191
192 /**
193 * {@inheritDoc}
194 */
195 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
196 if (VDBG) log("getDevicesMatchingStates()");
197 if (mService != null && isEnabled()) {
198 try {
199 return mService.getDevicesMatchingConnectionStates(states);
200 } catch (RemoteException e) {
201 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
202 return new ArrayList<BluetoothDevice>();
203 }
204 }
205 if (mService == null) Log.w(TAG, "Proxy not attached to service");
206 return new ArrayList<BluetoothDevice>();
207 }
208
209 /**
210 * {@inheritDoc}
211 */
212 public int getConnectionState(BluetoothDevice device) {
213 if (VDBG) log("getState(" + device + ")");
214 if (mService != null && isEnabled()
Jack He910201b2017-08-22 16:06:54 -0700215 && isValidDevice(device)) {
Mike Lockwood517b04f2014-06-02 16:20:37 -0700216 try {
217 return mService.getConnectionState(device);
218 } catch (RemoteException e) {
219 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
220 return BluetoothProfile.STATE_DISCONNECTED;
221 }
222 }
223 if (mService == null) Log.w(TAG, "Proxy not attached to service");
224 return BluetoothProfile.STATE_DISCONNECTED;
225 }
226
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700227 /**
228 * Gets the player application settings.
229 *
230 * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
231 */
232 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
233 if (DBG) Log.d(TAG, "getPlayerSettings");
234 BluetoothAvrcpPlayerSettings settings = null;
235 if (mService != null && isEnabled()) {
236 try {
237 settings = mService.getPlayerSettings(device);
238 } catch (RemoteException e) {
239 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
240 return null;
241 }
242 }
243 return settings;
244 }
245
246 /**
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700247 * Sets the player app setting for current player.
248 * returns true in case setting is supported by remote, false otherwise
249 */
250 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
251 if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
252 if (mService != null && isEnabled()) {
253 try {
254 return mService.setPlayerApplicationSetting(plAppSetting);
255 } catch (RemoteException e) {
256 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
257 return false;
258 }
259 }
260 if (mService == null) Log.w(TAG, "Proxy not attached to service");
261 return false;
262 }
263
264 /*
265 * Send Group Navigation Command to Remote.
266 * possible keycode values: next_grp, previous_grp defined above
267 */
268 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Jack He910201b2017-08-22 16:06:54 -0700269 Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
270 + keyState);
Sanket Agarwal25e84d42015-10-21 18:23:27 -0700271 if (mService != null && isEnabled()) {
272 try {
273 mService.sendGroupNavigationCmd(device, keyCode, keyState);
274 return;
275 } catch (RemoteException e) {
276 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
277 return;
278 }
279 }
280 if (mService == null) Log.w(TAG, "Proxy not attached to service");
281 }
282
Mike Lockwood517b04f2014-06-02 16:20:37 -0700283 private final ServiceConnection mConnection = new ServiceConnection() {
284 public void onServiceConnected(ComponentName className, IBinder service) {
285 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey73458a82016-11-04 11:23:46 -0600286 mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
Mike Lockwood517b04f2014-06-02 16:20:37 -0700287
288 if (mServiceListener != null) {
289 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
290 BluetoothAvrcpController.this);
291 }
292 }
Jack He910201b2017-08-22 16:06:54 -0700293
Mike Lockwood517b04f2014-06-02 16:20:37 -0700294 public void onServiceDisconnected(ComponentName className) {
295 if (DBG) Log.d(TAG, "Proxy object disconnected");
296 mService = null;
297 if (mServiceListener != null) {
298 mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
299 }
300 }
301 };
302
303 private boolean isEnabled() {
Jack He910201b2017-08-22 16:06:54 -0700304 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
305 return false;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700306 }
307
308 private boolean isValidDevice(BluetoothDevice device) {
Jack He910201b2017-08-22 16:06:54 -0700309 if (device == null) return false;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700310
Jack He910201b2017-08-22 16:06:54 -0700311 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
312 return false;
Mike Lockwood517b04f2014-06-02 16:20:37 -0700313 }
314
315 private static void log(String msg) {
Jack He910201b2017-08-22 16:06:54 -0700316 Log.d(TAG, msg);
Mike Lockwood517b04f2014-06-02 16:20:37 -0700317 }
318}