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