Loading services/core/java/com/android/server/devicestate/DeviceState.java +8 −1 Original line number Diff line number Diff line Loading @@ -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 {} Loading services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +72 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -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, Loading Loading @@ -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. */ Loading Loading @@ -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; } } services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +64 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading @@ -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. Loading Loading @@ -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. Loading Loading
services/core/java/com/android/server/devicestate/DeviceState.java +8 −1 Original line number Diff line number Diff line Loading @@ -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 {} Loading
services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +72 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -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, Loading Loading @@ -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. */ Loading Loading @@ -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; } }
services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +64 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading @@ -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. Loading Loading @@ -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. Loading