Loading android/app/jni/com_android_bluetooth_avrcp.cpp +72 −2 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,8 @@ static jmethodID method_getElementAttr; static jmethodID method_registerNotification; static jmethodID method_registerNotification; static jmethodID method_volumeChangeCallback; static jmethodID method_volumeChangeCallback; static jmethodID method_handlePassthroughCmd; static jmethodID method_handlePassthroughCmd; static jmethodID method_handlePassthroughRsp; static jmethodID method_onConnectionStateChanged; static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; static jobject mCallbacksObj = NULL; static jobject mCallbacksObj = NULL; Loading Loading @@ -142,6 +144,46 @@ static void btavrcp_passthrough_command_callback(int id, int pressed) { checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); } } static void btavrcp_passthrough_response_callback(int id, int pressed) { ALOGI("%s", __FUNCTION__); ALOGI("id: %d, pressed: %d", id, pressed); if (!checkCallbackThread()) { ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughRsp, (jint)id, (jint)pressed); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); } static void btavrcp_connection_state_callback(int state, bt_bdaddr_t* bd_addr) { jbyteArray addr; ALOGI("%s", __FUNCTION__); ALOGI("conn state: %d", state); if (!checkCallbackThread()) { \ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ return; \ } addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); if (!addr) { ALOGE("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_onConnectionStateChanged, (jint) state, addr); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); sCallbackEnv->DeleteLocalRef(addr); } static btrc_callbacks_t sBluetoothAvrcpCallbacks = { static btrc_callbacks_t sBluetoothAvrcpCallbacks = { sizeof(sBluetoothAvrcpCallbacks), sizeof(sBluetoothAvrcpCallbacks), btavrcp_remote_features_callback, btavrcp_remote_features_callback, Loading @@ -155,7 +197,9 @@ static btrc_callbacks_t sBluetoothAvrcpCallbacks = { btavrcp_get_element_attr_callback, btavrcp_get_element_attr_callback, btavrcp_register_notification_callback, btavrcp_register_notification_callback, btavrcp_volume_change_callback, btavrcp_volume_change_callback, btavrcp_passthrough_command_callback btavrcp_passthrough_command_callback, btavrcp_passthrough_response_callback, btavrcp_connection_state_callback }; }; static void classInitNative(JNIEnv* env, jclass clazz) { static void classInitNative(JNIEnv* env, jclass clazz) { Loading @@ -176,6 +220,12 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_handlePassthroughCmd = method_handlePassthroughCmd = env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V"); env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V"); method_handlePassthroughRsp = env->GetMethodID(clazz, "handlePassthroughRsp", "(II)V"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); ALOGI("%s: succeeds", __FUNCTION__); ALOGI("%s: succeeds", __FUNCTION__); } } Loading Loading @@ -395,6 +445,24 @@ static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) { return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } } static jboolean sendPassThroughCommandNative(JNIEnv *env, jobject object, jint key_code, jint key_state) { bt_status_t status; if (!sBluetoothAvrcpInterface) return JNI_FALSE; ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); ALOGI("key_code: %d, key_state: %d", key_code, key_state); if ((status = sBluetoothAvrcpInterface->send_pass_through_cmd((uint8_t)key_code, (uint8_t)key_state))!= BT_STATUS_SUCCESS) { ALOGE("Failed sending passthru command, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static JNINativeMethod sMethods[] = { static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void *) classInitNative}, {"classInitNative", "()V", (void *) classInitNative}, {"initNative", "()V", (void *) initNative}, {"initNative", "()V", (void *) initNative}, Loading @@ -408,7 +476,9 @@ static JNINativeMethod sMethods[] = { {"registerNotificationRspPlayPosNative", "(II)Z", {"registerNotificationRspPlayPosNative", "(II)Z", (void *) registerNotificationRspPlayPosNative}, (void *) registerNotificationRspPlayPosNative}, {"setVolumeNative", "(I)Z", {"setVolumeNative", "(I)Z", (void *) setVolumeNative} (void *) setVolumeNative}, {"sendPassThroughCommandNative", "(II)Z", (void *) sendPassThroughCommandNative}, }; }; int register_com_android_bluetooth_avrcp(JNIEnv* env) int register_com_android_bluetooth_avrcp(JNIEnv* env) Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +33 −0 Original line number Original line Diff line number Diff line Loading @@ -200,6 +200,25 @@ public class A2dpService extends ProfileService { return mStateMachine.isPlaying(device); return mStateMachine.isPlaying(device); } } synchronized public void sendPassThroughCmd(int keyCode, int keyState) { if (DBG) Log.d(TAG, "sendPassThroughCmd"); if (mAvrcp != null) { mAvrcp.sendPassThroughCmd(keyCode, keyState); } else { Log.e(TAG,"mAvrcp is null"); } } synchronized boolean isAvrcpConnected(BluetoothDevice device) { if (DBG) Log.d(TAG, "isAvrcpConnected"); if (mAvrcp != null) { return mAvrcp.isAvrcpConnected(device); } else { Log.e(TAG,"mAvrcp is null"); return false; } } //Binder object: Must be static class or memory leak may occur //Binder object: Must be static class or memory leak may occur private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { implements IProfileServiceBinder { Loading Loading @@ -291,5 +310,19 @@ public class A2dpService extends ProfileService { if (service == null) return false; if (service == null) return false; return service.isA2dpPlaying(device); return service.isA2dpPlaying(device); } } public boolean isAvrcpConnected(BluetoothDevice device) { Log.v(TAG,"Binder Call: isAvrcpConnected: " + device.getAddress()); A2dpService service = getService(); if (service == null) return false; return service.isAvrcpConnected(device); } public void sendPassThroughCmd(int keyCode, int keyState) { Log.v(TAG,"Binder Call: sendPassThroughCmd"); A2dpService service = getService(); if (service == null) return; service.sendPassThroughCmd(keyCode, keyState); } }; }; } } android/app/src/com/android/bluetooth/a2dp/Avrcp.java +82 −4 Original line number Original line Diff line number Diff line Loading @@ -50,6 +50,10 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.List; import java.util.Set; import java.util.Set; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothA2dp; /** /** * support Bluetooth AVRCP profile. * support Bluetooth AVRCP profile. Loading Loading @@ -91,6 +95,11 @@ final class Avrcp { /* AVRC IDs from avrc_defs.h */ /* AVRC IDs from avrc_defs.h */ private static final int AVRC_ID_REWIND = 0x48; private static final int AVRC_ID_REWIND = 0x48; private static final int AVRC_ID_FAST_FOR = 0x49; private static final int AVRC_ID_FAST_FOR = 0x49; public static final int AVRC_ID_PLAY = 0x44; public static final int AVRC_ID_PAUSE = 0x46; public static final int AVRC_ID_VOL_UP = 0x41; public static final int AVRC_ID_VOL_DOWN = 0x42; public static final int AVRC_ID_STOP = 0x45; /* BTRC features */ /* BTRC features */ public static final int BTRC_FEAT_METADATA = 0x01; public static final int BTRC_FEAT_METADATA = 0x01; Loading @@ -117,6 +126,11 @@ final class Avrcp { private static final int MESSAGE_ABS_VOL_TIMEOUT = 9; private static final int MESSAGE_ABS_VOL_TIMEOUT = 9; private static final int MESSAGE_FAST_FORWARD = 10; private static final int MESSAGE_FAST_FORWARD = 10; private static final int MESSAGE_REWIND = 11; private static final int MESSAGE_REWIND = 11; private static final int MESSAGE_SEND_PASS_THROUGH_CMD = 2001; private BluetoothDevice mDevice; private int mIsConnected; private static final int MESSAGE_CHANGE_PLAY_POS = 12; private static final int MESSAGE_CHANGE_PLAY_POS = 12; private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; private static final int MSG_UPDATE_STATE = 100; private static final int MSG_UPDATE_STATE = 100; Loading @@ -137,6 +151,10 @@ final class Avrcp { private static final int AVRCP_MAX_VOL = 127; private static final int AVRCP_MAX_VOL = 127; private static final int AVRCP_BASE_VOLUME_STEP = 1; private static final int AVRCP_BASE_VOLUME_STEP = 1; private static final int AVRCP_CONNECTED = 1; public static final int KEY_STATE_PRESSED = 0; public static final int KEY_STATE_RELEASED = 1; static { static { classInitNative(); classInitNative(); } } Loading @@ -160,6 +178,7 @@ final class Avrcp { mAbsVolRetryTimes = 0; mAbsVolRetryTimes = 0; mContext = context; mContext = context; mIsConnected = 0; initNative(); initNative(); Loading Loading @@ -337,8 +356,8 @@ final class Avrcp { break; break; case MESSAGE_VOLUME_CHANGED: case MESSAGE_VOLUME_CHANGED: if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + msg.arg1 + if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f) " ctype=" + msg.arg2); + " ctype=" + msg.arg2); if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { if (mVolCmdInProgress == false) { if (mVolCmdInProgress == false) { Loading @@ -352,8 +371,11 @@ final class Avrcp { if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT || if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_INTERIM)) { msg.arg2 == AVRC_RSP_INTERIM)) { notifyVolumeChanged(msg.arg1); byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD mAbsoluteVolume = msg.arg1; notifyVolumeChanged((int)absVol); mAbsoluteVolume = absVol; long pecentVolChanged = ((long)absVol * 100) / 0x7f; Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%"); } else if (msg.arg2 == AVRC_RSP_REJ) { } else if (msg.arg2 == AVRC_RSP_REJ) { Log.e(TAG, "setAbsoluteVolume call rejected"); Log.e(TAG, "setAbsoluteVolume call rejected"); } } Loading Loading @@ -409,6 +431,11 @@ final class Avrcp { } } break; break; case MESSAGE_SEND_PASS_THROUGH_CMD: if (DEBUG) Log.v(TAG, "MESSAGE_SEND_PASS_THROUGH_CMD"); sendPassThroughCommandNative(msg.arg1, msg.arg2); break; case MESSAGE_FAST_FORWARD: case MESSAGE_FAST_FORWARD: case MESSAGE_REWIND: case MESSAGE_REWIND: int skipAmount; int skipAmount; Loading Loading @@ -595,6 +622,21 @@ final class Avrcp { mHandler.sendMessage(msg); mHandler.sendMessage(msg); } } private void onConnectionStateChanged(int state, byte[] address) { Log.v(TAG, "onConnectionStateChanged"); mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (Utils.getAddressStringFromByte(address)); Intent intent = new Intent(BluetoothA2dp.ACTION_AVRCP_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, mIsConnected); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); Log.v(TAG, "Device Address: " + mDevice.getAddress()); mIsConnected = state; Log.v(TAG, "mIsConnected: " + mIsConnected); } private void registerNotification(int eventId, int param) { private void registerNotification(int eventId, int param) { Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); mHandler.sendMessage(msg); mHandler.sendMessage(msg); Loading Loading @@ -642,6 +684,22 @@ final class Avrcp { } } } } private void handlePassthroughRsp(int id, int keyState) { switch (id) { case AVRC_ID_VOL_UP: case AVRC_ID_VOL_DOWN: case AVRC_ID_PLAY: case AVRC_ID_PAUSE: case AVRC_ID_STOP: Log.v(TAG, "passthrough response received as: key: " + id + " state: " + keyState); break; default: Log.e(TAG, "Error: unhandled passthrough response received as: key: " + id + " state: " + keyState); } } private void fastForward(int keyState) { private void fastForward(int keyState) { Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); mHandler.sendMessage(msg); mHandler.sendMessage(msg); Loading Loading @@ -790,7 +848,25 @@ final class Avrcp { mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0); Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0); mHandler.sendMessage(msg); mHandler.sendMessage(msg); } public void sendPassThroughCmd(int keyCode, int keyState) { Log.v(TAG, "sendPassThroughCmd"); Log.v(TAG, "keyCode: " + keyCode + " keyState: " + keyState); Message msg = mHandler.obtainMessage(MESSAGE_SEND_PASS_THROUGH_CMD, keyCode, keyState); mHandler.sendMessage(msg); } public boolean isAvrcpConnected(BluetoothDevice device) { Log.v(TAG, "isAvrcpConnected: " + device.getAddress()); if (device.equals(mDevice)) { if (mIsConnected == AVRCP_CONNECTED) { Log.v(TAG, "Connected: true"); return true; } } Log.v(TAG, "Connected: false"); return false; } } /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the Loading Loading @@ -873,4 +949,6 @@ final class Avrcp { private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); private native boolean registerNotificationRspPlayPosNative(int type, int playPos); private native boolean registerNotificationRspPlayPosNative(int type, int playPos); private native boolean setVolumeNative(int volume); private native boolean setVolumeNative(int volume); private native boolean sendPassThroughCommandNative(int keyCode, int keyState); } } Loading
android/app/jni/com_android_bluetooth_avrcp.cpp +72 −2 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,8 @@ static jmethodID method_getElementAttr; static jmethodID method_registerNotification; static jmethodID method_registerNotification; static jmethodID method_volumeChangeCallback; static jmethodID method_volumeChangeCallback; static jmethodID method_handlePassthroughCmd; static jmethodID method_handlePassthroughCmd; static jmethodID method_handlePassthroughRsp; static jmethodID method_onConnectionStateChanged; static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; static jobject mCallbacksObj = NULL; static jobject mCallbacksObj = NULL; Loading Loading @@ -142,6 +144,46 @@ static void btavrcp_passthrough_command_callback(int id, int pressed) { checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); } } static void btavrcp_passthrough_response_callback(int id, int pressed) { ALOGI("%s", __FUNCTION__); ALOGI("id: %d, pressed: %d", id, pressed); if (!checkCallbackThread()) { ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughRsp, (jint)id, (jint)pressed); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); } static void btavrcp_connection_state_callback(int state, bt_bdaddr_t* bd_addr) { jbyteArray addr; ALOGI("%s", __FUNCTION__); ALOGI("conn state: %d", state); if (!checkCallbackThread()) { \ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ return; \ } addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); if (!addr) { ALOGE("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_onConnectionStateChanged, (jint) state, addr); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); sCallbackEnv->DeleteLocalRef(addr); } static btrc_callbacks_t sBluetoothAvrcpCallbacks = { static btrc_callbacks_t sBluetoothAvrcpCallbacks = { sizeof(sBluetoothAvrcpCallbacks), sizeof(sBluetoothAvrcpCallbacks), btavrcp_remote_features_callback, btavrcp_remote_features_callback, Loading @@ -155,7 +197,9 @@ static btrc_callbacks_t sBluetoothAvrcpCallbacks = { btavrcp_get_element_attr_callback, btavrcp_get_element_attr_callback, btavrcp_register_notification_callback, btavrcp_register_notification_callback, btavrcp_volume_change_callback, btavrcp_volume_change_callback, btavrcp_passthrough_command_callback btavrcp_passthrough_command_callback, btavrcp_passthrough_response_callback, btavrcp_connection_state_callback }; }; static void classInitNative(JNIEnv* env, jclass clazz) { static void classInitNative(JNIEnv* env, jclass clazz) { Loading @@ -176,6 +220,12 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_handlePassthroughCmd = method_handlePassthroughCmd = env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V"); env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V"); method_handlePassthroughRsp = env->GetMethodID(clazz, "handlePassthroughRsp", "(II)V"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); ALOGI("%s: succeeds", __FUNCTION__); ALOGI("%s: succeeds", __FUNCTION__); } } Loading Loading @@ -395,6 +445,24 @@ static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) { return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } } static jboolean sendPassThroughCommandNative(JNIEnv *env, jobject object, jint key_code, jint key_state) { bt_status_t status; if (!sBluetoothAvrcpInterface) return JNI_FALSE; ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); ALOGI("key_code: %d, key_state: %d", key_code, key_state); if ((status = sBluetoothAvrcpInterface->send_pass_through_cmd((uint8_t)key_code, (uint8_t)key_state))!= BT_STATUS_SUCCESS) { ALOGE("Failed sending passthru command, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static JNINativeMethod sMethods[] = { static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void *) classInitNative}, {"classInitNative", "()V", (void *) classInitNative}, {"initNative", "()V", (void *) initNative}, {"initNative", "()V", (void *) initNative}, Loading @@ -408,7 +476,9 @@ static JNINativeMethod sMethods[] = { {"registerNotificationRspPlayPosNative", "(II)Z", {"registerNotificationRspPlayPosNative", "(II)Z", (void *) registerNotificationRspPlayPosNative}, (void *) registerNotificationRspPlayPosNative}, {"setVolumeNative", "(I)Z", {"setVolumeNative", "(I)Z", (void *) setVolumeNative} (void *) setVolumeNative}, {"sendPassThroughCommandNative", "(II)Z", (void *) sendPassThroughCommandNative}, }; }; int register_com_android_bluetooth_avrcp(JNIEnv* env) int register_com_android_bluetooth_avrcp(JNIEnv* env) Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +33 −0 Original line number Original line Diff line number Diff line Loading @@ -200,6 +200,25 @@ public class A2dpService extends ProfileService { return mStateMachine.isPlaying(device); return mStateMachine.isPlaying(device); } } synchronized public void sendPassThroughCmd(int keyCode, int keyState) { if (DBG) Log.d(TAG, "sendPassThroughCmd"); if (mAvrcp != null) { mAvrcp.sendPassThroughCmd(keyCode, keyState); } else { Log.e(TAG,"mAvrcp is null"); } } synchronized boolean isAvrcpConnected(BluetoothDevice device) { if (DBG) Log.d(TAG, "isAvrcpConnected"); if (mAvrcp != null) { return mAvrcp.isAvrcpConnected(device); } else { Log.e(TAG,"mAvrcp is null"); return false; } } //Binder object: Must be static class or memory leak may occur //Binder object: Must be static class or memory leak may occur private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { implements IProfileServiceBinder { Loading Loading @@ -291,5 +310,19 @@ public class A2dpService extends ProfileService { if (service == null) return false; if (service == null) return false; return service.isA2dpPlaying(device); return service.isA2dpPlaying(device); } } public boolean isAvrcpConnected(BluetoothDevice device) { Log.v(TAG,"Binder Call: isAvrcpConnected: " + device.getAddress()); A2dpService service = getService(); if (service == null) return false; return service.isAvrcpConnected(device); } public void sendPassThroughCmd(int keyCode, int keyState) { Log.v(TAG,"Binder Call: sendPassThroughCmd"); A2dpService service = getService(); if (service == null) return; service.sendPassThroughCmd(keyCode, keyState); } }; }; } }
android/app/src/com/android/bluetooth/a2dp/Avrcp.java +82 −4 Original line number Original line Diff line number Diff line Loading @@ -50,6 +50,10 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.List; import java.util.Set; import java.util.Set; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothA2dp; /** /** * support Bluetooth AVRCP profile. * support Bluetooth AVRCP profile. Loading Loading @@ -91,6 +95,11 @@ final class Avrcp { /* AVRC IDs from avrc_defs.h */ /* AVRC IDs from avrc_defs.h */ private static final int AVRC_ID_REWIND = 0x48; private static final int AVRC_ID_REWIND = 0x48; private static final int AVRC_ID_FAST_FOR = 0x49; private static final int AVRC_ID_FAST_FOR = 0x49; public static final int AVRC_ID_PLAY = 0x44; public static final int AVRC_ID_PAUSE = 0x46; public static final int AVRC_ID_VOL_UP = 0x41; public static final int AVRC_ID_VOL_DOWN = 0x42; public static final int AVRC_ID_STOP = 0x45; /* BTRC features */ /* BTRC features */ public static final int BTRC_FEAT_METADATA = 0x01; public static final int BTRC_FEAT_METADATA = 0x01; Loading @@ -117,6 +126,11 @@ final class Avrcp { private static final int MESSAGE_ABS_VOL_TIMEOUT = 9; private static final int MESSAGE_ABS_VOL_TIMEOUT = 9; private static final int MESSAGE_FAST_FORWARD = 10; private static final int MESSAGE_FAST_FORWARD = 10; private static final int MESSAGE_REWIND = 11; private static final int MESSAGE_REWIND = 11; private static final int MESSAGE_SEND_PASS_THROUGH_CMD = 2001; private BluetoothDevice mDevice; private int mIsConnected; private static final int MESSAGE_CHANGE_PLAY_POS = 12; private static final int MESSAGE_CHANGE_PLAY_POS = 12; private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; private static final int MSG_UPDATE_STATE = 100; private static final int MSG_UPDATE_STATE = 100; Loading @@ -137,6 +151,10 @@ final class Avrcp { private static final int AVRCP_MAX_VOL = 127; private static final int AVRCP_MAX_VOL = 127; private static final int AVRCP_BASE_VOLUME_STEP = 1; private static final int AVRCP_BASE_VOLUME_STEP = 1; private static final int AVRCP_CONNECTED = 1; public static final int KEY_STATE_PRESSED = 0; public static final int KEY_STATE_RELEASED = 1; static { static { classInitNative(); classInitNative(); } } Loading @@ -160,6 +178,7 @@ final class Avrcp { mAbsVolRetryTimes = 0; mAbsVolRetryTimes = 0; mContext = context; mContext = context; mIsConnected = 0; initNative(); initNative(); Loading Loading @@ -337,8 +356,8 @@ final class Avrcp { break; break; case MESSAGE_VOLUME_CHANGED: case MESSAGE_VOLUME_CHANGED: if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + msg.arg1 + if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f) " ctype=" + msg.arg2); + " ctype=" + msg.arg2); if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { if (mVolCmdInProgress == false) { if (mVolCmdInProgress == false) { Loading @@ -352,8 +371,11 @@ final class Avrcp { if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT || if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_INTERIM)) { msg.arg2 == AVRC_RSP_INTERIM)) { notifyVolumeChanged(msg.arg1); byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD mAbsoluteVolume = msg.arg1; notifyVolumeChanged((int)absVol); mAbsoluteVolume = absVol; long pecentVolChanged = ((long)absVol * 100) / 0x7f; Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%"); } else if (msg.arg2 == AVRC_RSP_REJ) { } else if (msg.arg2 == AVRC_RSP_REJ) { Log.e(TAG, "setAbsoluteVolume call rejected"); Log.e(TAG, "setAbsoluteVolume call rejected"); } } Loading Loading @@ -409,6 +431,11 @@ final class Avrcp { } } break; break; case MESSAGE_SEND_PASS_THROUGH_CMD: if (DEBUG) Log.v(TAG, "MESSAGE_SEND_PASS_THROUGH_CMD"); sendPassThroughCommandNative(msg.arg1, msg.arg2); break; case MESSAGE_FAST_FORWARD: case MESSAGE_FAST_FORWARD: case MESSAGE_REWIND: case MESSAGE_REWIND: int skipAmount; int skipAmount; Loading Loading @@ -595,6 +622,21 @@ final class Avrcp { mHandler.sendMessage(msg); mHandler.sendMessage(msg); } } private void onConnectionStateChanged(int state, byte[] address) { Log.v(TAG, "onConnectionStateChanged"); mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (Utils.getAddressStringFromByte(address)); Intent intent = new Intent(BluetoothA2dp.ACTION_AVRCP_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, mIsConnected); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); Log.v(TAG, "Device Address: " + mDevice.getAddress()); mIsConnected = state; Log.v(TAG, "mIsConnected: " + mIsConnected); } private void registerNotification(int eventId, int param) { private void registerNotification(int eventId, int param) { Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); mHandler.sendMessage(msg); mHandler.sendMessage(msg); Loading Loading @@ -642,6 +684,22 @@ final class Avrcp { } } } } private void handlePassthroughRsp(int id, int keyState) { switch (id) { case AVRC_ID_VOL_UP: case AVRC_ID_VOL_DOWN: case AVRC_ID_PLAY: case AVRC_ID_PAUSE: case AVRC_ID_STOP: Log.v(TAG, "passthrough response received as: key: " + id + " state: " + keyState); break; default: Log.e(TAG, "Error: unhandled passthrough response received as: key: " + id + " state: " + keyState); } } private void fastForward(int keyState) { private void fastForward(int keyState) { Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); mHandler.sendMessage(msg); mHandler.sendMessage(msg); Loading Loading @@ -790,7 +848,25 @@ final class Avrcp { mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0); Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0); mHandler.sendMessage(msg); mHandler.sendMessage(msg); } public void sendPassThroughCmd(int keyCode, int keyState) { Log.v(TAG, "sendPassThroughCmd"); Log.v(TAG, "keyCode: " + keyCode + " keyState: " + keyState); Message msg = mHandler.obtainMessage(MESSAGE_SEND_PASS_THROUGH_CMD, keyCode, keyState); mHandler.sendMessage(msg); } public boolean isAvrcpConnected(BluetoothDevice device) { Log.v(TAG, "isAvrcpConnected: " + device.getAddress()); if (device.equals(mDevice)) { if (mIsConnected == AVRCP_CONNECTED) { Log.v(TAG, "Connected: true"); return true; } } Log.v(TAG, "Connected: false"); return false; } } /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the Loading Loading @@ -873,4 +949,6 @@ final class Avrcp { private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); private native boolean registerNotificationRspPlayPosNative(int type, int playPos); private native boolean registerNotificationRspPlayPosNative(int type, int playPos); private native boolean setVolumeNative(int volume); private native boolean setVolumeNative(int volume); private native boolean sendPassThroughCommandNative(int keyCode, int keyState); } }