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.media.MediaMetadata; 24 import android.media.session.PlaybackState; 25 import android.os.Binder; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently 35 * supports player information, playback support and track metadata. 36 * 37 *<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP 38 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 39 * the BluetoothAvrcpController proxy object. 40 * 41 * {@hide} 42 */ 43 public final class BluetoothAvrcpController implements BluetoothProfile { 44 private static final String TAG = "BluetoothAvrcpController"; 45 private static final boolean DBG = false; 46 private static final boolean VDBG = false; 47 48 /** 49 * Intent used to broadcast the change in connection state of the AVRCP Controller 50 * profile. 51 * 52 * <p>This intent will have 3 extras: 53 * <ul> 54 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 55 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> 56 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 57 * </ul> 58 * 59 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 60 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 61 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 62 * 63 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 64 * receive. 65 */ 66 public static final String ACTION_CONNECTION_STATE_CHANGED = 67 "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; 68 69 /** 70 * Intent used to broadcast the change in player application setting state on AVRCP AG. 71 * 72 * <p>This intent will have the following extras: 73 * <ul> 74 * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the 75 * most recent player setting. </li> 76 * </ul> 77 */ 78 public static final String ACTION_PLAYER_SETTING = 79 "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; 80 81 public static final String EXTRA_PLAYER_SETTING = 82 "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; 83 84 private Context mContext; 85 private ServiceListener mServiceListener; 86 private IBluetoothAvrcpController mService; 87 private BluetoothAdapter mAdapter; 88 89 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 90 new IBluetoothStateChangeCallback.Stub() { 91 public void onBluetoothStateChange(boolean up) { 92 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); 93 if (!up) { 94 if (VDBG) Log.d(TAG,"Unbinding service..."); 95 synchronized (mConnection) { 96 try { 97 mService = null; 98 mContext.unbindService(mConnection); 99 } catch (Exception re) { 100 Log.e(TAG,"",re); 101 } 102 } 103 } else { 104 synchronized (mConnection) { 105 try { 106 if (mService == null) { 107 if (VDBG) Log.d(TAG,"Binding service..."); 108 doBind(); 109 } 110 } catch (Exception re) { 111 Log.e(TAG,"",re); 112 } 113 } 114 } 115 } 116 }; 117 118 /** 119 * Create a BluetoothAvrcpController proxy object for interacting with the local 120 * Bluetooth AVRCP service. 121 * 122 */ BluetoothAvrcpController(Context context, ServiceListener l)123 /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { 124 mContext = context; 125 mServiceListener = l; 126 mAdapter = BluetoothAdapter.getDefaultAdapter(); 127 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 128 if (mgr != null) { 129 try { 130 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 131 } catch (RemoteException e) { 132 Log.e(TAG,"",e); 133 } 134 } 135 136 doBind(); 137 } 138 doBind()139 boolean doBind() { 140 Intent intent = new Intent(IBluetoothAvrcpController.class.getName()); 141 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 142 intent.setComponent(comp); 143 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 144 android.os.Process.myUserHandle())) { 145 Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent); 146 return false; 147 } 148 return true; 149 } 150 close()151 /*package*/ void close() { 152 mServiceListener = null; 153 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 154 if (mgr != null) { 155 try { 156 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 157 } catch (Exception e) { 158 Log.e(TAG,"",e); 159 } 160 } 161 162 synchronized (mConnection) { 163 if (mService != null) { 164 try { 165 mService = null; 166 mContext.unbindService(mConnection); 167 } catch (Exception re) { 168 Log.e(TAG,"",re); 169 } 170 } 171 } 172 } 173 finalize()174 public void finalize() { 175 close(); 176 } 177 178 /** 179 * {@inheritDoc} 180 */ getConnectedDevices()181 public List<BluetoothDevice> getConnectedDevices() { 182 if (VDBG) log("getConnectedDevices()"); 183 if (mService != null && isEnabled()) { 184 try { 185 return mService.getConnectedDevices(); 186 } catch (RemoteException e) { 187 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 188 return new ArrayList<BluetoothDevice>(); 189 } 190 } 191 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 192 return new ArrayList<BluetoothDevice>(); 193 } 194 195 /** 196 * {@inheritDoc} 197 */ getDevicesMatchingConnectionStates(int[] states)198 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 199 if (VDBG) log("getDevicesMatchingStates()"); 200 if (mService != null && isEnabled()) { 201 try { 202 return mService.getDevicesMatchingConnectionStates(states); 203 } catch (RemoteException e) { 204 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 205 return new ArrayList<BluetoothDevice>(); 206 } 207 } 208 if (mService == 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 if (mService != null && isEnabled() 218 && isValidDevice(device)) { 219 try { 220 return mService.getConnectionState(device); 221 } catch (RemoteException e) { 222 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 223 return BluetoothProfile.STATE_DISCONNECTED; 224 } 225 } 226 if (mService == 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 if (mService != null && isEnabled()) { 239 try { 240 settings = mService.getPlayerSettings(device); 241 } catch (RemoteException e) { 242 Log.e(TAG, "Error talking to BT service in getMetadata() " + e); 243 return null; 244 } 245 } 246 return settings; 247 } 248 249 /** 250 * Sets the player app setting for current player. 251 * returns true in case setting is supported by remote, false otherwise 252 */ setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)253 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { 254 if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); 255 if (mService != null && isEnabled()) { 256 try { 257 return mService.setPlayerApplicationSetting(plAppSetting); 258 } catch (RemoteException e) { 259 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); 260 return false; 261 } 262 } 263 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 264 return false; 265 } 266 267 /* 268 * Send Group Navigation Command to Remote. 269 * possible keycode values: next_grp, previous_grp defined above 270 */ sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)271 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { 272 Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); 273 if (mService != null && isEnabled()) { 274 try { 275 mService.sendGroupNavigationCmd(device, keyCode, keyState); 276 return; 277 } catch (RemoteException e) { 278 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); 279 return; 280 } 281 } 282 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 283 } 284 285 private final ServiceConnection mConnection = new ServiceConnection() { 286 public void onServiceConnected(ComponentName className, IBinder service) { 287 if (DBG) Log.d(TAG, "Proxy object connected"); 288 mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); 289 290 if (mServiceListener != null) { 291 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, 292 BluetoothAvrcpController.this); 293 } 294 } 295 public void onServiceDisconnected(ComponentName className) { 296 if (DBG) Log.d(TAG, "Proxy object disconnected"); 297 mService = null; 298 if (mServiceListener != null) { 299 mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); 300 } 301 } 302 }; 303 isEnabled()304 private boolean isEnabled() { 305 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 306 return false; 307 } 308 isValidDevice(BluetoothDevice device)309 private boolean isValidDevice(BluetoothDevice device) { 310 if (device == null) return false; 311 312 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 313 return false; 314 } 315 log(String msg)316 private static void log(String msg) { 317 Log.d(TAG, msg); 318 } 319 } 320