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

Commit a62f3275 authored by Yan Han's avatar Yan Han Committed by Android (Google) Code Review
Browse files

Merge changes from topics "presubmit-am-7ed5c317d7b347f1a59eb332f20e8522",...

Merge changes from topics "presubmit-am-7ed5c317d7b347f1a59eb332f20e8522", "presubmit-am-86ce18b053454a4db98a349f8b39985e", "presubmit-am-94d3142a4e1c418fadfd6eca6208920a", "presubmit-am-a466ed1382304d549cb550a5cae804e6" into tm-dev

* changes:
  Add Absolute Volume Control support for a TV as the System Audio device
  Implement support for Absolute Volume Control
  Add call to onBootPhase to tests that use HdmiControlService
  Add wrapper for AudioDeviceVolumeManager in the HDMI framework
parents cc3898c6 73dee584
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,97 +16,88 @@

package com.android.server.hdmi;

import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;

import com.android.server.hdmi.HdmiControlService.SendMessageCallback;

import java.util.List;

/**
 * Action to update audio status (volume or mute) of audio amplifier
 * Action to query and track the audio status of the System Audio device when enabling or using
 * Absolute Volume Control. Must be removed when AVC is disabled. Performs two main functions:
 * 1. When enabling AVC: queries the starting audio status of the System Audio device and
 *    enables the feature upon receiving a response.
 * 2. While AVC is enabled: monitors <Report Audio Status> messages from the System Audio device and
 *    notifies AudioService if the audio status changes.
 */
final class SystemAudioStatusAction extends HdmiCecFeatureAction {
    private static final String TAG = "SystemAudioStatusAction";
final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {
    private static final String TAG = "AbsoluteVolumeAudioStatusAction";

    // State that waits for <ReportAudioStatus>.
    private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
    private int mInitialAudioStatusRetriesLeft = 2;

    private final int mAvrAddress;
    private static final int STATE_WAIT_FOR_INITIAL_AUDIO_STATUS = 1;
    private static final int STATE_MONITOR_AUDIO_STATUS = 2;

    SystemAudioStatusAction(
            HdmiCecLocalDevice source, int avrAddress, List<IHdmiControlCallback> callbacks) {
        super(source, callbacks);
        mAvrAddress = avrAddress;
    }
    private final int mTargetAddress;

    SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress,
            IHdmiControlCallback callback) {
        super(source, callback);
        mAvrAddress = avrAddress;
    private AudioStatus mLastAudioStatus;

    AbsoluteVolumeAudioStatusAction(HdmiCecLocalDevice source, int targetAddress) {
        super(source);
        mTargetAddress = targetAddress;
    }

    @Override
    boolean start() {
        mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
        mState = STATE_WAIT_FOR_INITIAL_AUDIO_STATUS;
        sendGiveAudioStatus();
        return true;
    }

    private void sendGiveAudioStatus() {
        sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrAddress),
                new SendMessageCallback() {
            @Override
            public void onSendCompleted(int error) {
                if (error != SendMessageResult.SUCCESS) {
                    handleSendGiveAudioStatusFailure();
                }
    void updateVolume(int volumeIndex) {
        mLastAudioStatus = new AudioStatus(volumeIndex, mLastAudioStatus.getMute());
    }
        });
    }

    private void handleSendGiveAudioStatusFailure() {

        // Still return SUCCESS to callback.
        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
    private void sendGiveAudioStatus() {
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
        sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mTargetAddress));
    }

    @Override
    boolean processCommand(HdmiCecMessage cmd) {
        if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS || mAvrAddress != cmd.getSource()) {
            return false;
        }

        switch (cmd.getOpcode()) {
            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
                handleReportAudioStatus(cmd);
                return true;
                return handleReportAudioStatus(cmd);
        }

        return false;
    }

    private void handleReportAudioStatus(HdmiCecMessage cmd) {
        byte[] params = cmd.getParams();
    private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
        if (mTargetAddress != cmd.getSource() || cmd.getParams().length == 0) {
            return false;
        }

        boolean mute = HdmiUtils.isAudioStatusMute(cmd);
        int volume = HdmiUtils.getAudioStatusVolume(cmd);
        tv().setAudioStatus(mute, volume);

        if (!(tv().isSystemAudioActivated() ^ mute)) {
            // Toggle AVR's mute status to match with the system audio status.
            sendUserControlPressedAndReleased(mAvrAddress, HdmiCecKeycode.CEC_KEYCODE_MUTE);
        AudioStatus audioStatus = new AudioStatus(volume, mute);
        if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
            localDevice().getService().enableAbsoluteVolumeControl(audioStatus);
            mState = STATE_MONITOR_AUDIO_STATUS;
        } else if (mState == STATE_MONITOR_AUDIO_STATUS) {
            if (audioStatus.getVolume() != mLastAudioStatus.getVolume()) {
                localDevice().getService().notifyAvcVolumeChange(audioStatus.getVolume());
            }
        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
            if (audioStatus.getMute() != mLastAudioStatus.getMute()) {
                localDevice().getService().notifyAvcMuteChange(audioStatus.getMute());
            }
        }
        mLastAudioStatus = audioStatus;

        return true;
    }

    @Override
    void handleTimerEvent(int state) {
        if (mState != state) {
            return;
        } else if (mInitialAudioStatusRetriesLeft > 0) {
            mInitialAudioStatusRetriesLeft--;
            sendGiveAudioStatus();
        }

        handleSendGiveAudioStatusFailure();
    }
}
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.hdmi;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
import android.media.VolumeInfo;

