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

Commit aec17b50 authored by Joseph Pirozzo's avatar Joseph Pirozzo
Browse files

AVRCP Controller AbsoluteVolumeNotification

Update AbsoluteVolumeNotification request to properly respond to
requests based upon the Fixed vs Dynamic volume levels.

Bug: 129478624
Test: atest com.android.bluetooth.avrcpcontroller.AvrcpControllerStateMachineTest#testRegisterAbsVolumeNotification
Change-Id: Ic9b745641a84712c005d40ecec50e55e9684d3ef
parent b035bef6
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
     *