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

Commit fd133451 authored by Nathalie Le Clair's avatar Nathalie Le Clair
Browse files

Create and remove HdmiEarcLocalDeviceTx

Create when HdmiControlService is started, or eARC gets enabled.
Remove when eARC gets disabled.

Test: atest
Bug: 240388108
Change-Id: I1c563de3022ab3b614b6b71fc36250daced9b06b
parent f9bfa032
Loading
Loading
Loading
Loading
+155 −10
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.hdmi;

import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_ENABLED;
@@ -184,13 +185,14 @@ public class HdmiControlService extends SystemService {

    static final String PERMISSION = "android.permission.HDMI_CEC";

    // The reason code to initiate initializeCec().
    // The reason code to initiate initializeCec() and initializeEarc().
    static final int INITIATED_BY_ENABLE_CEC = 0;
    static final int INITIATED_BY_BOOT_UP = 1;
    static final int INITIATED_BY_SCREEN_ON = 2;
    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
    static final int INITIATED_BY_HOTPLUG = 4;
    static final int INITIATED_BY_SOUNDBAR_MODE = 5;
    static final int INITIATED_BY_ENABLE_EARC = 6;

    // The reason code representing the intent action that drives the standby
    // procedure. The procedure starts either by Intent.ACTION_SCREEN_OFF or
@@ -384,6 +386,15 @@ public class HdmiControlService extends SystemService {
    @HdmiControlManager.HdmiCecControl
    private int mHdmiControlEnabled;

    // Set to true while the eARC feature is supported by the hardware and the eARC HAL is present.
    @GuardedBy("mLock")
    @VisibleForTesting
    protected boolean mEarcSupported;

    // Set to true while the eARC feature is enabled.
    @GuardedBy("mLock")
    private boolean mEarcEnabled;

    // Set to true while the service is in normal mode. While set to false, no input change is
    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
    // system upgrade, etc., a.k.a. "prohibit mode".
@@ -417,6 +428,9 @@ public class HdmiControlService extends SystemService {

    private HdmiCecPowerStatusController mPowerStatusController;

    @Nullable
    private HdmiEarcLocalDevice mEarcLocalDevice;

    @ServiceThreadOnly
    private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault());

@@ -631,9 +645,13 @@ public class HdmiControlService extends SystemService {
            mPowerStatusController = new HdmiCecPowerStatusController(this);
        }
        mPowerStatusController.setPowerStatus(getInitialPowerStatus());
        mProhibitMode = false;
        setProhibitMode(false);
        mHdmiControlEnabled = mHdmiCecConfig.getIntValue(
                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
        synchronized (mLock) {
            mEarcEnabled = (mHdmiCecConfig.getIntValue(
                    HdmiControlManager.SETTING_NAME_EARC_ENABLED) == EARC_FEATURE_ENABLED);
        }
        setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
@@ -655,11 +673,16 @@ public class HdmiControlService extends SystemService {
            Slog.i(TAG, "Device does not support MHL-control.");
        }
        mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
        if (mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
        if (isCecControlEnabled()) {
            initializeCec(INITIATED_BY_BOOT_UP);
        } else {
            mCecController.enableCec(false);
        }
        synchronized (mLock) {
            if (mEarcSupported && mEarcEnabled) {
                initializeEarc(INITIATED_BY_BOOT_UP);
            }
        }
        mMhlDevices = Collections.emptyList();

        mHdmiCecNetwork.initPortInfo();
@@ -743,8 +766,16 @@ public class HdmiControlService extends SystemService {
                            mCecController.enableWakeupByOtp(tv().getAutoWakeup());
                        }
                    }
                },
                mServiceThreadExecutor);
                }, mServiceThreadExecutor);
        mHdmiCecConfig.registerChangeListener(HdmiControlManager.SETTING_NAME_EARC_ENABLED,
                new HdmiCecConfig.SettingChangeListener() {
                    @Override
                    public void onChange(String setting) {
                        @HdmiControlManager.HdmiCecControl int enabled = mHdmiCecConfig.getIntValue(
                                HdmiControlManager.SETTING_NAME_EARC_ENABLED);
                        setEarcEnabled(enabled);
                    }
                }, mServiceThreadExecutor);
    }

    /** Returns true if the device screen is off */
