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