• 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     final private 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      *
120      */
BluetoothAvrcpController(Context context, ServiceListener l)121     /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
122         mContext = context;
123         mServiceListener = l;
124         mAdapter = BluetoothAdapter.getDefaultAdapter();
125         IBluetoothManager mgr = mAdapter.getBluetoothManager();
126         if (mgr != null) {
127             try {
128                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
129             } catch (RemoteException e) {
130                 Log.e(TAG,"",e);
131             }
132         }
133 
134         doBind();
135     }
136 
doBind()137     boolean doBind() {
138         Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
139         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
140         intent.setComponent(comp);
141         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
142                 android.os.Process.myUserHandle())) {
143             Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
144             return false;
145         }
146         return true;
147     }
148 
close()149     /*package*/ void close() {
150         mServiceListener = null;
151         IBluetoothManager mgr = mAdapter.getBluetoothManager();
152         if (mgr != null) {
153             try {
154                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
155             } catch (Exception e) {
156                 Log.e(TAG,"",e);
157             }
158         }
159 
160         synchronized (mConnection) {
161             if (mService != null) {
162                 try {
163                     mService = null;
164                     mContext.unbindService(mConnection);
165                 } catch (Exception re) {
166                     Log.e(TAG,"",re);
167                 }
168             }
169         }
170     }
171 
finalize()172     public void finalize() {
173         close();
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
getConnectedDevices()179     public List<BluetoothDevice> getConnectedDevices() {
180         if (VDBG) log("getConnectedDevices()");
181         final IBluetoothAvrcpController service = mService;
182         if (service != null && isEnabled()) {
183             try {
184                 return service.getConnectedDevices();
185             } catch (RemoteException e) {
186                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
187                 return new ArrayList<BluetoothDevice>();
188             }
189         }
190         if (service == null) Log.w(TAG, "Proxy not attached to service");
191         return new ArrayList<BluetoothDevice>();
192     }
193 
194     /**
195      * {@inheritDoc}
196      */
getDevicesMatchingConnectionStates(int[] states)197     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
198         if (VDBG) log("getDevicesMatchingStates()");
199         final IBluetoothAvrcpController service = mService;
200         if (service != null && isEnabled()) {
201             try {
202                 return service.getDevicesMatchingConnectionStates(states);
203             } catch (RemoteException e) {
204                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
205                 return new ArrayList<BluetoothDevice>();
206             }
207         }
208         if (service == null) Log.w(TAG, "Proxy not attached to service");
209         return new ArrayList<BluetoothDevice>();
210     }
211 
212     /**
213      * {@inheritDoc}
214      */
getConnectionState(BluetoothDevice device)215     public int getConnectionState(BluetoothDevice device) {
216         if (VDBG) log("getState(" + device + ")");
217         final IBluetoothAvrcpController service = mService;
218         if (service != null && isEnabled() && isValidDevice(device)) {
219             try {
220                 return service.getConnectionState(device);
221             } catch (RemoteException e) {
222                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
223                 return BluetoothProfile.STATE_DISCONNECTED;
224             }
225         }
226         if (service == null) Log.w(TAG, "Proxy not attached to service");
227         return BluetoothProfile.STATE_DISCONNECTED;
228     }
229 
230     /**
231      * Gets the player application settings.
232      *
233      * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
234      */
getPlayerSettings(BluetoothDevice device)235     public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
236         if (DBG) Log.d(TAG, "getPlayerSettings");
237         BluetoothAvrcpPlayerSettings settings = null;
238         final IBluetoothAvrcpController service = mService;
239         if (service != null && isEnabled()) {
240             try {
241                 settings = service.getPlayerSettings(device);
242             } catch (RemoteException e) {
243                 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
244                 return null;
245             }
246         }
247         return settings;
248     }
249 
250     /**
251      * Sets the player app setting for current player.
252      * returns true in case setting is supported by remote, false otherwise
253      */
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)254     public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
255         if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
256         final IBluetoothAvrcpController service = mService;
257         if (service != null && isEnabled()) {
258             try {
259                 return service.setPlayerApplicationSetting(plAppSetting);
260             } catch (RemoteException e) {
261                 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
262                 return false;
263             }
264         }
265         if (service == null) Log.w(TAG, "Proxy not attached to service");
266         return false;
267     }
268 
269     /*
270      * Send Group Navigation Command to Remote.
271      * possible keycode values: next_grp, previous_grp defined above
272      */
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)273     public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
274         Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
275                 + keyState);
276         final IBluetoothAvrcpController service = mService;
277         if (service != null && isEnabled()) {
278             try {
279                 service.sendGroupNavigationCmd(device, keyCode, keyState);
280                 return;
281             } catch (RemoteException e) {
282                 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
283                 return;
284             }
285         }
286         if (service == null) Log.w(TAG, "Proxy not attached to service");
287     }
288 
289     private final ServiceConnection mConnection = new ServiceConnection() {
290         public void onServiceConnected(ComponentName className, IBinder service) {
291             if (DBG) Log.d(TAG, "Proxy object connected");
292             mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
293             if (mServiceListener != null) {
294                 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
295                         BluetoothAvrcpController.this);
296             }
297         }
298         public void onServiceDisconnected(ComponentName className) {
299             if (DBG) Log.d(TAG, "Proxy object disconnected");
300             mService = null;
301             if (mServiceListener != null) {
302                 mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
303             }
304         }
305     };
306 
isEnabled()307     private boolean isEnabled() {
308         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
309     }
310 
isValidDevice(BluetoothDevice device)311     private static boolean isValidDevice(BluetoothDevice device) {
312         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
313     }
314 
log(String msg)315     private static void log(String msg) {
316       Log.d(TAG, msg);
317     }
318 }
319