From 95a0e33d49e616a49101e24d8b29f111fb4f2112 Mon Sep 17 00:00:00 2001 From: Jiaming Liu Date: Wed, 11 Jan 2023 04:54:11 +0000 Subject: [PATCH] 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 --- .../server/devicestate/DeviceState.java | 9 ++- .../policy/DeviceStateProviderImpl.java | 77 +++++++++++++++++-- .../policy/DeviceStateProviderImplTest.java | 65 +++++++++++++++- 3 files changed, 144 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java index 6baa1bd2ae20c..a589313a1e921 100644 --- a/services/core/java/com/android/server/devicestate/DeviceState.java +++ b/services/core/java/com/android/server/devicestate/DeviceState.java @@ -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 {} diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index f6497d7f8091b..7f733efef8a23 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -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 mLatestSensorEvent = new ArrayMap<>(); + @GuardedBy("mLock") + private @PowerManager.ThermalStatus int mThermalStatus = PowerManager.THERMAL_STATUS_NONE; private DeviceStateProviderImpl(@NonNull Context context, @NonNull List 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 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 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 deviceStates) { + for (DeviceState state : deviceStates) { + if (state.hasFlag(DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) { + return true; + } + } + return false; + } } diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java index 261156611a06f..7fac9b6081141 100644 --- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -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 { + " \n" + " \n" + " \n" + + " \n" + + " 4\n" + + " THERMAL_TEST\n" + + " \n" + + " FLAG_EMULATED_ONLY\n" + + " FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL\n" + + " \n" + + " \n" + "\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.