• 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.annotation.RequiresPermission;
20 import android.annotation.SdkConstant;
21 import android.annotation.SuppressLint;
22 import android.annotation.SdkConstant.SdkConstantType;
23 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
24 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
25 import android.content.Attributable;
26 import android.content.AttributionSource;
27 import android.content.Context;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.util.Log;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
38  * supports player information, playback support and track metadata.
39  *
40  * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
41  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
42  * the BluetoothAvrcpController proxy object.
43  *
44  * {@hide}
45  */
46 public final class BluetoothAvrcpController implements BluetoothProfile {
47     private static final String TAG = "BluetoothAvrcpController";
48     private static final boolean DBG = false;
49     private static final boolean VDBG = false;
50 
51     /**
52      * Intent used to broadcast the change in connection state of the AVRCP Controller
53      * profile.
54      *
55      * <p>This intent will have 3 extras:
56      * <ul>
57      * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
58      * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
59      * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
60      * </ul>
61      *
62      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
63      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
64      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
65      */
66     @RequiresLegacyBluetoothPermission
67     @RequiresBluetoothConnectPermission
68     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
69     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
70     public static final String ACTION_CONNECTION_STATE_CHANGED =
71             "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
72 
73     /**
74      * Intent used to broadcast the change in player application setting state on AVRCP AG.
75      *
76      * <p>This intent will have the following extras:
77      * <ul>
78      * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
79      * most recent player setting. </li>
80      * </ul>
81      */
82     @RequiresBluetoothConnectPermission
83     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
84     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
85     public static final String ACTION_PLAYER_SETTING =
86             "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
87 
88     public static final String EXTRA_PLAYER_SETTING =
89             "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
90 
91     private final BluetoothAdapter mAdapter;
92     private final AttributionSource mAttributionSource;
93     private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
94             new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
95                     "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
96                 @Override
97                 public IBluetoothAvrcpController getServiceInterface(IBinder service) {
98                     return IBluetoothAvrcpController.Stub.asInterface(
99                             Binder.allowBlocking(service));
100                 }
101     };
102 
103     /**
104      * Create a BluetoothAvrcpController proxy object for interacting with the local
105      * Bluetooth AVRCP service.
106      */
BluetoothAvrcpController(Context context, ServiceListener listener, BluetoothAdapter adapter)107     /* package */ BluetoothAvrcpController(Context context, ServiceListener listener,
108             BluetoothAdapter adapter) {
109         mAdapter = adapter;
110         mAttributionSource = adapter.getAttributionSource();
111         mProfileConnector.connect(context, listener);
112     }
113 
close()114     /*package*/ void close() {
115         mProfileConnector.disconnect();
116     }
117 
getService()118     private IBluetoothAvrcpController getService() {
119         return mProfileConnector.getService();
120     }
121 
122     @Override
finalize()123     public void finalize() {
124         close();
125     }
126 
127     /**
128      * {@inheritDoc}
129      */
130     @Override
131     @RequiresBluetoothConnectPermission
132     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getConnectedDevices()133     public List<BluetoothDevice> getConnectedDevices() {
134         if (VDBG) log("getConnectedDevices()");
135         final IBluetoothAvrcpController service =
136                 getService();
137         if (service != null && isEnabled()) {
138             try {
139                 return Attributable.setAttributionSource(
140                         service.getConnectedDevices(mAttributionSource), mAttributionSource);
141             } catch (RemoteException e) {
142                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
143                 return new ArrayList<BluetoothDevice>();
144             }
145         }
146         if (service == null) Log.w(TAG, "Proxy not attached to service");
147         return new ArrayList<BluetoothDevice>();
148     }
149 
150     /**
151      * {@inheritDoc}
152      */
153     @Override
154     @RequiresBluetoothConnectPermission
155     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getDevicesMatchingConnectionStates(int[] states)156     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
157         if (VDBG) log("getDevicesMatchingStates()");
158         final IBluetoothAvrcpController service =
159                 getService();
160         if (service != null && isEnabled()) {
161             try {
162                 return Attributable.setAttributionSource(
163                         service.getDevicesMatchingConnectionStates(states, mAttributionSource),
164                         mAttributionSource);
165             } catch (RemoteException e) {
166                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
167                 return new ArrayList<BluetoothDevice>();
168             }
169         }
170         if (service == null) Log.w(TAG, "Proxy not attached to service");
171         return new ArrayList<BluetoothDevice>();
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     @RequiresBluetoothConnectPermission
179     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getConnectionState(BluetoothDevice device)180     public int getConnectionState(BluetoothDevice device) {
181         if (VDBG) log("getState(" + device + ")");
182         final IBluetoothAvrcpController service =
183                 getService();
184         if (service != null && isEnabled() && isValidDevice(device)) {
185             try {
186                 return service.getConnectionState(device, mAttributionSource);
187             } catch (RemoteException e) {
188                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
189                 return BluetoothProfile.STATE_DISCONNECTED;
190             }
191         }
192         if (service == null) Log.w(TAG, "Proxy not attached to service");
193         return BluetoothProfile.STATE_DISCONNECTED;
194     }
195 
196     /**
197      * Gets the player application settings.
198      *
199      * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
200      */
201     @RequiresBluetoothConnectPermission
202     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getPlayerSettings(BluetoothDevice device)203     public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
204         if (DBG) Log.d(TAG, "getPlayerSettings");
205         BluetoothAvrcpPlayerSettings settings = null;
206         final IBluetoothAvrcpController service =
207                 getService();
208         if (service != null && isEnabled()) {
209             try {
210                 settings = service.getPlayerSettings(device, mAttributionSource);
211             } catch (RemoteException e) {
212                 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
213                 return null;
214             }
215         }
216         return settings;
217     }
218 
219     /**
220      * Sets the player app setting for current player.
221      * returns true in case setting is supported by remote, false otherwise
222      */
223     @RequiresBluetoothConnectPermission
224     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)225     public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
226         if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
227         final IBluetoothAvrcpController service =
228                 getService();
229         if (service != null && isEnabled()) {
230             try {
231                 return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource);
232             } catch (RemoteException e) {
233                 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
234                 return false;
235             }
236         }
237         if (service == null) Log.w(TAG, "Proxy not attached to service");
238         return false;
239     }
240 
241     /**
242      * Send Group Navigation Command to Remote.
243      * possible keycode values: next_grp, previous_grp defined above
244      */
245     @RequiresBluetoothConnectPermission
246     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)247     public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
248         Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
249                 + keyState);
250         final IBluetoothAvrcpController service =
251                 getService();
252         if (service != null && isEnabled()) {
253             try {
254                 service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource);
255                 return;
256             } catch (RemoteException e) {
257                 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
258                 return;
259             }
260         }
261         if (service == null) Log.w(TAG, "Proxy not attached to service");
262     }
263 
isEnabled()264     private boolean isEnabled() {
265         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
266     }
267 
isValidDevice(BluetoothDevice device)268     private static boolean isValidDevice(BluetoothDevice device) {
269         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
270     }
271 
log(String msg)272     private static void log(String msg) {
273         Log.d(TAG, msg);
274     }
275 }
276