Merge "Update supported states when device has critical thermal status"

This commit is contained in:
Jiaming Liu
2023-01-19 04:12:25 +00:00
committed by Android (Google) Code Review
3 changed files with 144 additions and 7 deletions

View File

@@ -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 {}

View File

@@ -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;
}
}

View File

@@ -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.