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

Commit 6613fd3f authored by Yan Han's avatar Yan Han
Browse files

Add wrapper for AudioDeviceVolumeManager in the HDMI framework

Adding a wrapper interface for AudioDeviceVolumeManager allows the class
to be faked. This allows tests that instantiate HdmiControlService to
avoid also instantiating AudioDeviceVolumeManager, which can affect the
state of AudioService on the DUT.

Using an interface instead of inheritance allows constructors to be more
independent, and allows the same class to "wrap" multiple managers in
the future if they need to share state (e.g. AudioManager and
AudioDeviceVolumeManager).

Bug: 217923086
Test: atest com.android.server.hdmi
Change-Id: I673ab6e3a6acbbc6af8998e2f5c048fec59ad47a
parent 30eec56e
Loading
Loading
Loading
Loading
+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);
}
+35 −10
Original line number Diff line number Diff line
@@ -410,6 +410,9 @@ public class HdmiControlService extends SystemService {
    @Nullable
    private PowerManagerInternalWrapper mPowerManagerInternal;

    @Nullable
    private AudioDeviceVolumeManagerWrapperInterface mAudioDeviceVolumeManager;

    @Nullable
    private Looper mIoLooper;

@@ -439,11 +442,21 @@ public class HdmiControlService extends SystemService {

    private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();

    @VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes) {
    /**
     * Constructor for testing.
     *
     * It's critical to use a fake AudioDeviceVolumeManager because a normally instantiated
     * AudioDeviceVolumeManager can access the "real" AudioService on the DUT.
     *
     * @see FakeAudioDeviceVolumeManagerWrapper
     */
    @VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes,
            AudioDeviceVolumeManagerWrapperInterface audioDeviceVolumeManager) {
        super(context);
        mLocalDevices = deviceTypes;
        mSettingsObserver = new SettingsObserver(mHandler);
        mHdmiCecConfig = new HdmiCecConfig(context);
        mAudioDeviceVolumeManager = audioDeviceVolumeManager;
    }

    public HdmiControlService(Context context) {
@@ -744,6 +757,8 @@ public class HdmiControlService extends SystemService {
                    Context.TV_INPUT_SERVICE);
            mPowerManager = new PowerManagerWrapper(getContext());
            mPowerManagerInternal = new PowerManagerInternalWrapper();
            mAudioDeviceVolumeManager =
                    new AudioDeviceVolumeManagerWrapper(getContext());
        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
            runOnServiceThread(this::bootCompleted);
        }
@@ -3086,6 +3101,7 @@ public class HdmiControlService extends SystemService {
    private void announceHdmiCecVolumeControlFeatureChange(
            @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
        assertRunOnServiceThread();
        synchronized (mLock) {
            mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
                try {
                    listener.onHdmiCecVolumeControlFeature(hdmiCecVolumeControl);
@@ -3096,6 +3112,7 @@ public class HdmiControlService extends SystemService {
                }
            });
        }
    }

    public HdmiCecLocalDeviceTv tv() {
        return (HdmiCecLocalDeviceTv) mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
@@ -3135,6 +3152,14 @@ public class HdmiControlService extends SystemService {
        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
    }

    /**
     * Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
     */
    @Nullable
    private AudioDeviceVolumeManagerWrapperInterface getAudioDeviceVolumeManager() {
        return mAudioDeviceVolumeManager;
    }

    boolean isControlEnabled() {
        synchronized (mLock) {
            return mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+2 −1
Original line number Diff line number Diff line
@@ -61,7 +61,8 @@ public class ActiveSourceActionTest {
    public void setUp() throws Exception {
        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));

        mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
        mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList(),
                new FakeAudioDeviceVolumeManagerWrapper()) {
            @Override
            AudioManager getAudioManager() {
                return new AudioManager() {
+2 −1
Original line number Diff line number Diff line
@@ -68,7 +68,8 @@ public class ArcInitiationActionFromAvrTest {
        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));

        HdmiControlService hdmiControlService =
                new HdmiControlService(mContextSpy, Collections.emptyList()) {
                new HdmiControlService(mContextSpy, Collections.emptyList(),
                        new FakeAudioDeviceVolumeManagerWrapper()) {
                    @Override
                    boolean isPowerStandby() {
                        return false;
Loading