namespace android {
static jmethodID method_onConnectionStateChanged;
+static jmethodID method_onAudioStateChanged;
static const btav_interface_t *sBluetoothA2dpInterface = NULL;
static jobject mCallbacksObj = NULL;
sCallbackEnv->DeleteLocalRef(addr);
}
+static void bta2dp_audio_state_callback(btav_audio_state_t state, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+
+ LOGI("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) { \
+ LOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
+ return; \
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ LOGE("Fail to new jbyteArray bd addr for connection state");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state,
+ addr);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
static btav_callbacks_t sBluetoothA2dpCallbacks = {
sizeof(sBluetoothA2dpCallbacks),
bta2dp_connection_state_callback,
+ bta2dp_audio_state_callback
};
static void classInitNative(JNIEnv* env, jclass clazz) {
method_onConnectionStateChanged =
env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
+ method_onAudioStateChanged =
+ env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
/*
if ( (btInf = getBluetoothInterface()) == NULL) {
LOGE("Bluetooth module is not loaded");
private BluetoothDevice mCurrentDevice = null;
private BluetoothDevice mTargetDevice = null;
private BluetoothDevice mIncomingDevice = null;
+ private BluetoothDevice mPlayingA2dpDevice = null;
static {
classInitNative();
@Override
public void enter() {
log("Enter Connected: " + getCurrentMessage().what);
+ // Upon connected, the audio starts out as stopped
+ broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
}
@Override
case EVENT_TYPE_CONNECTION_STATE_CHANGED:
processConnectionEvent(event.valueInt, event.device);
break;
+ case EVENT_TYPE_AUDIO_STATE_CHANGED:
+ processAudioStateEvent(event.valueInt, event.device);
+ break;
default:
Log.e(TAG, "Unexpected stack event: " + event.type);
break;
break;
}
}
+ private void processAudioStateEvent(int state, BluetoothDevice device) {
+ if (!mCurrentDevice.equals(device)) {
+ Log.e(TAG, "Audio State Device:" + device + "is different from ConnectedDevice:" +
+ mCurrentDevice);
+ return;
+ }
+ switch (state) {
+ case AUDIO_STATE_STARTED:
+ if (mPlayingA2dpDevice == null) {
+ mPlayingA2dpDevice = device;
+ broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
+ BluetoothA2dp.STATE_NOT_PLAYING);
+ }
+ break;
+ case AUDIO_STATE_STOPPED:
+ if(mPlayingA2dpDevice != null) {
+ mPlayingA2dpDevice = null;
+ broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ }
+ break;
+ default:
+ Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
+ break;
+ }
+ }
}
int getConnectionState(BluetoothDevice device) {
boolean isPlaying(BluetoothDevice device) {
synchronized(this) {
- if (getCurrentState() == mConnected && mCurrentDevice.equals(device)) {
+ if (device.equals(mPlayingA2dpDevice)) {
return true;
}
}
}
}
+ private void broadcastAudioState(BluetoothDevice device, int state, int prevState) {
+ Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
+
+ if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
+ }
+
private byte[] getByteAddress(BluetoothDevice device) {
return Utils.getBytesFromAddress(device.getAddress());
}
sendMessage(STACK_EVENT, event);
}
+ private void onAudioStateChanged(int state, byte[] address) {
+ StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
+ event.valueInt = state;
+ event.device = getDevice(address);
+ sendMessage(STACK_EVENT, event);
+ }
private BluetoothDevice getDevice(byte[] address) {
return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
}
// Event types for STACK_EVENT message
final private static int EVENT_TYPE_NONE = 0;
final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
+ final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
// Do not modify without upating the HAL bt_av.h files.
final static int CONNECTION_STATE_CONNECTED = 2;
final static int CONNECTION_STATE_DISCONNECTING = 3;
+ // match up with btav_audio_state_t enum of bt_av.h
+ final static int AUDIO_STATE_REMOTE_SUSPEND = 0;
+ final static int AUDIO_STATE_STOPPED = 1;
+ final static int AUDIO_STATE_STARTED = 2;
+
private native static void classInitNative();
private native void initNative();
private native void cleanupNative();