Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0e9887e authored by Mike Lockwood's avatar Mike Lockwood Committed by Android Partner Code Review
Browse files

Merge "[4/4] Add Support for AVRCP Controller Feature" into klp-wireless-dev

parents 91265749 eaf8fdd0
Loading
Loading
Loading
Loading
+72 −2
Original line number Original line Diff line number Diff line
@@ -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;
@@ -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,
@@ -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) {
@@ -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__);
}
}


@@ -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},
@@ -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)
+33 −0
Original line number Original line Diff line number Diff line
@@ -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 {
@@ -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);
        }
    };
    };
}
}
+82 −4
Original line number Original line Diff line number Diff line
@@ -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.
@@ -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;
@@ -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;
@@ -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();
    }
    }
@@ -160,6 +178,7 @@ final class Avrcp {
        mAbsVolRetryTimes = 0;
        mAbsVolRetryTimes = 0;


        mContext = context;
        mContext = context;
        mIsConnected = 0;


        initNative();
        initNative();


@@ -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) {
@@ -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");
                }
                }
@@ -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;
@@ -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);
@@ -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);
@@ -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
@@ -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);

}
}