Merge "New public APIs for BluetoothA2dp and BluetoothHeadset profiles."

This commit is contained in:
Jaikumar Ganesh
2010-09-28 19:56:05 -07:00
committed by Android (Google) Code Review
5 changed files with 1437 additions and 465 deletions

View File

@@ -38675,6 +38675,111 @@
</package>
<package name="android.bluetooth"
>
<class name="BluetoothA2dp"
extends="java.lang.Object"
abstract="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
<implements name="android.bluetooth.BluetoothProfile">
</implements>
<method name="getConnectedDevices"
return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getConnectionState"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<method name="getDevicesMatchingConnectionStates"
return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="states" type="int[]">
</parameter>
</method>
<method name="isA2dpPlaying"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<field name="ACTION_CONNECTION_STATE_CHANGED"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="ACTION_PLAYING_STATE_CHANGED"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_NOT_PLAYING"
type="int"
transient="false"
volatile="false"
value="11"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_PLAYING"
type="int"
transient="false"
volatile="false"
value="10"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class>
<class name="BluetoothAdapter"
extends="java.lang.Object"
abstract="false"
@@ -38707,6 +38812,21 @@
<parameter name="address" type="java.lang.String">
</parameter>
</method>
<method name="closeProfileProxy"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="profile" type="int">
</parameter>
<parameter name="proxy" type="android.bluetooth.BluetoothProfile">
</parameter>
</method>
<method name="disable"
return="boolean"
abstract="false"
@@ -38773,6 +38893,23 @@
visibility="public"
>
</method>
<method name="getProfileProxy"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
<parameter name="listener" type="android.bluetooth.BluetoothProfile.ServiceListener">
</parameter>
<parameter name="profile" type="int">
</parameter>
</method>
<method name="getRemoteDevice"
return="android.bluetooth.BluetoothDevice"
abstract="false"
@@ -38871,6 +39008,17 @@
visibility="public"
>
</method>
<field name="ACTION_CONNECTION_STATE_CHANGED"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="ACTION_DISCOVERY_FINISHED"
type="java.lang.String"
transient="false"
@@ -38959,6 +39107,17 @@
visibility="public"
>
</field>
<field name="EXTRA_CONNECTION_STATE"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.adapter.extra.CONNECTION_STATE&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="EXTRA_DISCOVERABLE_DURATION"
type="java.lang.String"
transient="false"
@@ -38981,6 +39140,17 @@
visibility="public"
>
</field>
<field name="EXTRA_PREVIOUS_CONNECTION_STATE"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="EXTRA_PREVIOUS_SCAN_MODE"
type="java.lang.String"
transient="false"
@@ -39058,6 +39228,50 @@
visibility="public"
>
</field>
<field name="STATE_CONNECTED"
type="int"
transient="false"
volatile="false"
value="2"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_CONNECTING"
type="int"
transient="false"
volatile="false"
value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_DISCONNECTED"
type="int"
transient="false"
volatile="false"
value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_DISCONNECTING"
type="int"
transient="false"
volatile="false"
value="3"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_OFF"
type="int"
transient="false"
@@ -40299,6 +40513,306 @@
>
</field>
</class>
<class name="BluetoothHeadset"
extends="java.lang.Object"
abstract="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
<implements name="android.bluetooth.BluetoothProfile">
</implements>
<method name="getConnectedDevices"
return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getConnectionState"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<method name="getDevicesMatchingConnectionStates"
return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="states" type="int[]">
</parameter>
</method>
<method name="isAudioConnected"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<method name="startVoiceRecognition"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<method name="stopVoiceRecognition"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<field name="ACTION_AUDIO_STATE_CHANGED"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="ACTION_CONNECTION_STATE_CHANGED"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_AUDIO_CONNECTED"
type="int"
transient="false"
volatile="false"
value="10"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_AUDIO_DISCONNECTED"
type="int"
transient="false"
volatile="false"
value="11"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class>
<interface name="BluetoothProfile"
abstract="true"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<method name="getConnectedDevices"
return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getConnectionState"
return="int"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="device" type="android.bluetooth.BluetoothDevice">
</parameter>
</method>
<method name="getDevicesMatchingConnectionStates"
return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="states" type="int[]">
</parameter>
</method>
<field name="A2DP"
type="int"
transient="false"
volatile="false"
value="2"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="EXTRA_PREVIOUS_STATE"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.profile.extra.PREVIOUS_STATE&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="EXTRA_STATE"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;android.bluetooth.profile.extra.STATE&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="HEADSET"
type="int"
transient="false"
volatile="false"
value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_CONNECTED"
type="int"
transient="false"
volatile="false"
value="2"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_CONNECTING"
type="int"
transient="false"
volatile="false"
value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_DISCONNECTED"
type="int"
transient="false"
volatile="false"
value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="STATE_DISCONNECTING"
type="int"
transient="false"
volatile="false"
value="3"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</interface>
<interface name="BluetoothProfile.ServiceListener"
abstract="true"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<method name="onServiceConnected"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="profile" type="int">
</parameter>
<parameter name="proxy" type="android.bluetooth.BluetoothProfile">
</parameter>
</method>
<method name="onServiceDisconnected"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="profile" type="int">
</parameter>
</method>
</interface>
<class name="BluetoothServerSocket"
extends="java.lang.Object"
abstract="false"

