• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.bluetooth;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.os.Binder;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.util.Log;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
33  * supports player information, playback support and track metadata.
34  *
35  * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
36  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
37  * the BluetoothAvrcpController proxy object.
38  *
39  * {@hide}
40  */
41 public final class BluetoothAvrcpController implements BluetoothProfile {
42     private static final String TAG = "BluetoothAvrcpController";
43     private static final boolean DBG = false;
44     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>
52      * <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>
55      * </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 =
65             "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
66 
67     /**
68      * 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>
72      * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
73      * most recent player setting. </li>
74      * </ul>
75      */
76     public static final String ACTION_PLAYER_SETTING =
77             "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
78 
79     public static final String EXTRA_PLAYER_SETTING =
80             "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
81 
82     private Context mContext;
83     private ServiceListener mServiceListener;
84     private volatile IBluetoothAvrcpController mService;
85     private BluetoothAdapter mAdapter;
86 
87     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
88             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);
99                             }
100                         }
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                             }
111                         }
112                     }
113                 }
114             };
115 
116     /**
117      * Create a BluetoothAvrcpController proxy object for interacting with the local
118      * Bluetooth AVRCP service.
119      */
BluetoothAvrcpController(Context context, ServiceListener l)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) {
129                 Log.e(TAG, "", e);
130             }
131         }
132 
133         doBind();
134     }
135 
doBind()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                 mContext.getUser())) {
142             Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
143             return false;
144         }
145         return true;
146     }
147 
close()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) {
155                 Log.e(TAG, "", e);
156             }
157         }
158 
159         synchronized (mConnection) {
160             if (mService != null) {
161                 try {
162                     mService = null;
163                     mContext.unbindService(mConnection);
164                 } catch (Exception re) {
165                     Log.e(TAG, "", re);
166                 }
167             }
168         }
169     }
170 
171     @Override
finalize()172     public void finalize() {
173         close();
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     @Override
getConnectedDevices()180     public List<BluetoothDevice> getConnectedDevices() {
181         if (VDBG) log("getConnectedDevices()");
182         final IBluetoothAvrcpController service = mService;
183         if (service != null && isEnabled()) {
184             try {
185                 return service.getConnectedDevices();
186             } catch (RemoteException e) {
187                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
188                 return new ArrayList<BluetoothDevice>();
189             }
190         }
191         if (service == null) Log.w(TAG, "Proxy not attached to service");
192         return new ArrayList<BluetoothDevice>();
193     }
194 
195     /**
196      * {@inheritDoc}
197      */
198     @Override
getDevicesMatchingConnectionStates(int[] states)199     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
200         if (VDBG) log("getDevicesMatchingStates()");
201         final IBluetoothAvrcpController service = mService;
202         if (service != null && isEnabled()) {
203             try {
204                 return service.getDevicesMatchingConnectionStates(states);
205             } catch (RemoteException e) {
206                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
207                 return new ArrayList<BluetoothDevice>();
208             }
209         }
210         if (service == null) Log.w(TAG, "Proxy not attached to service");
211         return new ArrayList<BluetoothDevice>();
212     }
213 
214     /**
215      * {@inheritDoc}
216      */
217     @Override
getConnectionState(BluetoothDevice device)218     public int getConnectionState(BluetoothDevice device) {
219         if (VDBG) log("getState(" + device + ")");
220         final IBluetoothAvrcpController service = mService;
221         if (service != null && isEnabled() && isValidDevice(device)) {
222             try {
223                 return service.getConnectionState(device);
224             } catch (RemoteException e) {
225                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
226                 return BluetoothProfile.STATE_DISCONNECTED;
227             }
228         }
229         if (service == null) Log.w(TAG, "Proxy not attached to service");
230         return BluetoothProfile.STATE_DISCONNECTED;
231     }
232 
233     /**
234      * Gets the player application settings.
235      *
236      * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
237      */
getPlayerSettings(BluetoothDevice device)238     public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
239         if (DBG) Log.d(TAG, "getPlayerSettings");
240         BluetoothAvrcpPlayerSettings settings = null;
241         final IBluetoothAvrcpController service = mService;
242         if (service != null && isEnabled()) {
243             try {
244                 settings = service.getPlayerSettings(device);
245             } 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     /**
254      * Sets the player app setting for current player.
255      * returns true in case setting is supported by remote, false otherwise
256      */
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)257     public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
258         if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
259         final IBluetoothAvrcpController service = mService;
260         if (service != null && isEnabled()) {
261             try {
262                 return service.setPlayerApplicationSetting(plAppSetting);
263             } catch (RemoteException e) {
264                 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
265                 return false;
266             }
267         }
268         if (service == null) Log.w(TAG, "Proxy not attached to service");
269         return false;
270     }
271 
272     /**
273      * Send Group Navigation Command to Remote.
274      * possible keycode values: next_grp, previous_grp defined above
275      */
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)276     public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
277         Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
278                 + keyState);
279         final IBluetoothAvrcpController service = mService;
280         if (service != null && isEnabled()) {
281             try {
282                 service.sendGroupNavigationCmd(device, keyCode, keyState);
283                 return;
284             } catch (RemoteException e) {
285                 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
286                 return;
287             }
288         }
289         if (service == null) Log.w(TAG, "Proxy not attached to service");
290     }
291 
292     private final ServiceConnection mConnection = new ServiceConnection() {
293         public void onServiceConnected(ComponentName className, IBinder service) {
294             if (DBG) Log.d(TAG, "Proxy object connected");
295             mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
296             if (mServiceListener != null) {
297                 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
298                         BluetoothAvrcpController.this);
299             }
300         }
301 
302         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 
isEnabled()311     private boolean isEnabled() {
312         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
313     }
314 
isValidDevice(BluetoothDevice device)315     private static boolean isValidDevice(BluetoothDevice device) {
316         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
317     }
318 
log(String msg)319     private static void log(String msg) {
320         Log.d(TAG, msg);
321     }
322 }
323