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.