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