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

Commit 95a0e33d authored by Jiaming Liu's avatar Jiaming Liu
Browse files

Update supported states when device has critical thermal status

Update supported states when the devices enters (or exits) the thermal
critical status. The device states marked with the flag will be disabled
when the device is overheating and removed from the supported states
list.

Bug: 264799106
Test: atest com.android.server.policy.DeviceStateProviderImplTest

Change-Id: I0623e5ca8a47664bf86b9e49fc2297c14024631e
parent 737b6984
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -72,12 +72,19 @@ public final class DeviceState {
     */
    public static final int FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 1 << 3;

    /**
     * This flag indicates that the corresponding state should be disabled when the device is
     * overheating and reaching the critical status.
     */
    public static final int FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;

    /** @hide */
    @IntDef(prefix = {"FLAG_"}, flag = true, value = {
            FLAG_CANCEL_OVERRIDE_REQUESTS,
            FLAG_APP_INACCESSIBLE,
            FLAG_EMULATED_ONLY,
            FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP
            FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
            FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface DeviceStateFlags {}
+72 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Environment;
import android.os.PowerManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -80,7 +81,8 @@ import javax.xml.datatype.DatatypeConfigurationException;
 * provided.
 */
public final class DeviceStateProviderImpl implements DeviceStateProvider,
        InputManagerInternal.LidSwitchCallback, SensorEventListener {
        InputManagerInternal.LidSwitchCallback, SensorEventListener,
        PowerManager.OnThermalStatusChangedListener {
    private static final String TAG = "DeviceStateProviderImpl";
    private static final boolean DEBUG = false;

@@ -99,6 +101,8 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
    private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
    private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
            "FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
    private static final String FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL =
            "FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL";

    /** Interface that allows reading the device state configuration. */
    interface ReadableConfig {
@@ -158,6 +162,9 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
                                case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
                                    flags |= DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP;
                                    break;
                                case FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL:
                                    flags |= DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL;
                                    break;
                                default:
                                    Slog.w(TAG, "Parsed unknown flag with name: "
                                            + configFlagString);
@@ -200,6 +207,8 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
    private Boolean mIsLidOpen;
    @GuardedBy("mLock")
    private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>();
    @GuardedBy("mLock")
    private @PowerManager.ThermalStatus int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;

    private DeviceStateProviderImpl(@NonNull Context context,
            @NonNull List<DeviceState> deviceStates,
@@ -214,6 +223,16 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
        mOrderedStates = orderedStates;

        setStateConditions(deviceStates, stateConditions);

        // If any of the device states are thermal sensitive, i.e. it should be disabled when the
        // device is overheating, then we will update the list of supported states when thermal
        // status changes.
        if (hasThermalSensitiveState(deviceStates)) {
            PowerManager powerManager = context.getSystemService(PowerManager.class);
            if (powerManager != null) {
                powerManager.addThermalStatusListener(this);
            }
        }
    }

    private void setStateConditions(@NonNull List<DeviceState> deviceStates,
@@ -353,16 +372,25 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,

    /** Notifies the listener that the set of supported device states has changed. */
    private void notifySupportedStatesChanged() {
        DeviceState[] supportedStates;
        List<DeviceState> supportedStates = new ArrayList<>();
        Listener listener;
        synchronized (mLock) {
            if (mListener == null) {
                return;
            }

            supportedStates = Arrays.copyOf(mOrderedStates, mOrderedStates.length);
            listener = mListener;
            for (DeviceState deviceState : mOrderedStates) {
                if (isThermalStatusCriticalOrAbove(mThermalStatus)
                        && deviceState.hasFlag(
                                DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) {
                    continue;
                }
                supportedStates.add(deviceState);
            }
        }

        mListener.onSupportedDeviceStatesChanged(supportedStates);
        listener.onSupportedDeviceStatesChanged(
                supportedStates.toArray(new DeviceState[supportedStates.size()]));
    }

    /** Computes the current device state and notifies the listener of a change, if needed. */
@@ -645,4 +673,43 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
            return new FileInputStream(mFile);
        }
    }

    @Override
    public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) {
        int previousThermalStatus;
        synchronized (mLock) {
            previousThermalStatus = mThermalStatus;
            mThermalStatus = thermalStatus;
        }

        boolean isThermalStatusCriticalOrAbove = isThermalStatusCriticalOrAbove(thermalStatus);
        boolean isPreviousThermalStatusCriticalOrAbove =
                isThermalStatusCriticalOrAbove(previousThermalStatus);
        if (isThermalStatusCriticalOrAbove != isPreviousThermalStatusCriticalOrAbove) {
            Slog.i(TAG, "Updating supported device states due to thermal status change."
                    + " isThermalStatusCriticalOrAbove: " + isThermalStatusCriticalOrAbove);
            notifySupportedStatesChanged();
        }
    }

    private static boolean isThermalStatusCriticalOrAbove(
            @PowerManager.ThermalStatus int thermalStatus) {
        switch (thermalStatus) {
            case PowerManager.THERMAL_STATUS_CRITICAL:
            case PowerManager.THERMAL_STATUS_EMERGENCY:
            case PowerManager.THERMAL_STATUS_SHUTDOWN:
                return true;
            default:
                return false;
        }
    }

    private static boolean hasThermalSensitiveState(List<DeviceState> deviceStates) {
        for (DeviceState state : deviceStates) {
            if (state.hasFlag(DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) {
                return true;
            }
        }
        return false;
    }
}
+64 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.os.PowerManager;

import androidx.annotation.NonNull;

@@ -312,6 +313,14 @@ public final class DeviceStateProviderImplTest {
                + "            </sensor>\n"
                + "        </conditions>\n"
                + "    </device-state>\n"
                + "    <device-state>\n"
                + "        <identifier>4</identifier>\n"
                + "        <name>THERMAL_TEST</name>\n"
                + "        <flags>\n"
                + "            <flag>FLAG_EMULATED_ONLY</flag>\n"
                + "            <flag>FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL</flag>\n"
                + "        </flags>\n"
                + "    </device-state>\n"
                + "</device-state-config>\n";
        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
        return DeviceStateProviderImpl.createFromConfig(mContext,
@@ -332,7 +341,10 @@ public final class DeviceStateProviderImplTest {
                new DeviceState[]{
                        new DeviceState(1, "CLOSED", 0 /* flags */),
                        new DeviceState(2, "HALF_OPENED", 0 /* flags */),
                        new DeviceState(3, "OPENED", 0 /* flags */) },
                        new DeviceState(3, "OPENED", 0 /* flags */),
                        new DeviceState(4, "THERMAL_TEST",
                                DeviceState.FLAG_EMULATED_ONLY
                                        | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
                mDeviceStateArrayCaptor.getValue());
        // onStateChanged() should not be called because the provider has not yet been notified of
        // the initial sensor state.
@@ -375,6 +387,57 @@ public final class DeviceStateProviderImplTest {
        assertEquals(1, mIntegerCaptor.getValue().intValue());
    }

    @Test
    public void test_flagDisableWhenThermalStatusCritical() throws Exception {
        Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
        when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
        DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);

        provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
        DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
        provider.setListener(listener);

        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
        assertArrayEquals(
                new DeviceState[]{
                        new DeviceState(1, "CLOSED", 0 /* flags */),
                        new DeviceState(2, "HALF_OPENED", 0 /* flags */),
                        new DeviceState(3, "OPENED", 0 /* flags */),
                        new DeviceState(4, "THERMAL_TEST",
                                DeviceState.FLAG_EMULATED_ONLY
                                        | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
                mDeviceStateArrayCaptor.getValue());
        Mockito.clearInvocations(listener);

        provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_MODERATE);
        verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
        Mockito.clearInvocations(listener);

        // The THERMAL_TEST state should be disabled.
        provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_CRITICAL);
        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
        assertArrayEquals(
                new DeviceState[]{
                        new DeviceState(1, "CLOSED", 0 /* flags */),
                        new DeviceState(2, "HALF_OPENED", 0 /* flags */),
                        new DeviceState(3, "OPENED", 0 /* flags */) },
                mDeviceStateArrayCaptor.getValue());
        Mockito.clearInvocations(listener);

        // The THERMAL_TEST state should be re-enabled.
        provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
        assertArrayEquals(
                new DeviceState[]{
                        new DeviceState(1, "CLOSED", 0 /* flags */),
                        new DeviceState(2, "HALF_OPENED", 0 /* flags */),
                        new DeviceState(3, "OPENED", 0 /* flags */),
                        new DeviceState(4, "THERMAL_TEST",
                                DeviceState.FLAG_EMULATED_ONLY
                                        | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
                mDeviceStateArrayCaptor.getValue());
    }

    @Test
    public void test_invalidSensorValues() throws Exception {
        // onStateChanged() should not be triggered by invalid sensor values.