@@ -1083,7 +1114,8 @@ public class HdmiControlService extends SystemService {
    }

    @ServiceThreadOnly
    private void initializeCecLocalDevices(final int initiatedBy) {
    @VisibleForTesting
    protected void initializeCecLocalDevices(final int initiatedBy) {
        assertRunOnServiceThread();
        // A container for [Device type, Local device info].
        ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
@@ -2568,7 +2600,9 @@ public class HdmiControlService extends SystemService {
            if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");

            synchronized (mLock) {
                pw.println("mProhibitMode: " + mProhibitMode);
            }
            pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
            pw.println("mIsCecAvailable: " + mIsCecAvailable);
            pw.println("mCecVersion: " + mCecVersion);
@@ -2577,9 +2611,9 @@ public class HdmiControlService extends SystemService {
            // System settings
            pw.println("System_settings:");
            pw.increaseIndent();
            pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
            pw.println("mMhlInputChangeEnabled: " + isMhlInputChangeEnabled());
            pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
            pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControl);
            pw.println("mHdmiCecVolumeControlEnabled: " + getHdmiCecVolumeControl());
            pw.decreaseIndent();

            // CEC settings
@@ -2605,6 +2639,14 @@ public class HdmiControlService extends SystemService {
            pw.increaseIndent();
            mMhlController.dump(pw);
            pw.decreaseIndent();
            pw.print("eARC local device: ");
            pw.increaseIndent();
            if (mEarcLocalDevice == null) {
                pw.println("None. eARC is either disabled or not available.");
            } else {
                mEarcLocalDevice.dump(pw);
            }
            pw.decreaseIndent();
            mHdmiCecNetwork.dump(pw);
            if (mCecController != null) {
                pw.println("mCecController: ");
@@ -3313,6 +3355,12 @@ public class HdmiControlService extends SystemService {
        }
    }

    private boolean isEarcSupportedAndEnabled() {
        synchronized (mLock) {
            return mEarcSupported && mEarcEnabled;
        }
    }

    @ServiceThreadOnly
    int getPowerStatus() {
        assertRunOnServiceThread();
@@ -3384,7 +3432,7 @@ public class HdmiControlService extends SystemService {
        mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON,
                false);
        if (mCecController != null) {
            if (mHdmiControlEnabled == HDMI_CEC_CONTROL_ENABLED) {
            if (isCecControlEnabled()) {
                int startReason = -1;
                switch (wakeUpAction) {
                    case WAKE_UP_SCREEN_ON:
@@ -3406,6 +3454,22 @@ public class HdmiControlService extends SystemService {
        } else {
            Slog.i(TAG, "Device does not support HDMI-CEC.");
        }
        if (isEarcSupportedAndEnabled()) {
            int startReason = -1;
            switch (wakeUpAction) {
                case WAKE_UP_SCREEN_ON:
                    startReason = INITIATED_BY_SCREEN_ON;
                    break;
                case WAKE_UP_BOOT_UP:
                    startReason = INITIATED_BY_BOOT_UP;
                    break;
                default:
                    Slog.e(TAG, "wakeUpAction " + wakeUpAction + " not defined.");
                    return;

            }
            initializeEarc(startReason);
        }
        // TODO: Initialize MHL local devices.
    }

@@ -4296,4 +4360,85 @@ public class HdmiControlService extends SystemService {
        getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC,
                volume * mStreamMusicMaxVolume / AudioStatus.MAX_VOLUME, flags);
    }

    private void initializeEarc(int initiatedBy) {
        Slog.i(TAG, "eARC initialized, reason = " + initiatedBy);
        initializeEarcLocalDevice(initiatedBy);
    }

    @ServiceThreadOnly
    @VisibleForTesting
    protected void initializeEarcLocalDevice(final int initiatedBy) {
        // TODO remove initiatedBy argument if it stays unused
        assertRunOnServiceThread();
        if (isTvDevice() && mEarcLocalDevice == null) {
            mEarcLocalDevice = new HdmiEarcLocalDeviceTx();
        }
        // TODO create HdmiEarcLocalDeviceRx if we're an audio system device.
    }

    @ServiceThreadOnly
    @VisibleForTesting
    protected void setEarcEnabled(@HdmiControlManager.EarcFeature int enabled) {
        assertRunOnServiceThread();
        synchronized (mLock) {
            mEarcEnabled = (enabled == EARC_FEATURE_ENABLED);

            if (!mEarcSupported) {
                Slog.i(TAG, "Enabled/disabled eARC setting, but the hardware doesn´t support eARC."
                        + "This settings change doesn´t have an effect.");
                return;
            }

            if (mEarcEnabled) {
                onEnableEarc();
                return;
            }
        }
        runOnServiceThread(new Runnable() {
            @Override
            public void run() {
                onDisableEarc();
            }
        });
    }

    @VisibleForTesting
    protected void setEarcSupported(boolean supported) {
        synchronized (mLock) {
            mEarcSupported = supported;
        }
    }

    @ServiceThreadOnly
    private void onEnableEarc() {
        initializeEarc(INITIATED_BY_ENABLE_EARC);
    }

    @ServiceThreadOnly
    private void onDisableEarc() {
        clearEarcLocalDevice();
    }

    @ServiceThreadOnly
    @VisibleForTesting
    protected void clearEarcLocalDevice() {
        assertRunOnServiceThread();
        mEarcLocalDevice = null;
    }

    @ServiceThreadOnly
    @VisibleForTesting
    protected void addEarcLocalDevice(HdmiEarcLocalDevice localDevice) {
        assertRunOnServiceThread();
        mEarcLocalDevice = localDevice;
    }

    @ServiceThreadOnly
    @VisibleForTesting
    protected HdmiEarcLocalDevice getEarcLocalDevice() {
        assertRunOnServiceThread();
        return mEarcLocalDevice;
    }

}
+32 −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.util.IndentingPrintWriter;

/**
 * Class that models a local eARC device hosted in this system.
 * The class contains methods that are common between eARC TX and eARC RX devices.
 */
public class HdmiEarcLocalDevice {
    private static final String TAG = "HdmiEarcLocalDevice";

    /** Dump internal status of HdmiEarcLocalDevice object */
    protected void dump(final IndentingPrintWriter pw) {
        // Should be overridden in the more specific classes
    }
}
+32 −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.util.IndentingPrintWriter;

/**
 * Represents a local eARC device of type TX residing in the Android system.
 * Only TV panel devices can have a local eARC TX device.
 */
public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice {
    private static final String TAG = "HdmiEarcLocalDeviceTx";

    /** Dump internal status of HdmiEarcLocalDeviceTx object */
    protected void dump(final IndentingPrintWriter pw) {
        pw.println("TX");
    }
}
+32 −0
Original line number Diff line number Diff line
@@ -605,4 +605,36 @@ public class HdmiCecNetworkTest {

        assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
    }

    @Test
    public void disableCec_clearCecLocalDevices() {
        mHdmiCecNetwork.clearLocalDevices();
        mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_TV,
                new HdmiCecLocalDeviceTv(mHdmiControlService));

        assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
        assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
                HdmiCecLocalDeviceTv.class);
        mHdmiControlService.setCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
        mTestLooper.dispatchAll();

        assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(0);
    }

    @Test
    public void disableEarc_doNotClearCecLocalDevices() {
        mHdmiCecNetwork.clearLocalDevices();
        mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_TV,
                new HdmiCecLocalDeviceTv(mHdmiControlService));

        assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
        assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
                HdmiCecLocalDeviceTv.class);
        mHdmiControlService.setEarcEnabled(HdmiControlManager.EARC_FEATURE_DISABLED);
        mTestLooper.dispatchAll();

        assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
        assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
                HdmiCecLocalDeviceTv.class);
    }
}
+49 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -1082,6 +1083,54 @@ public class HdmiControlServiceTest {
        assertThat(mHdmiControlServiceSpy.audioSystem()).isNull();
    }

    @Test
    public void disableEarc_clearEarcLocalDevice() {
        mHdmiControlServiceSpy.setEarcSupported(true);
        mHdmiControlServiceSpy.clearEarcLocalDevice();
        mHdmiControlServiceSpy.addEarcLocalDevice(new HdmiEarcLocalDeviceTx());
        assertThat(mHdmiControlServiceSpy.getEarcLocalDevice()).isNotNull();

        mHdmiControlServiceSpy.setEarcEnabled(HdmiControlManager.EARC_FEATURE_DISABLED);
        mTestLooper.dispatchAll();
        assertThat(mHdmiControlServiceSpy.getEarcLocalDevice()).isNull();
    }

    @Test
    public void disableCec_doNotClearEarcLocalDevice() {
        mHdmiControlServiceSpy.setEarcSupported(true);
        mHdmiControlServiceSpy.clearEarcLocalDevice();
        mHdmiControlServiceSpy.addEarcLocalDevice(new HdmiEarcLocalDeviceTx());
        assertThat(mHdmiControlServiceSpy.getEarcLocalDevice()).isNotNull();

        mHdmiControlServiceSpy.setCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
        mTestLooper.dispatchAll();
        assertThat(mHdmiControlServiceSpy.getEarcLocalDevice()).isNotNull();
    }

    @Test
    public void enableCec_initializeCecLocalDevices() {
        mHdmiControlServiceSpy.setEarcSupported(true);
        Mockito.clearInvocations(mHdmiControlServiceSpy);
        mHdmiControlServiceSpy.setCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
        mTestLooper.dispatchAll();
        mHdmiControlServiceSpy.setCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
        mTestLooper.dispatchAll();
        verify(mHdmiControlServiceSpy, times(1)).initializeCecLocalDevices(anyInt());
        verify(mHdmiControlServiceSpy, times(0)).initializeEarcLocalDevice(anyInt());
    }

    @Test
    public void enableEarc_initializeEarcLocalDevices() {
        mHdmiControlServiceSpy.setEarcSupported(true);
        Mockito.clearInvocations(mHdmiControlServiceSpy);
        mHdmiControlServiceSpy.setEarcEnabled(HdmiControlManager.EARC_FEATURE_DISABLED);
        mTestLooper.dispatchAll();
        mHdmiControlServiceSpy.setEarcEnabled(HdmiControlManager.EARC_FEATURE_ENABLED);
        mTestLooper.dispatchAll();
        verify(mHdmiControlServiceSpy, times(0)).initializeCecLocalDevices(anyInt());
        verify(mHdmiControlServiceSpy, times(1)).initializeEarcLocalDevice(anyInt());
    }

    protected static class MockPlaybackDevice extends HdmiCecLocalDevicePlayback {

        private boolean mCanGoToStandby;