Framework changes for HFP and A2DP profile implementation of the new stack.
Add IBluetoothHeadsetPhone.aidl for a small service in Phone to get
phone state changes
Change-Id: I1015e4a69720c4e9cd18ae4236ccd0dbff2e1b2c
diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java
index c8e60f1..c59a5aa 100644
--- a/framework/java/android/bluetooth/BluetoothA2dp.java
+++ b/framework/java/android/bluetooth/BluetoothA2dp.java
@@ -18,11 +18,13 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import java.util.ArrayList;
@@ -42,7 +44,7 @@
*/
public final class BluetoothA2dp implements BluetoothProfile {
private static final String TAG = "BluetoothA2dp";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
/**
* Intent used to broadcast the change in connection state of the A2DP
@@ -101,6 +103,7 @@
*/
public static final int STATE_NOT_PLAYING = 11;
+ private Context mContext;
private ServiceListener mServiceListener;
private IBluetoothA2dp mService;
private BluetoothAdapter mAdapter;
@@ -110,22 +113,12 @@
* Bluetooth A2DP service.
*
*/
- /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
- //TODO(BT): Fix this
- IBinder b = null;
+ /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
+ mContext = context;
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!");
-
- // Instead of throwing an exception which prevents people from going
- // into Wireless settings in the emulator. Let it crash later when it is actually used.
- mService = null;
+ if (!context.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth A2DP Service");
}
}
@@ -347,67 +340,6 @@
}
/**
- * 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_CONNECTION_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}
- * permission.
- *
- * @param device Remote A2DP sink
- * @return false on immediate error,
- * true otherwise
- * @hide
- */
- public boolean suspendSink(BluetoothDevice device) {
- 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 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) {
- 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;
- }
-
- /**
* This function checks if the remote device is an AVCRP
* target and thus whether we should send volume keys
* changes or not.
@@ -428,23 +360,6 @@
}
/**
- * Allow or disallow incoming connection
- * @param device Sink
- * @param value True / False
- * @return Success or Failure of the binder call.
- * @hide
- */
- public boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
- if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")");
- try {
- return mService.allowIncomingConnect(device, value);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return false;
- }
- }
-
- /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
@@ -469,6 +384,24 @@
}
}
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ mService = IBluetoothA2dp.Stub.asInterface(service);
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ mService = null;
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
+ }
+ }
+ };
+
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java
index 2bbf008..adf07b4 100644
--- a/framework/java/android/bluetooth/BluetoothHeadset.java
+++ b/framework/java/android/bluetooth/BluetoothHeadset.java
@@ -562,25 +562,6 @@
}
/**
- * 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 && isEnabled()) {
- try {
- return mService.cancelConnectThread();
- } 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;
- }
-
- /**
* Accept the incoming connection.
* Note: This is an internal function and shouldn't be exposed
*
@@ -600,25 +581,6 @@
}
/**
- * 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 && isEnabled()) {
- try {
- return mService.createIncomingConnect(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 false;
- }
-
- /**
* Reject the incoming connection.
* @hide
*/
@@ -636,63 +598,6 @@
}
/**
- * 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 && isEnabled()) {
- try {
- return mService.connectHeadsetInternal(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 false;
- }
-
- /**
- * 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 && !isDisabled()) {
- try {
- return mService.disconnectHeadsetInternal(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 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 && !isDisabled()) {
- 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;
- }
-
- /**
* Get the current audio state of the Headset.
* Note: This is an internal function and shouldn't be exposed
*
@@ -712,6 +617,75 @@
}
/**
+ * Check if Bluetooth SCO audio is connected.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return true if SCO is connected,
+ * false otherwise or on error
+ * @hide
+ */
+ public boolean isAudioOn() {
+ if (DBG) log("isAudioOn()");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.isAudioOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+
+ }
+
+ /**
+ * Initiates a connection of headset audio.
+ * It setup SCO channel with remote connected headset device.
+ *
+ * @return true if successful
+ * false if there was some error such as
+ * there is no connected headset
+ * @hide
+ */
+ public boolean connectAudio() {
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.connectAudio();
+ } 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;
+ }
+
+ /**
+ * Initiates a disconnection of headset audio.
+ * It tears down the SCO channel from remote headset device.
+ *
+ * @return true if successful
+ * false if there was some error such as
+ * there is no connected SCO channel
+ * @hide
+ */
+ public boolean disconnectAudio() {
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.disconnectAudio();
+ } 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;
+ }
+
+ /**
* Initiates a SCO channel connection with the headset (if connected).
* Also initiates a virtual voice call for Handsfree devices as many devices
* do not accept SCO audio without a call.
@@ -760,6 +734,68 @@
return false;
}
+ /**
+ * Notify Headset of phone state change.
+ * This is a backdoor for phone app to call BluetoothHeadset since
+ * there is currently not a good way to get precise call state change outside
+ * of phone app.
+ *
+ * @hide
+ */
+ public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
+ int type) {
+ if (mService != null && isEnabled()) {
+ try {
+ mService.phoneStateChanged(numActive, numHeld, callState, number, type);
+ } 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()));
+ }
+ }
+
+ /**
+ * Notify Headset of phone roam state change.
+ * This is a backdoor for phone app to call BluetoothHeadset since
+ * there is currently not a good way to get roaming state change outside
+ * of phone app.
+ *
+ * @hide
+ */
+ public void roamChanged(boolean roaming) {
+ if (mService != null && isEnabled()) {
+ try {
+ mService.roamChanged(roaming);
+ } 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()));
+ }
+ }
+
+ /**
+ * Send Headset of CLCC response
+ *
+ * @hide
+ */
+ public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
+ String number, int type) {
+ if (mService != null && isEnabled()) {
+ try {
+ mService.clccResponse(index, direction, status, mode, mpty, number, type);
+ } 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()));
+ }
+ }
+
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl
index 444dd1e..1f10998 100644
--- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -33,12 +33,4 @@
boolean setPriority(in BluetoothDevice device, int priority);
int getPriority(in BluetoothDevice device);
boolean isA2dpPlaying(in BluetoothDevice device);
-
- // Internal APIs
- boolean suspendSink(in BluetoothDevice device);
- boolean resumeSink(in BluetoothDevice device);
- boolean connectSinkInternal(in BluetoothDevice device);
- boolean disconnectSinkInternal(in BluetoothDevice device);
- boolean allowIncomingConnect(in BluetoothDevice device, boolean value);
-
}
diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl
index ec00527..fc7627a 100644
--- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -40,15 +40,17 @@
int getBatteryUsageHint(in BluetoothDevice device);
// Internal functions, not be made public
- boolean createIncomingConnect(in BluetoothDevice device);
boolean acceptIncomingConnect(in BluetoothDevice device);
boolean rejectIncomingConnect(in BluetoothDevice device);
- boolean cancelConnectThread();
- boolean connectHeadsetInternal(in BluetoothDevice device);
- boolean disconnectHeadsetInternal(in BluetoothDevice device);
- boolean setAudioState(in BluetoothDevice device, int state);
int getAudioState(in BluetoothDevice device);
+ boolean isAudioOn();
+ boolean connectAudio();
+ boolean disconnectAudio();
boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device);
boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device);
+ void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type);
+ void roamChanged(boolean roam);
+ void clccResponse(int index, int direction, int status, int mode, boolean mpty,
+ String number, int type);
}
diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
new file mode 100644
index 0000000..ab9dafe
--- /dev/null
+++ b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ */
+
+package android.bluetooth;
+
+/**
+ * API for Bluetooth Headset Phone Service in phone app
+ *
+ * {@hide}
+ */
+interface IBluetoothHeadsetPhone {
+ // Internal functions, not be made public
+ boolean answerCall();
+ boolean hangupCall();
+ boolean sendDtmf(int dtmf);
+ boolean processChld(int chld);
+ String getNetworkOperator();
+ String getSubscriberNumber();
+ boolean listCurrentCalls();
+
+ // Internal for phone app to call
+ void updateBtHandsfreeAfterRadioTechnologyChange();
+ void cdmaSwapSecondCallState();
+ void cdmaSetSecondCallState(boolean state);
+}