import java.util.concurrent.Executor;

/**
 * Wrapper for {@link AudioDeviceVolumeManager}. Creates an instance of the class and directly
 * passes method calls to that instance.
 */
public class AudioDeviceVolumeManagerWrapper
        implements AudioDeviceVolumeManagerWrapperInterface {

    private static final String TAG = "AudioDeviceVolumeManagerWrapper";

    private final AudioDeviceVolumeManager mAudioDeviceVolumeManager;

    public AudioDeviceVolumeManagerWrapper(Context context) {
        mAudioDeviceVolumeManager = new AudioDeviceVolumeManager(context);
    }

    @Override
    public void addOnDeviceVolumeBehaviorChangedListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener)
            throws SecurityException {
        mAudioDeviceVolumeManager.addOnDeviceVolumeBehaviorChangedListener(executor, listener);
    }

    @Override
    public void removeOnDeviceVolumeBehaviorChangedListener(
            @NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener) {
        mAudioDeviceVolumeManager.removeOnDeviceVolumeBehaviorChangedListener(listener);
    }

    @Override
    public void setDeviceAbsoluteVolumeBehavior(
            @NonNull AudioDeviceAttributes device,
            @NonNull VolumeInfo volume,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
            boolean handlesVolumeAdjustment) {
        mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeBehavior(device, volume, executor,
                vclistener, handlesVolumeAdjustment);
    }
}
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.hdmi;

import static android.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener;
import static android.media.AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
import android.media.VolumeInfo;

import java.util.concurrent.Executor;

/**
 * Interface with the methods from {@link AudioDeviceVolumeManager} used by the HDMI framework.
 * Allows the class to be faked for tests.
 */
public interface AudioDeviceVolumeManagerWrapperInterface {

    /**
     * Wrapper for {@link AudioDeviceVolumeManager#addOnDeviceVolumeBehaviorChangedListener(
     * Executor, OnDeviceVolumeBehaviorChangedListener)}
     */
    void addOnDeviceVolumeBehaviorChangedListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener);

    /**
     * Wrapper for {@link AudioDeviceVolumeManager#removeOnDeviceVolumeBehaviorChangedListener(
     * OnDeviceVolumeBehaviorChangedListener)}
     */
    void removeOnDeviceVolumeBehaviorChangedListener(
            @NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener);

    /**
     * Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior(
     * AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
     */
    void setDeviceAbsoluteVolumeBehavior(
            @NonNull AudioDeviceAttributes device,
            @NonNull VolumeInfo volume,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
            boolean handlesVolumeAdjustment);
}
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.hdmi;

import android.annotation.Nullable;

import java.util.Objects;

/**
 * Immutable representation of the information in the [Audio Status] operand:
 * volume status (0 <= N <= 100) and mute status (muted or unmuted).
 */
public class AudioStatus {
    public static final int MAX_VOLUME = 100;
    public static final int MIN_VOLUME = 0;

    int mVolume;
    boolean mMute;

    public AudioStatus(int volume, boolean mute) {
        mVolume = volume;
        mMute = mute;
    }

    public int getVolume() {
        return mVolume;
    }

    public boolean getMute() {
        return mMute;
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if (!(obj instanceof AudioStatus)) {
            return false;
        }

        AudioStatus other = (AudioStatus) obj;
        return mVolume == other.mVolume
                && mMute == other.mMute;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mVolume, mMute);
    }

    @Override
    public String toString() {
        return "AudioStatus mVolume:" + mVolume + " mMute:" + mMute;
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ final class Constants {
            MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
            MESSAGE_GIVE_AUDIO_STATUS,
            MESSAGE_SET_SYSTEM_AUDIO_MODE,
            MESSAGE_SET_AUDIO_VOLUME_LEVEL,
            MESSAGE_REPORT_AUDIO_STATUS,
            MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS,
            MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
@@ -197,9 +198,9 @@ final class Constants {
    static final int MESSAGE_SYSTEM_AUDIO_MODE_REQUEST = 0x70;
    static final int MESSAGE_GIVE_AUDIO_STATUS = 0x71;
    static final int MESSAGE_SET_SYSTEM_AUDIO_MODE = 0x72;
    static final int MESSAGE_SET_AUDIO_VOLUME_LEVEL = 0x73;
    static final int MESSAGE_REPORT_AUDIO_STATUS = 0x7A;
    static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D;
    static final int MESSAGE_SET_AUDIO_VOLUME_LEVEL = 0x73;
    static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 0x7E;
    static final int MESSAGE_ROUTING_CHANGE = 0x80;
    static final int MESSAGE_ROUTING_INFORMATION = 0x81;
Loading