blob: a395aa470f5bc4be9fe82dc405f038deb3b6090c [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;
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070023import android.media.MediaMetadata;
24import android.media.session.PlaybackState;
Mike Lockwood94b59de2014-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 Agarwal1bec6a52015-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 Lockwood94b59de2014-06-02 16:20:37 -070035 *
36 *<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
37 * 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 Agarwal1bec6a52015-10-21 18:23:27 -070044 private static final boolean DBG = false;
Mike Lockwood94b59de2014-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>
53 * <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>
56 * </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}.
61 *
62 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
63 * receive.
64 */
65 public static final String ACTION_CONNECTION_STATE_CHANGED =
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070066 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
67
68 /**
Sanket Agarwal1bec6a52015-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>
73 * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
74 * most recent player setting. </li>
75 * </ul>
76 */
77 public static final String ACTION_PLAYER_SETTING =
78 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
79
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070080 public static final String EXTRA_PLAYER_SETTING =
81 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
82
Mike Lockwood94b59de2014-06-02 16:20:37 -070083 private Context mContext;
84 private ServiceListener mServiceListener;
85 private IBluetoothAvrcpController mService;
86 private BluetoothAdapter mAdapter;
87
88 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070089 new IBluetoothStateChangeCallback.Stub() {
90 public void onBluetoothStateChange(boolean up) {
91 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
92 if (!up) {
93 if (VDBG) Log.d(TAG,"Unbinding service...");
94 synchronized (mConnection) {
95 try {
96 mService = null;
97 mContext.unbindService(mConnection);
98 } catch (Exception re) {
99 Log.e(TAG,"",re);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700100 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700101 }
102 } else {
103 synchronized (mConnection) {
104 try {
105 if (mService == null) {
106 if (VDBG) Log.d(TAG,"Binding service...");
107 doBind();
Mike Lockwood94b59de2014-06-02 16:20:37 -0700108 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700109 } catch (Exception re) {
110 Log.e(TAG,"",re);
Mike Lockwood94b59de2014-06-02 16:20:37 -0700111 }
112 }
113 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700114 }
115 };
Mike Lockwood94b59de2014-06-02 16:20:37 -0700116
117 /**
118 * Create a BluetoothAvrcpController proxy object for interacting with the local
119 * Bluetooth AVRCP service.
120 *
121 */
122 /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
123 mContext = context;
124 mServiceListener = l;
125 mAdapter = BluetoothAdapter.getDefaultAdapter();
126 IBluetoothManager mgr = mAdapter.getBluetoothManager();
127 if (mgr != null) {
128 try {
129 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
130 } catch (RemoteException e) {
131 Log.e(TAG,"",e);
132 }
133 }
134
135 doBind();
136 }
137
138 boolean doBind() {
139 Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
140 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
141 intent.setComponent(comp);
142 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
143 android.os.Process.myUserHandle())) {
144 Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
145 return false;
146 }
147 return true;
148 }
149
150 /*package*/ void close() {
151 mServiceListener = null;
152 IBluetoothManager mgr = mAdapter.getBluetoothManager();
153 if (mgr != null) {
154 try {
155 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
156 } catch (Exception e) {
157 Log.e(TAG,"",e);
158 }
159 }
160
161 synchronized (mConnection) {
162 if (mService != null) {
163 try {
164 mService = null;
165 mContext.unbindService(mConnection);
166 } catch (Exception re) {
167 Log.e(TAG,"",re);
168 }
169 }
170 }
171 }
172
173 public void finalize() {
174 close();
175 }
176
177 /**
178 * {@inheritDoc}
179 */
180 public List<BluetoothDevice> getConnectedDevices() {
181 if (VDBG) log("getConnectedDevices()");
182 if (mService != null && isEnabled()) {
183 try {
184 return mService.getConnectedDevices();
185 } catch (RemoteException e) {
186 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
187 return new ArrayList<BluetoothDevice>();
188 }
189 }
190 if (mService == null) Log.w(TAG, "Proxy not attached to service");
191 return new ArrayList<BluetoothDevice>();
192 }
193
194 /**
195 * {@inheritDoc}
196 */
197 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
198 if (VDBG) log("getDevicesMatchingStates()");
199 if (mService != null && isEnabled()) {
200 try {
201 return mService.getDevicesMatchingConnectionStates(states);
202 } catch (RemoteException e) {
203 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
204 return new ArrayList<BluetoothDevice>();
205 }
206 }
207 if (mService == null) Log.w(TAG, "Proxy not attached to service");
208 return new ArrayList<BluetoothDevice>();
209 }
210
211 /**
212 * {@inheritDoc}
213 */
214 public int getConnectionState(BluetoothDevice device) {
215 if (VDBG) log("getState(" + device + ")");
216 if (mService != null && isEnabled()
217 && isValidDevice(device)) {
218 try {
219 return mService.getConnectionState(device);
220 } catch (RemoteException e) {
221 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
222 return BluetoothProfile.STATE_DISCONNECTED;
223 }
224 }
225 if (mService == null) Log.w(TAG, "Proxy not attached to service");
226 return BluetoothProfile.STATE_DISCONNECTED;
227 }
228
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700229 /**
230 * Gets the player application settings.
231 *
232 * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
233 */
234 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
235 if (DBG) Log.d(TAG, "getPlayerSettings");
236 BluetoothAvrcpPlayerSettings settings = null;
237 if (mService != null && isEnabled()) {
238 try {
239 settings = mService.getPlayerSettings(device);
240 } catch (RemoteException e) {
241 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
242 return null;
243 }
244 }
245 return settings;
246 }
247
248 /**
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700249 * Sets the player app setting for current player.
250 * returns true in case setting is supported by remote, false otherwise
251 */
252 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
253 if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
254 if (mService != null && isEnabled()) {
255 try {
256 return mService.setPlayerApplicationSetting(plAppSetting);
257 } catch (RemoteException e) {
258 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
259 return false;
260 }
261 }
262 if (mService == null) Log.w(TAG, "Proxy not attached to service");
263 return false;
264 }
265
266 /*
267 * Send Group Navigation Command to Remote.
268 * possible keycode values: next_grp, previous_grp defined above
269 */
270 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
271 Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState);
272 if (mService != null && isEnabled()) {
273 try {
274 mService.sendGroupNavigationCmd(device, keyCode, keyState);
275 return;
276 } catch (RemoteException e) {
277 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
278 return;
279 }
280 }
281 if (mService == null) Log.w(TAG, "Proxy not attached to service");
282 }
283
Mike Lockwood94b59de2014-06-02 16:20:37 -0700284 private final ServiceConnection mConnection = new ServiceConnection() {
285 public void onServiceConnected(ComponentName className, IBinder service) {
286 if (DBG) Log.d(TAG, "Proxy object connected");
287 mService = IBluetoothAvrcpController.Stub.asInterface(service);
288
289 if (mServiceListener != null) {
290 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
291 BluetoothAvrcpController.this);
292 }
293 }
294 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() {
304 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
305 return false;
306 }
307
308 private boolean isValidDevice(BluetoothDevice device) {
309 if (device == null) return false;
310
311 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
312 return false;
313 }
314
315 private static void log(String msg) {
316 Log.d(TAG, msg);
317 }
318}