BluetoothA2dp.java revision f5b4b98fada53d91c4c2ebeb5a1d33ccc95c94d2
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 17package android.bluetooth; 18 19import android.annotation.SdkConstant; 20import android.annotation.SdkConstant.SdkConstantType; 21import android.server.BluetoothA2dpService; 22import android.content.Context; 23import android.os.ServiceManager; 24import android.os.RemoteException; 25import android.os.IBinder; 26import android.util.Log; 27 28import java.util.List; 29 30/** 31 * Public API for controlling the Bluetooth A2DP Profile Service. 32 * 33 * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP 34 * Service via IPC. 35 * 36 * Creating a BluetoothA2dp object will initiate a binding with the 37 * BluetoothHeadset service. Users of this object should call close() when they 38 * are finished, so that this proxy object can unbind from the service. 39 * 40 * Currently the BluetoothA2dp service runs in the system server and this 41 * proxy object will be immediately bound to the service on construction. 42 * However this may change in future releases, and error codes such as 43 * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the 44 * proxy object is not yet attached. 45 * 46 * Currently this class provides methods to connect to A2DP audio sinks. 47 * 48 * @hide 49 */ 50public class BluetoothA2dp { 51 private static final String TAG = "BluetoothA2dp"; 52 private static final boolean DBG = false; 53 54 /** int extra for SINK_STATE_CHANGED_ACTION */ 55 public static final String SINK_STATE = 56 "android.bluetooth.a2dp.intent.SINK_STATE"; 57 /** int extra for SINK_STATE_CHANGED_ACTION */ 58 public static final String SINK_PREVIOUS_STATE = 59 "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE"; 60 61 /** Indicates the state of an A2DP audio sink has changed. 62 * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and 63 * BluetoothIntent.ADDRESS extras. 64 */ 65 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 66 public static final String SINK_STATE_CHANGED_ACTION = 67 "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED"; 68 69 public static final int STATE_DISCONNECTED = 0; 70 public static final int STATE_CONNECTING = 1; 71 public static final int STATE_CONNECTED = 2; 72 public static final int STATE_DISCONNECTING = 3; 73 /** Playing implies connected */ 74 public static final int STATE_PLAYING = 4; 75 76 /** Default priority for a2dp devices that should allow incoming 77 * connections */ 78 public static final int PRIORITY_AUTO = 100; 79 /** Default priority for a2dp devices that should not allow incoming 80 * connections */ 81 public static final int PRIORITY_OFF = 0; 82 private final IBluetoothA2dp mService; 83 private final Context mContext; 84 85 /** 86 * Create a BluetoothA2dp proxy object for interacting with the local 87 * Bluetooth A2DP service. 88 * @param c Context 89 */ 90 public BluetoothA2dp(Context c) { 91 mContext = c; 92 IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); 93 if (b == null) { 94 throw new RuntimeException("Bluetooth A2DP service not available!"); 95 } 96 mService = IBluetoothA2dp.Stub.asInterface(b); 97 } 98 99 /** Initiate a connection to an A2DP sink. 100 * Listen for SINK_STATE_CHANGED_ACTION to find out when the 101 * connection is completed. 102 * @param address Remote BT address. 103 * @return Result code, negative indicates an immediate error. 104 * @hide 105 */ 106 public int connectSink(String address) { 107 if (DBG) log("connectSink(" + address + ")"); 108 try { 109 return mService.connectSink(address); 110 } catch (RemoteException e) { 111 Log.w(TAG, "", e); 112 return BluetoothError.ERROR_IPC; 113 } 114 } 115 116 /** Initiate disconnect from an A2DP sink. 117 * Listen for SINK_STATE_CHANGED_ACTION to find out when 118 * disconnect is completed. 119 * @param address Remote BT address. 120 * @return Result code, negative indicates an immediate error. 121 * @hide 122 */ 123 public int disconnectSink(String address) { 124 if (DBG) log("disconnectSink(" + address + ")"); 125 try { 126 return mService.disconnectSink(address); 127 } catch (RemoteException e) { 128 Log.w(TAG, "", e); 129 return BluetoothError.ERROR_IPC; 130 } 131 } 132 133 /** Check if a specified A2DP sink is connected. 134 * @param address Remote BT address. 135 * @return True if connected (or playing), false otherwise and on error. 136 * @hide 137 */ 138 public boolean isSinkConnected(String address) { 139 if (DBG) log("isSinkConnected(" + address + ")"); 140 int state = getSinkState(address); 141 return state == STATE_CONNECTED || state == STATE_PLAYING; 142 } 143 144 /** Check if any A2DP sink is connected. 145 * @return a List of connected A2DP sinks, or null on error. 146 * @hide 147 */ 148 public List<String> listConnectedSinks() { 149 if (DBG) log("listConnectedSinks()"); 150 try { 151 return mService.listConnectedSinks(); 152 } catch (RemoteException e) { 153 Log.w(TAG, "", e); 154 return null; 155 } 156 } 157 158 /** Get the state of an A2DP sink 159 * @param address Remote BT address. 160 * @return State code, or negative on error 161 * @hide 162 */ 163 public int getSinkState(String address) { 164 if (DBG) log("getSinkState(" + address + ")"); 165 try { 166 return mService.getSinkState(address); 167 } catch (RemoteException e) { 168 Log.w(TAG, "", e); 169 return BluetoothError.ERROR_IPC; 170 } 171 } 172 173 /** 174 * Set priority of a2dp sink. 175 * Priority is a non-negative integer. By default paired sinks will have 176 * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). 177 * Sinks with priority greater than zero will accept incoming connections 178 * (if no sink is currently connected). 179 * Priority for unpaired sink must be PRIORITY_NONE. 180 * @param address Paired sink 181 * @param priority Integer priority, for example PRIORITY_AUTO or 182 * PRIORITY_NONE 183 * @return Result code, negative indicates an error 184 */ 185 public int setSinkPriority(String address, int priority) { 186 if (DBG) log("setSinkPriority(" + address + ", " + priority + ")"); 187 try { 188 return mService.setSinkPriority(address, priority); 189 } catch (RemoteException e) { 190 Log.w(TAG, "", e); 191 return BluetoothError.ERROR_IPC; 192 } 193 } 194 195 /** 196 * Get priority of a2dp sink. 197 * @param address Sink 198 * @return non-negative priority, or negative error code on error. 199 */ 200 public int getSinkPriority(String address) { 201 if (DBG) log("getSinkPriority(" + address + ")"); 202 try { 203 return mService.getSinkPriority(address); 204 } catch (RemoteException e) { 205 Log.w(TAG, "", e); 206 return BluetoothError.ERROR_IPC; 207 } 208 } 209 210 /** 211 * Check class bits for possible A2DP Sink support. 212 * This is a simple heuristic that tries to guess if a device with the 213 * given class bits might be a A2DP Sink. It is not accurate for all 214 * devices. It tries to err on the side of false positives. 215 * @return True if this device might be a A2DP sink 216 */ 217 public static boolean doesClassMatchSink(int btClass) { 218 if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { 219 return true; 220 } 221 // By the A2DP spec, sinks must indicate the RENDER service. 222 // However we found some that do not (Chordette). So lets also 223 // match on some other class bits. 224 switch (BluetoothClass.Device.getDevice(btClass)) { 225 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: 226 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: 227 case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: 228 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 229 return true; 230 default: 231 return false; 232 } 233 } 234 235 /** Helper for converting a state to a string. 236 * For debug use only - strings are not internationalized. 237 * @hide 238 */ 239 public static String stateToString(int state) { 240 switch (state) { 241 case STATE_DISCONNECTED: 242 return "disconnected"; 243 case STATE_CONNECTING: 244 return "connecting"; 245 case STATE_CONNECTED: 246 return "connected"; 247 case STATE_DISCONNECTING: 248 return "disconnecting"; 249 case STATE_PLAYING: 250 return "playing"; 251 default: 252 return "<unknown state " + state + ">"; 253 } 254 } 255 256 private static void log(String msg) { 257 Log.d(TAG, msg); 258 } 259} 260