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

Commit c52a8b08 authored by Joseph Pirozzo's avatar Joseph Pirozzo Committed by android-build-merger
Browse files

Merge "AVRCP Controller AbsoluteVolumeNotification"

am: c657ffd8

Change-Id: I074d79db9c6754f40d5cf157d2d527cecfb06b0b
parents 220c68ad c657ffd8
Loading
Loading
Loading
Loading
+64 −26
Original line number Diff line number Diff line
@@ -94,7 +94,14 @@ class AvrcpControllerStateMachine extends StateMachine {
     */
    private static final int ABS_VOL_BASE = 127;

    /*
     * Notification types for Avrcp protocol JNI.
     */
    private static final byte NOTIFICATION_RSP_TYPE_INTERIM = 0x00;
    private static final byte NOTIFICATION_RSP_TYPE_CHANGED = 0x01;

    private final AudioManager mAudioManager;
    private final boolean mIsVolumeFixed;

    protected final BluetoothDevice mDevice;
    protected final byte[] mDeviceAddress;
@@ -113,6 +120,7 @@ class AvrcpControllerStateMachine extends StateMachine {
    private int mAddressedPlayerId = -1;
    private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>();
    private int mVolumeChangedNotificationsToIgnore = 0;
    private int mVolumeNotificationLabel = -1;

    GetFolderList mGetFolderList = null;

@@ -142,6 +150,7 @@ class AvrcpControllerStateMachine extends StateMachine {
        mGetFolderList = new GetFolderList();
        addState(mGetFolderList, mConnected);
        mAudioManager = (AudioManager) service.getSystemService(Context.AUDIO_SERVICE);
        mIsVolumeFixed = mAudioManager.isVolumeFixed();

        setInitialState(mDisconnected);
    }
@@ -311,7 +320,14 @@ class AvrcpControllerStateMachine extends StateMachine {
                    removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT);
                    sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT,
                            ABS_VOL_TIMEOUT_MILLIS);
                    setAbsVolume(msg.arg1, msg.arg2);
                    handleAbsVolumeRequest(msg.arg1, msg.arg2);
                    return true;

                case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
                    mVolumeNotificationLabel = msg.arg1;
                    mService.sendRegisterAbsVolRspNative(mDeviceAddress,
                            NOTIFICATION_RSP_TYPE_INTERIM,
                            getAbsVolume(), mVolumeNotificationLabel);
                    return true;

                case MESSAGE_GET_FOLDER_ITEMS:
@@ -587,24 +603,9 @@ class AvrcpControllerStateMachine extends StateMachine {
                    }
                    break;

                case CONNECT:
                case DISCONNECT:
                case MSG_AVRCP_PASSTHRU:
                case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
                case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
                case MESSAGE_PROCESS_TRACK_CHANGED:
                case MESSAGE_PROCESS_PLAY_POS_CHANGED:
                case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
                case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
                case MESSAGE_PLAY_ITEM:
                case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED:
                default:
                    // All of these messages should be handled by parent state immediately.
                    return false;

                default:
                    logD(STATE_TAG + " deferring message " + msg.what
                            + " to connected!");
                    deferMessage(msg);
            }
            return true;
        }
@@ -717,23 +718,60 @@ class AvrcpControllerStateMachine extends StateMachine {
        }
    }

    /**
     * Handle a request to align our local volume with the volume of a remote device. If
     * we're assuming the source volume is fixed then a response of ABS_VOL_MAX will always be
     * sent and no volume adjustment action will be taken on the sink side.
     *
     * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX]
     * @param label Volume notification label
     */
    private void handleAbsVolumeRequest(int absVol, int label) {
        logD("handleAbsVolumeRequest: absVol = " + absVol + ", label = " + label);
        if (mIsVolumeFixed) {
            logD("Source volume is assumed to be fixed, responding with max volume");
            absVol = ABS_VOL_BASE;
        } else {
            mVolumeChangedNotificationsToIgnore++;
            removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT);
            sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT,
                    ABS_VOL_TIMEOUT_MILLIS);
            setAbsVolume(absVol);
        }
        mService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
    }

    /**
     * Align our volume with a requested absolute volume level
     *
     * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX]
     */
    private void setAbsVolume(int absVol) {
        int maxLocalVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int curLocalVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        int reqLocalVolume = (maxLocalVolume * absVol) / ABS_VOL_BASE;
        logD("setAbsVolme: absVol = " + absVol + ", reqLocal = " + reqLocalVolume
                + ", curLocal = " + curLocalVolume + ", maxLocal = " + maxLocalVolume);

    private void setAbsVolume(int absVol, int label) {
        int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        int newIndex = (maxVolume * absVol) / ABS_VOL_BASE;
        logD(" setAbsVolume =" + absVol + " maxVol = " + maxVolume
                + " cur = " + currIndex + " new = " + newIndex);
        /*
         * In some cases change in percentage is not sufficient enough to warrant
         * change in index values which are in range of 0-15. For such cases
         * no action is required
         */
        if (newIndex != currIndex) {
            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex,
        if (reqLocalVolume != curLocalVolume) {
            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, reqLocalVolume,
                    AudioManager.FLAG_SHOW_UI);
        }
        mService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
    }

    private int getAbsVolume() {
        if (mIsVolumeFixed) {
            return ABS_VOL_BASE;
        }
        int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        int newIndex = (currIndex * ABS_VOL_BASE) / maxVolume;
        return newIndex;
    }

    MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {
+20 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Looper;
import android.support.v4.media.session.MediaControllerCompat;

@@ -70,6 +71,8 @@ public class AvrcpControllerStateMachineTest {
    @Mock
    private AdapterService mAdapterService;
    @Mock
    private AudioManager mAudioManager;
    @Mock
    private AvrcpControllerService mAvrcpControllerService;

    AvrcpControllerStateMachine mAvrcpStateMachine;
@@ -90,6 +93,11 @@ public class AvrcpControllerStateMachineTest {
        TestUtils.setAdapterService(mAdapterService);
        TestUtils.startService(mServiceRule, AvrcpControllerService.class);
        doReturn(mTargetContext.getResources()).when(mAvrcpControllerService).getResources();
        doReturn(15).when(mAudioManager).getStreamMaxVolume(anyInt());
        doReturn(8).when(mAudioManager).getStreamVolume(anyInt());
        doReturn(true).when(mAudioManager).isVolumeFixed();
        doReturn(mAudioManager).when(mAvrcpControllerService)
                .getSystemService(Context.AUDIO_SERVICE);

        // This line must be called to make sure relevant objects are initialized properly
        mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -524,6 +532,18 @@ public class AvrcpControllerStateMachineTest {
                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
    }

    /**
     * Test that Absolute Volume Registration is working
     */
    @Test
    public void testRegisterAbsVolumeNotification() {
        setUpConnectedState(true, true);
        mAvrcpStateMachine.sendMessage(
                AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION);
        verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .sendRegisterAbsVolRspNative(any(), anyByte(), eq(127), anyInt());
    }

    /**
     * Setup Connected State
     *