View File

@@ -18,88 +18,104 @@ package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.server.BluetoothA2dpService;
import android.content.Context;
import android.os.ServiceManager;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.server.BluetoothA2dpService;
import android.util.Log;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
/**
* Public API for controlling the Bluetooth A2DP Profile Service.
* This class provides the public APIs to control the Bluetooth A2DP
* profile.
*
* BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
* Service via IPC.
*<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothA2dp proxy object.
*
* Creating a BluetoothA2dp object will initiate a binding with the
* BluetoothHeadset service. Users of this object should call close() when they
* are finished, so that this proxy object can unbind from the service.
*
* Currently the BluetoothA2dp service runs in the system server and this
* proxy object will be immediately bound to the service on construction.
*
* Currently this class provides methods to connect to A2DP audio sinks.
*
* @hide
* <p> Android only supports one connected Bluetooth A2dp device at a time.
* Each method is protected with its appropriate permission.
*/
public final class BluetoothA2dp {
public final class BluetoothA2dp implements BluetoothProfile {
private static final String TAG = "BluetoothA2dp";
private static final boolean DBG = false;
/** int extra for ACTION_SINK_STATE_CHANGED */
public static final String EXTRA_SINK_STATE =
"android.bluetooth.a2dp.extra.SINK_STATE";
/** int extra for ACTION_SINK_STATE_CHANGED */
public static final String EXTRA_PREVIOUS_SINK_STATE =
"android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE";
/** Indicates the state of an A2DP audio sink has changed.
* This intent will always contain EXTRA_SINK_STATE,
* EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE
* extras.
/**
* Intent used to broadcast the change in connection state of the A2DP
* profile.
*
* <p>This intent will have 3 extras:
* {@link #EXTRA_STATE} - The current state of the profile.
* {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
* {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
*
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SINK_STATE_CHANGED =
"android.bluetooth.a2dp.action.SINK_STATE_CHANGED";
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
public static final int STATE_DISCONNECTED = 0;
public static final int STATE_CONNECTING = 1;
public static final int STATE_CONNECTED = 2;
public static final int STATE_DISCONNECTING = 3;
/** Playing implies connected */
public static final int STATE_PLAYING = 4;
/**
* Intent used to broadcast the change in the Playing state of the A2DP
* profile.
*
* <p>This intent will have 3 extras:
* {@link #EXTRA_STATE} - The current state of the profile.
* {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
* {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
*
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PLAYING_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
/** Default priority for a2dp devices that we try to auto-connect
* and allow incoming connections */
public static final int PRIORITY_AUTO_CONNECT = 1000;
/** Default priority for a2dp devices that should allow incoming
* connections */
public static final int PRIORITY_ON = 100;
/** Default priority for a2dp devices that should not allow incoming
* connections */
public static final int PRIORITY_OFF = 0;
/** Default priority when not set or when the device is unpaired */
public static final int PRIORITY_UNDEFINED = -1;
/**
* A2DP sink device is streaming music. This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_PLAYING_STATE_CHANGED} intent.
*/
public static final int STATE_PLAYING = 10;
private final IBluetoothA2dp mService;
private final Context mContext;
/**
* A2DP sink device is NOT streaming music. This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_PLAYING_STATE_CHANGED} intent.
*/
public static final int STATE_NOT_PLAYING = 11;
private ServiceListener mServiceListener;
private IBluetoothA2dp mService;
private BluetoothAdapter mAdapter;
/**
* Create a BluetoothA2dp proxy object for interacting with the local
* Bluetooth A2DP service.
* @param c Context
*
*/
public BluetoothA2dp(Context c) {
mContext = c;
/*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
if (b != null) {
mService = IBluetoothA2dp.Stub.asInterface(b);
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this);
}
} else {
Log.w(TAG, "Bluetooth A2DP service not available!");
@@ -109,167 +125,222 @@ public final class BluetoothA2dp {
}
}
/** Initiate a connection to an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when the
* connection is completed.
* @param device Remote BT device.
* @return false on immediate error, true otherwise
* @hide
/**
* {@inheritDoc}
* @hide
*/
public boolean connectSink(BluetoothDevice device) {
if (DBG) log("connectSink(" + device + ")");
try {
return mService.connectSink(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.connect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/** Initiate disconnect from an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when
* disconnect is completed.
* @param device Remote BT device.
* @return false on immediate error, true otherwise
* @hide
/**
* {@inheritDoc}
* @hide
*/
public boolean disconnectSink(BluetoothDevice device) {
if (DBG) log("disconnectSink(" + device + ")");
try {
return mService.disconnectSink(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/** Initiate suspend from an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when
* suspend is completed.
* @param device Remote BT device.
* @return false on immediate error, true otherwise
* @hide
/**
* {@inheritDoc}
*/
public Set<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
if (mService != null && isEnabled()) {
try {
return toDeviceSet(mService.getConnectedDevices());
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return toDeviceSet(new BluetoothDevice[0]);
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return toDeviceSet(new BluetoothDevice[0]);
}
/**
* {@inheritDoc}
*/
public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
if (mService != null && isEnabled()) {
try {
return toDeviceSet(mService.getDevicesMatchingConnectionStates(states));
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return toDeviceSet(new BluetoothDevice[0]);
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return toDeviceSet(new BluetoothDevice[0]);
}
/**
* {@inheritDoc}
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getState(" + device + ")");
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
return mService.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
/**
* {@inheritDoc}
* @hide
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
if (mService != null && isEnabled()
&& isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF &&
priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
return mService.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* {@inheritDoc}
* @hide
*/
public int getPriority(BluetoothDevice device) {
if (DBG) log("getPriority(" + device + ")");
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
return mService.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.PRIORITY_OFF;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.PRIORITY_OFF;
}
/**
* Check if A2DP profile is streaming music.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param device BluetoothDevice device
*/
public boolean isA2dpPlaying(BluetoothDevice device) {
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
return mService.isA2dpPlaying(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* Initiate suspend from an A2DP sink.
*
* <p> This API will return false in scenarios like the A2DP
* device is not in connected state etc. When this API returns,
* true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
* intent will be broadcasted with the state. Users can get the
* state of the A2DP device from this intent.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param device Remote A2DP sink
* @return false on immediate error,
* true otherwise
* @hide
*/
public boolean suspendSink(BluetoothDevice device) {
try {
return mService.suspendSink(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
return mService.suspendSink(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/** Initiate resume from an suspended A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when
* resume is completed.
* @param device Remote BT device.
* @return false on immediate error, true otherwise
* @hide
/**
* Initiate resume from a suspended A2DP sink.
*
* <p> This API will return false in scenarios like the A2DP
* device is not in suspended state etc. When this API returns,
* true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
* intent will be broadcasted with the state. Users can get the
* state of the A2DP device from this intent.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param device Remote A2DP sink
* @return false on immediate error,
* true otherwise
* @hide
*/
public boolean resumeSink(BluetoothDevice device) {
try {
return mService.resumeSink(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
return mService.resumeSink(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/** Check if a specified A2DP sink is connected.
* @param device Remote BT device.
* @return True if connected (or playing), false otherwise and on error.
* @hide
*/
public boolean isSinkConnected(BluetoothDevice device) {
if (DBG) log("isSinkConnected(" + device + ")");
int state = getSinkState(device);
return state == STATE_CONNECTED || state == STATE_PLAYING;
}
/** Check if any A2DP sink is connected.
* @return a unmodifiable set of connected A2DP sinks, or null on error.
* @hide
*/
public Set<BluetoothDevice> getConnectedSinks() {
if (DBG) log("getConnectedSinks()");
try {
return Collections.unmodifiableSet(
new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
} catch (RemoteException e) {
Log.e(TAG, "", e);
return null;
}
}
/** Check if any A2DP sink is in Non Disconnected state
* i.e playing, connected, connecting, disconnecting.
* @return a unmodifiable set of connected A2DP sinks, or null on error.
* @hide
*/
public Set<BluetoothDevice> getNonDisconnectedSinks() {
if (DBG) log("getNonDisconnectedSinks()");
try {
return Collections.unmodifiableSet(
new HashSet<BluetoothDevice>(Arrays.asList(mService.getNonDisconnectedSinks())));
} catch (RemoteException e) {
Log.e(TAG, "", e);
return null;
}
}
/** Get the state of an A2DP sink
* @param device Remote BT device.
* @return State code, one of STATE_
* @hide
*/
public int getSinkState(BluetoothDevice device) {
if (DBG) log("getSinkState(" + device + ")");
try {
return mService.getSinkState(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return BluetoothA2dp.STATE_DISCONNECTED;
}
}
/**
* Set priority of a2dp sink.
* Priority is a non-negative integer. By default paired sinks will have
* a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
* Sinks with priority greater than zero will accept incoming connections
* (if no sink is currently connected).
* Priority for unpaired sink must be PRIORITY_NONE.
* @param device Paired sink
* @param priority Integer priority, for example PRIORITY_AUTO or
* PRIORITY_NONE
* @return true if priority is set, false on error
*/
public boolean setSinkPriority(BluetoothDevice device, int priority) {
if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
try {
return mService.setSinkPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
/**
* Get priority of a2dp sink.
* @param device Sink
* @return non-negative priority, or negative error code on error.
*/
public int getSinkPriority(BluetoothDevice device) {
if (DBG) log("getSinkPriority(" + device + ")");
try {
return mService.getSinkPriority(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return PRIORITY_OFF;
}
}
/** Helper for converting a state to a string.
/**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
* @hide
*/
@@ -285,12 +356,31 @@ public final class BluetoothA2dp {
return "disconnecting";
case STATE_PLAYING:
return "playing";
case STATE_NOT_PLAYING:
return "not playing";
default:
return "<unknown state " + state + ">";
}
}
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
}
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
return false;
}
private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
return Collections.unmodifiableSet(
new HashSet<BluetoothDevice>(Arrays.asList(devices)));
}
private static void log(String msg) {
Log.d(TAG, msg);
Log.d(TAG, msg);
}
}

View File

@@ -18,6 +18,7 @@ package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -279,6 +280,61 @@ public final class BluetoothAdapter {
*/
public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
/**
* Intent used to broadcast the change in connection state of the local
* Bluetooth adapter to a profile of the remote device. When the adapter is
* not connected to any profiles of any remote devices and it attempts a
* connection to a profile this intent will sent. Once connected, this intent
* will not be sent for any more connection attempts to any profiles of any
* remote device. When the adapter disconnects from the last profile its
* connected to of any remote device, this intent will be sent.
*
* <p> This intent is useful for applications that are only concerned about
* whether the local adapter is connected to any profile of any device and
* are not really concerned about which profile. For example, an application
* which displays an icon to display whether Bluetooth is connected or not
* can use this intent.
*
* <p>This intent will have 3 extras:
* {@link #EXTRA_STATE} - The current state.
* {@link #EXTRA_PREVIOUS_STATE}- The previous.
* {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
*
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
/**
* Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
*
* This extra represents the current connection state.
*/
public static final String EXTRA_CONNECTION_STATE =
"android.bluetooth.adapter.extra.CONNECTION_STATE";
/**
* Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
*
* This extra represents the previous connection state.
*/
public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
"android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
/** The profile is in disconnected state */
public static final int STATE_DISCONNECTED = 0;
/** The profile is in connecting state */
public static final int STATE_CONNECTING = 1;
/** The profile is in connected state */
public static final int STATE_CONNECTED = 2;
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
/** @hide */
public static final String BLUETOOTH_SERVICE = "bluetooth";
@@ -896,6 +952,54 @@ public final class BluetoothAdapter {
return null;
}
/*
* Get the profile proxy object associated with the profile.
*
* <p>Profile can be one of {@link BluetoothProfile.HEADSET} or
* {@link BluetoothProfile.A2DP}. Clients must implements
* {@link BluetoothProfile.ServiceListener} to get notified of
* the connection status and to get the proxy object.
*
* @param context Context of the application
* @param listener The service Listener for connection callbacks.
* @param profile
* @return true on success, false on error
*/
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile) {
if (context == null || listener == null) return false;
if (profile == BluetoothProfile.HEADSET) {
BluetoothHeadset headset = new BluetoothHeadset(context, listener);
return true;
} else if (profile == BluetoothProfile.A2DP) {
BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
return true;
} else {
return false;
}
}
/**
* Close the connection of the profile proxy to the Service.
*
* <p> Clients should call this when they are no longer using
* the proxy obtained from {@link #getProfileProxy}.
* Profile can be one of {@link BluetoothProfile#HEADSET} or
* {@link BluetoothProfile#A2DP}
*
* @param profile
* @param proxy Profile proxy object
*/
public void closeProfileProxy(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
BluetoothHeadset headset = (BluetoothHeadset)proxy;
if (headset != null) {
headset.close();
}
}
}
private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
for (int i = 0; i < addresses.length; i++) {

View File

@@ -18,6 +18,8 @@ package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,63 +28,66 @@ import android.os.RemoteException;
import android.os.IBinder;
import android.util.Log;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* The Android Bluetooth API is not finalized, and *will* change. Use at your
* own risk.
*
* Public API for controlling the Bluetooth Headset Service. This includes both
* Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will
* attempt a handsfree connection first, and fall back to headset.
* Bluetooth Headset and Handsfree (v1.5) profiles.
*
* BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
* <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
* Service via IPC.
*
* Creating a BluetoothHeadset object will create a binding with the
* BluetoothHeadset service. Users of this object should call close() when they
* are finished with the BluetoothHeadset, so that this proxy object can unbind
* from the service.
* <p> Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothHeadset proxy object. Use
* {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
*
* This BluetoothHeadset object is not immediately bound to the
* BluetoothHeadset service. Use the ServiceListener interface to obtain a
* notification when it is bound, this is especially important if you wish to
* immediately call methods on BluetoothHeadset after construction.
*
* Android only supports one connected Bluetooth Headset at a time.
*
* @hide
* <p> Android only supports one connected Bluetooth Headset at a time.
* Each method is protected with its appropriate permission.
*/
public final class BluetoothHeadset {
public final class BluetoothHeadset implements BluetoothProfile {
private static final String TAG = "BluetoothHeadset";
private static final boolean DBG = false;
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_STATE_CHANGED =
"android.bluetooth.headset.action.STATE_CHANGED";
/**
* TODO(API release): Consider incorporating as new state in
* HEADSET_STATE_CHANGED
* Intent used to broadcast the change in connection state of the Headset
* profile.
*
* <p>This intent will have 3 extras:
* {@link #EXTRA_STATE} - The current state of the profile.
* {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
* {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
*
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
/**
* Intent used to broadcast the change in the Audio Connection state of the
* A2DP profile.
*
* <p>This intent will have 3 extras:
* {@link #EXTRA_STATE} - The current state of the profile.
* {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
* {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
*
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headset.action.AUDIO_STATE_CHANGED";
public static final String EXTRA_STATE =
"android.bluetooth.headset.extra.STATE";
public static final String EXTRA_PREVIOUS_STATE =
"android.bluetooth.headset.extra.PREVIOUS_STATE";
public static final String EXTRA_AUDIO_STATE =
"android.bluetooth.headset.extra.AUDIO_STATE";
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
/** Extra to be used with the Headset State change intent.
* This will be used only when Headset state changes to
* {@link #STATE_DISCONNECTED} from any previous state.
* This extra field is optional and will be used when
* we have deterministic information regarding whether
* the disconnect was initiated by the remote device or
* by the local adapter.
*/
public static final String EXTRA_DISCONNECT_INITIATOR =
"android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
/**
* Broadcast Action: Indicates a headset has posted a vendor-specific event.
@@ -124,100 +129,47 @@ public final class BluetoothHeadset {
public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
"android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
/*
* Headset state when SCO audio is connected
* This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_AUDIO_STATE_CHANGED} intent.
*/
public static final int STATE_AUDIO_CONNECTED = 10;
/**
* TODO(API release): Consider incorporating as new state in
* HEADSET_STATE_CHANGED
* Headset state when SCO audio is NOT connected
* This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_AUDIO_STATE_CHANGED} intent.
*/
public static final int STATE_AUDIO_DISCONNECTED = 11;
private Context mContext;
private ServiceListener mServiceListener;
private IBluetoothHeadset mService;
private final Context mContext;
private final ServiceListener mServiceListener;
/** There was an error trying to obtain the state */
public static final int STATE_ERROR = -1;
/** No headset currently connected */
public static final int STATE_DISCONNECTED = 0;
/** Connection attempt in progress */
public static final int STATE_CONNECTING = 1;
/** A headset is currently connected */
public static final int STATE_CONNECTED = 2;
/** A SCO audio channel is not established */
public static final int AUDIO_STATE_DISCONNECTED = 0;
/** A SCO audio channel is established */
public static final int AUDIO_STATE_CONNECTED = 1;
public static final int RESULT_FAILURE = 0;
public static final int RESULT_SUCCESS = 1;
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
/** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */
public static final int REMOTE_DISCONNECT = 0;
public static final int LOCAL_DISCONNECT = 1;
/** Default priority for headsets that for which we will accept
* inconing connections and auto-connect */
public static final int PRIORITY_AUTO_CONNECT = 1000;
/** Default priority for headsets that for which we will accept
* inconing connections but not auto-connect */
public static final int PRIORITY_ON = 100;
/** Default priority for headsets that should not be auto-connected
* and not allow incoming connections. */
public static final int PRIORITY_OFF = 0;
/** Default priority when not set or when the device is unpaired */
public static final int PRIORITY_UNDEFINED = -1;
/**
* An interface for notifying BluetoothHeadset IPC clients when they have
* been connected to the BluetoothHeadset service.
*/
public interface ServiceListener {
/**
* Called to notify the client when this proxy object has been
* connected to the BluetoothHeadset service. Clients must wait for
* this callback before making IPC calls on the BluetoothHeadset
* service.
*/
public void onServiceConnected();
/**
* Called to notify the client that this proxy object has been
* disconnected from the BluetoothHeadset service. Clients must not
* make IPC calls on the BluetoothHeadset service after this callback.
* This callback will currently only occur if the application hosting
* the BluetoothHeadset service, but may be called more often in future.
*/
public void onServiceDisconnected();
}
BluetoothAdapter mAdapter;
/**
* Create a BluetoothHeadset proxy object.
*/
public BluetoothHeadset(Context context, ServiceListener l) {
/*package*/ BluetoothHeadset(Context context, ServiceListener l) {
mContext = context;
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth Headset Service");
}
}
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Close the connection to the backing service.
* Other public functions of BluetoothHeadset will return default error
* results once close() has been called. Multiple invocations of close()
* are ok.
*/
public synchronized void close() {
/*package*/ synchronized void close() {
if (DBG) log("close()");
if (mConnection != null) {
mContext.unbindService(mConnection);
@@ -226,190 +178,212 @@ public final class BluetoothHeadset {
}
/**
* Get the current state of the Bluetooth Headset service.
* @return One of the STATE_ return codes, or STATE_ERROR if this proxy
* object is currently not connected to the Headset service.
* {@inheritDoc}
* @hide
*/
public int getState(BluetoothDevice device) {
if (DBG) log("getState()");
if (mService != null) {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.getState(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
return BluetoothHeadset.STATE_ERROR;
}
/**
* Get the BluetoothDevice for the current headset.
* @return current headset, or null if not in connected or connecting
* state, or if this proxy object is not connected to the Headset
* service.
*/
public BluetoothDevice getCurrentHeadset() {
if (DBG) log("getCurrentHeadset()");
if (mService != null) {
try {
return mService.getCurrentHeadset();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
return null;
}
/**
* Request to initiate a connection to a headset.
* This call does not block. Fails if a headset is already connecting
* or connected.
* Initiates auto-connection if device is null. Tries to connect to all
* devices with priority greater than PRIORITY_AUTO in descending order.
* @param device device to connect to, or null to auto-connect last connected
* headset
* @return false if there was a problem initiating the connection
* procedure, and no further HEADSET_STATE_CHANGED intents
* will be expected.
*/
public boolean connectHeadset(BluetoothDevice device) {
if (DBG) log("connectHeadset(" + device + ")");
if (mService != null) {
try {
if (mService.connectHeadset(device)) {
return true;
}
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
return mService.connect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* Returns true if the specified headset is connected (does not include
* connecting). Returns false if not connected, or if this proxy object
* if not currently connected to the headset service.
* {@inheritDoc}
* @hide
*/
public boolean isConnected(BluetoothDevice device) {
if (DBG) log("isConnected(" + device + ")");
if (mService != null) {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.isConnected(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
return mService.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* Disconnects the current headset. Currently this call blocks, it may soon
* be made asynchornous. Returns false if this proxy object is
* not currently connected to the Headset service.
* {@inheritDoc}
*/
public boolean disconnectHeadset(BluetoothDevice device) {
if (DBG) log("disconnectHeadset()");
if (mService != null) {
public Set<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
if (mService != null && isEnabled()) {
try {
mService.disconnectHeadset(device);
return true;
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
return toDeviceSet(mService.getConnectedDevices());
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return toDeviceSet(new BluetoothDevice[0]);
}
}
return false;
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return toDeviceSet(new BluetoothDevice[0]);
}
/**
* Start BT Voice Recognition mode, and set up Bluetooth audio path.
* Returns false if there is no headset connected, or if the
* connected headset does not support voice recognition, or on
* error.
* {@inheritDoc}
*/
public boolean startVoiceRecognition() {
if (DBG) log("startVoiceRecognition()");
if (mService != null) {
public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
if (mService != null && isEnabled()) {
try {
return mService.startVoiceRecognition();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
return toDeviceSet(mService.getDevicesMatchingConnectionStates(states));
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return toDeviceSet(new BluetoothDevice[0]);
}
}
return false;
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return toDeviceSet(new BluetoothDevice[0]);
}
/**
* Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
* Returns false if there is no headset connected, or the connected
* headset is not in voice recognition mode, or on error.
* {@inheritDoc}
*/
public boolean stopVoiceRecognition() {
if (DBG) log("stopVoiceRecognition()");
if (mService != null) {
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.stopVoiceRecognition();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
return mService.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
return false;
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
/**
* Set priority of headset.
* Priority is a non-negative integer. By default paired headsets will have
* a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
* Headsets with priority greater than zero will be auto-connected, and
* incoming connections will be accepted (if no other headset is
* connected).
* Auto-connection occurs at the following events: boot, incoming phone
* call, outgoing phone call.
* Headsets with priority equal to zero, or that are unpaired, are not
* auto-connected.
* Incoming connections are ignored regardless of priority if there is
* already a headset connected.
* @param device paired headset
* @param priority Integer priority, for example PRIORITY_AUTO or
* PRIORITY_NONE
* @return true if successful, false if there was some error
* {@inheritDoc}
* @hide
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
if (mService != null) {
if (mService != null && isEnabled() &&
isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF &&
priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
return mService.setPriority(device, priority);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* Get priority of headset.
* @param device headset
* @return non-negative priority, or negative error code on error
* {@inheritDoc}
* @hide
*/
public int getPriority(BluetoothDevice device) {
if (DBG) log("getPriority(" + device + ")");
if (mService != null) {
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.getPriority(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
return -1;
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return PRIORITY_OFF;
}
/**
* Start Bluetooth voice recognition. This methods sends the voice
* recognition AT command to the headset and establishes the
* audio connection.
*
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
* {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED}
* when the audio connection is established.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param device Bluetooth headset
* @return false if there is no headset connected of if the
* connected headset doesn't support voice recognition
* or on error, true otherwise
*/
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.startVoiceRecognition(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* Stop Bluetooth Voice Recognition mode, and shut down the
* Bluetooth audio path.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param device Bluetooth headset
* @return false if there is no headset connected
* or on error, true otherwise
*/
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.stopVoiceRecognition(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* Check if Bluetooth SCO audio is connected.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param device Bluetooth headset
* @return true if SCO is connected,
* false otherwise or on error
*/
public boolean isAudioConnected(BluetoothDevice device) {
if (DBG) log("isAudioConnected()");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.isAudioConnected(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
@@ -420,24 +394,29 @@ public final class BluetoothHeadset {
* boot. This is a good indicator for spammy headset/handsfree units that
* can keep the device awake by polling for cellular status updates. As a
* rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
*
* @param device the bluetooth headset.
* @return monotonically increasing battery usage hint, or a negative error
* code on error
* @hide
*/
public int getBatteryUsageHint() {
public int getBatteryUsageHint(BluetoothDevice device) {
if (DBG) log("getBatteryUsageHint()");
if (mService != null) {
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mService.getBatteryUsageHint();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
return mService.getBatteryUsageHint(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return -1;
}
/**
* Indicates if current platform supports voice dialing over bluetooth SCO.
*
* @return true if voice dialing over bluetooth is supported, false otherwise.
* @hide
*/
@@ -448,11 +427,13 @@ public final class BluetoothHeadset {
/**
* Cancel the outgoing connection.
* Note: This is an internal function and shouldn't be exposed
*
* @hide
*/
public boolean cancelConnectThread() {
if (DBG) log("cancelConnectThread");
if (mService != null) {
if (mService != null && isEnabled()) {
try {
return mService.cancelConnectThread();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -465,11 +446,13 @@ public final class BluetoothHeadset {
/**
* Accept the incoming connection.
* Note: This is an internal function and shouldn't be exposed
*
* @hide
*/
public boolean acceptIncomingConnect(BluetoothDevice device) {
if (DBG) log("acceptIncomingConnect");
if (mService != null) {
if (mService != null && isEnabled()) {
try {
return mService.acceptIncomingConnect(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -481,12 +464,14 @@ public final class BluetoothHeadset {
}
/**
* Create the connect thread the incoming connection.
* Create the connect thread for the incoming connection.
* Note: This is an internal function and shouldn't be exposed
*
* @hide
*/
public boolean createIncomingConnect(BluetoothDevice device) {
if (DBG) log("createIncomingConnect");
if (mService != null) {
if (mService != null && isEnabled()) {
try {
return mService.createIncomingConnect(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -500,11 +485,12 @@ public final class BluetoothHeadset {
/**
* Connect to a Bluetooth Headset.
* Note: This is an internal function and shouldn't be exposed
*
* @hide
*/
public boolean connectHeadsetInternal(BluetoothDevice device) {
if (DBG) log("connectHeadsetInternal");
if (mService != null) {
if (mService != null && isEnabled()) {
try {
return mService.connectHeadsetInternal(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -518,11 +504,12 @@ public final class BluetoothHeadset {
/**
* Disconnect a Bluetooth Headset.
* Note: This is an internal function and shouldn't be exposed
*
* @hide
*/
public boolean disconnectHeadsetInternal(BluetoothDevice device) {
if (DBG) log("disconnectHeadsetInternal");
if (mService != null) {
if (mService != null && isEnabled()) {
try {
return mService.disconnectHeadsetInternal(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -532,23 +519,61 @@ public final class BluetoothHeadset {
}
return false;
}
/**
* Set the audio state of the Headset.
* Note: This is an internal function and shouldn't be exposed
*
* @hide
*/
public boolean setAudioState(BluetoothDevice device, int state) {
if (DBG) log("setAudioState");
if (mService != null && isEnabled()) {
try {
return mService.setAudioState(device, state);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
return false;
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothHeadset.Stub.asInterface(service);
if (mServiceListener != null) {
mServiceListener.onServiceConnected();
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
}
}
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "Proxy object disconnected");
mService = null;
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected();
mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
}
}
};
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
}
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
return false;
}
private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
return Collections.unmodifiableSet(
new HashSet<BluetoothDevice>(Arrays.asList(devices)));
}
private static void log(String msg) {
Log.d(TAG, msg);
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import java.util.Set;
/**
* Public APIs for the Bluetooth Profiles.
*
* <p> Clients should call {@link BluetoothAdapter#getProfileProxy},
* to get the Profile Proxy. Each public profile implements this
* interface.
*/
public interface BluetoothProfile {
/**
* Extra for the connection state intents of the individual profiles.
*
* This extra represents the current connection state of the profile of the
* Bluetooth device.
*/
public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
/**
* Extra for the connection state intents of the individual profiles.
*
* This extra represents the previous connection state of the profile of the
* Bluetooth device.
*/
public static final String EXTRA_PREVIOUS_STATE =
"android.bluetooth.profile.extra.PREVIOUS_STATE";
/** The profile is in disconnected state */
public static final int STATE_DISCONNECTED = 0;
/** The profile is in connecting state */
public static final int STATE_CONNECTING = 1;
/** The profile is in connected state */
public static final int STATE_CONNECTED = 2;
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
/**
* Headset and Handsfree profile
*/
public static final int HEADSET = 1;
/**
* A2DP profile.
*/
public static final int A2DP = 2;
/**
* Default priority for devices that we try to auto-connect to and
* and allow incoming connections for the profile
* @hide
**/
public static final int PRIORITY_AUTO_CONNECT = 1000;
/**
* Default priority for devices that allow incoming
* and outgoing connections for the profile
* @hide
**/
public static final int PRIORITY_ON = 100;
/**
* Default priority for devices that does not allow incoming
* connections and outgoing connections for the profile.
* @hide
**/
public static final int PRIORITY_OFF = 0;
/**
* Default priority when not set or when the device is unpaired
* @hide
* */
public static final int PRIORITY_UNDEFINED = -1;
/**
* Initiate connection to a profile of the remote bluetooth device.
*
* <p> Currently, the system supports only 1 connection to the
* A2DP and Headset/Handsfree profile. The API will automatically
* disconnect connected devices before connecting.
*
* <p> This API returns false in scenarios like the profile on the
* device is already connected or Bluetooth is not turned on.
* When this API returns true, it is guaranteed that
* connection state intent for the profile will be broadcasted with
* the state. Users can get the connection state of the profile
* from this intent.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param device Remote Bluetooth Device
* @return false on immediate error,
* true otherwise
* @hide
*/
public boolean connect(BluetoothDevice device);
/**
* Initiate disconnection from a profile
*
* <p> This API will return false in scenarios like the profile on the
* Bluetooth device is not in connected state etc. When this API returns,
* true, it is guaranteed that the connection state change
* intent will be broadcasted with the state. Users can get the
* disconnection state of the profile from this intent.
*
* <p> If the disconnection is initiated by a remote device, the state
* will transition from {@link #STATE_CONNECTED} to
* {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
* host (local) device the state will transition from
* {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
* state {@link #STATE_DISCONNECTED}. The transition to
* {@link #STATE_DISCONNECTING} can be used to distinguish between the
* two scenarios.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param device Remote Bluetooth Device
* @return false on immediate error,
* true otherwise
* @hide
*/
public boolean disconnect(BluetoothDevice device);
/**
* Get connected devices for this specific profile.
*
* <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return An unmodifiable set of devices. The set will be empty on error.
*/
public Set<BluetoothDevice> getConnectedDevices();
/**
* Get a set of devices that match any of the given connection
* states.
*
* <p> If none of devices match any of the given states,
* an empty set will be returned.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param states Array of states. States can be one of
* {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
* @return An unmodifiable set of devices. The set will be empty on error.
*/
public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
/**
* Get the current connection state of the profile
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param device Remote bluetooth device.
* @return State of the profile connection. One of
* {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
public int getConnectionState(BluetoothDevice device);
/**
* Set priority of the profile
*
* <p> The device should already be paired.
* Priority can be one of {@link #PRIORITY_ON} or
* {@link #PRIORITY_OFF},
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param device Paired bluetooth device
* @param priority
* @return true if priority is set, false on error
* @hide
*/
public boolean setPriority(BluetoothDevice device, int priority);
/**
* Get the priority of the profile.
*
* <p> The priority can be any of:
* {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
* {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param device Bluetooth device
* @return priority of the device
* @hide
*/
public int getPriority(BluetoothDevice device);
/**
* An interface for notifying BluetoothProfile IPC clients when they have
* been connected or disconnected to the service.
*/
public interface ServiceListener {
/**
* Called to notify the client when the proxy object has been
* connected to the service.
* @param profile - One of {@link #HEADSET} or
* {@link #A2DP}
* @param proxy - One of {@link BluetoothHeadset} or
* {@link BluetoothA2dp}
*/
public void onServiceConnected(int profile, BluetoothProfile proxy);
/**
* Called to notify the client that this proxy object has been
* disconnected from the service.
* @param profile - One of {@link #HEADSET} or
* {@link #A2DP}
*/
public void onServiceDisconnected(int profile);
}
}