1 /* 2 * Copyright (C) 2008 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.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.server.BluetoothA2dpService; 22 import android.content.Context; 23 import android.os.ServiceManager; 24 import android.os.RemoteException; 25 import android.os.IBinder; 26 import android.util.Log; 27 28 import java.util.Arrays; 29 import java.util.Collections; 30 import java.util.Set; 31 import java.util.HashSet; 32 33 /** 34 * Public API for controlling the Bluetooth A2DP Profile Service. 35 * 36 * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP 37 * Service via IPC. 38 * 39 * Creating a BluetoothA2dp object will initiate a binding with the 40 * BluetoothHeadset service. Users of this object should call close() when they 41 * are finished, so that this proxy object can unbind from the service. 42 * 43 * Currently the BluetoothA2dp service runs in the system server and this 44 * proxy object will be immediately bound to the service on construction. 45 * 46 * Currently this class provides methods to connect to A2DP audio sinks. 47 * 48 * @hide 49 */ 50 public final class BluetoothA2dp { 51 private static final String TAG = "BluetoothA2dp"; 52 private static final boolean DBG = false; 53 54 /** int extra for ACTION_SINK_STATE_CHANGED */ 55 public static final String EXTRA_SINK_STATE = 56 "android.bluetooth.a2dp.extra.SINK_STATE"; 57 /** int extra for ACTION_SINK_STATE_CHANGED */ 58 public static final String EXTRA_PREVIOUS_SINK_STATE = 59 "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE"; 60 61 /** Indicates the state of an A2DP audio sink has changed. 62 * This intent will always contain EXTRA_SINK_STATE, 63 * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE 64 * extras. 65 */ 66 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 67 public static final String ACTION_SINK_STATE_CHANGED = 68 "android.bluetooth.a2dp.action.SINK_STATE_CHANGED"; 69 70 public static final int STATE_DISCONNECTED = 0; 71 public static final int STATE_CONNECTING = 1; 72 public static final int STATE_CONNECTED = 2; 73 public static final int STATE_DISCONNECTING = 3; 74 /** Playing implies connected */ 75 public static final int STATE_PLAYING = 4; 76 77 /** Default priority for a2dp devices that we try to auto-connect 78 * and allow incoming connections */ 79 public static final int PRIORITY_AUTO_CONNECT = 1000; 80 /** Default priority for a2dp devices that should allow incoming 81 * connections */ 82 public static final int PRIORITY_ON = 100; 83 /** Default priority for a2dp devices that should not allow incoming 84 * connections */ 85 public static final int PRIORITY_OFF = 0; 86 /** Default priority when not set or when the device is unpaired */ 87 public static final int PRIORITY_UNDEFINED = -1; 88 89 private final IBluetoothA2dp mService; 90 private final Context mContext; 91 92 /** 93 * Create a BluetoothA2dp proxy object for interacting with the local 94 * Bluetooth A2DP service. 95 * @param c Context 96 */ 97 public BluetoothA2dp(Context c) { 98 mContext = c; 99 100 IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); 101 if (b != null) { 102 mService = IBluetoothA2dp.Stub.asInterface(b); 103 } else { 104 Log.w(TAG, "Bluetooth A2DP service not available!"); 105 106 // Instead of throwing an exception which prevents people from going 107 // into Wireless settings in the emulator. Let it crash later when it is actually used. 108 mService = null; 109 } 110 } 111 112 /** Initiate a connection to an A2DP sink. 113 * Listen for SINK_STATE_CHANGED_ACTION to find out when the 114 * connection is completed. 115 * @param device Remote BT device. 116 * @return false on immediate error, true otherwise 117 * @hide 118 */ 119 public boolean connectSink(BluetoothDevice device) { 120 if (DBG) log("connectSink(" + device + ")"); 121 try { 122 return mService.connectSink(device); 123 } catch (RemoteException e) { 124 Log.e(TAG, "", e); 125 return false; 126 } 127 } 128 129 /** Initiate disconnect from an A2DP sink. 130 * Listen for SINK_STATE_CHANGED_ACTION to find out when 131 * disconnect is completed. 132 * @param device Remote BT device. 133 * @return false on immediate error, true otherwise 134 * @hide 135 */ 136 public boolean disconnectSink(BluetoothDevice device) { 137 if (DBG) log("disconnectSink(" + device + ")"); 138 try { 139 return mService.disconnectSink(device); 140 } catch (RemoteException e) { 141 Log.e(TAG, "", e); 142 return false; 143 } 144 } 145 146 /** Initiate suspend from an A2DP sink. 147 * Listen for SINK_STATE_CHANGED_ACTION to find out when 148 * suspend is completed. 149 * @param device Remote BT device. 150 * @return false on immediate error, true otherwise 151 * @hide 152 */ 153 public boolean suspendSink(BluetoothDevice device) { 154 try { 155 return mService.suspendSink(device); 156 } catch (RemoteException e) { 157 Log.e(TAG, "", e); 158 return false; 159 } 160 } 161 162 /** Initiate resume from an suspended A2DP sink. 163 * Listen for SINK_STATE_CHANGED_ACTION to find out when 164 * resume is completed. 165 * @param device Remote BT device. 166 * @return false on immediate error, true otherwise 167 * @hide 168 */ 169 public boolean resumeSink(BluetoothDevice device) { 170 try { 171 return mService.resumeSink(device); 172 } catch (RemoteException e) { 173 Log.e(TAG, "", e); 174 return false; 175 } 176 } 177 178 /** Check if a specified A2DP sink is connected. 179 * @param device Remote BT device. 180 * @return True if connected (or playing), false otherwise and on error. 181 * @hide 182 */ 183 public boolean isSinkConnected(BluetoothDevice device) { 184 if (DBG) log("isSinkConnected(" + device + ")"); 185 int state = getSinkState(device); 186 return state == STATE_CONNECTED || state == STATE_PLAYING; 187 } 188 189 /** Check if any A2DP sink is connected. 190 * @return a unmodifiable set of connected A2DP sinks, or null on error. 191 * @hide 192 */ 193 public Set<BluetoothDevice> getConnectedSinks() { 194 if (DBG) log("getConnectedSinks()"); 195 try { 196 return Collections.unmodifiableSet( 197 new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks()))); 198 } catch (RemoteException e) { 199 Log.e(TAG, "", e); 200 return null; 201 } 202 } 203 204 /** Check if any A2DP sink is in Non Disconnected state 205 * i.e playing, connected, connecting, disconnecting. 206 * @return a unmodifiable set of connected A2DP sinks, or null on error. 207 * @hide 208 */ 209 public Set<BluetoothDevice> getNonDisconnectedSinks() { 210 if (DBG) log("getNonDisconnectedSinks()"); 211 try { 212 return Collections.unmodifiableSet( 213 new HashSet<BluetoothDevice>(Arrays.asList(mService.getNonDisconnectedSinks()))); 214 } catch (RemoteException e) { 215 Log.e(TAG, "", e); 216 return null; 217 } 218 } 219 220 /** Get the state of an A2DP sink 221 * @param device Remote BT device. 222 * @return State code, one of STATE_ 223 * @hide 224 */ 225 public int getSinkState(BluetoothDevice device) { 226 if (DBG) log("getSinkState(" + device + ")"); 227 try { 228 return mService.getSinkState(device); 229 } catch (RemoteException e) { 230 Log.e(TAG, "", e); 231 return BluetoothA2dp.STATE_DISCONNECTED; 232 } 233 } 234 235 /** 236 * Set priority of a2dp sink. 237 * Priority is a non-negative integer. By default paired sinks will have 238 * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). 239 * Sinks with priority greater than zero will accept incoming connections 240 * (if no sink is currently connected). 241 * Priority for unpaired sink must be PRIORITY_NONE. 242 * @param device Paired sink 243 * @param priority Integer priority, for example PRIORITY_AUTO or 244 * PRIORITY_NONE 245 * @return true if priority is set, false on error 246 */ 247 public boolean setSinkPriority(BluetoothDevice device, int priority) { 248 if (DBG) log("setSinkPriority(" + device + ", " + priority + ")"); 249 try { 250 return mService.setSinkPriority(device, priority); 251 } catch (RemoteException e) { 252 Log.e(TAG, "", e); 253 return false; 254 } 255 } 256 257 /** 258 * Get priority of a2dp sink. 259 * @param device Sink 260 * @return non-negative priority, or negative error code on error. 261 */ 262 public int getSinkPriority(BluetoothDevice device) { 263 if (DBG) log("getSinkPriority(" + device + ")"); 264 try { 265 return mService.getSinkPriority(device); 266 } catch (RemoteException e) { 267 Log.e(TAG, "", e); 268 return PRIORITY_OFF; 269 } 270 } 271 272 /** Helper for converting a state to a string. 273 * For debug use only - strings are not internationalized. 274 * @hide 275 */ 276 public static String stateToString(int state) { 277 switch (state) { 278 case STATE_DISCONNECTED: 279 return "disconnected"; 280 case STATE_CONNECTING: 281 return "connecting"; 282 case STATE_CONNECTED: 283 return "connected"; 284 case STATE_DISCONNECTING: 285 return "disconnecting"; 286 case STATE_PLAYING: 287 return "playing"; 288 default: 289 return "<unknown state " + state + ">"; 290 } 291 } 292 293 private static void log(String msg) { 294 Log.d(TAG, msg); 295 } 296 } 297