From f89a5e337d5e473b5328afa88ea647525aaf505b Mon Sep 17 00:00:00 2001 From: Darryl L Johnson Date: Thu, 22 Apr 2021 17:03:48 -0700 Subject: [PATCH] Don't throw exception if DeviceStateProviderImpl can't find required sensor. DeviceStateProviderImpl would thrown an exception when constructed if the config it is based on required the presence of a Sensor that did not exist. This changes the provider to ignore states with conditions that have unknown sensors. If there are no states with known conditions it defaults to the state with the lowest identifier. Fixes: 186166148 Test: atest DeviceStateProviderImplTest Change-Id: Iec73e33e2b464680fb9404863d11a26e9643248b --- .../policy/DeviceStateProviderImpl.java | 50 ++++++++++++++----- .../policy/DeviceStateProviderImplTest.java | 50 +++++++++++++++++++ 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index cd7f6854a37df..204e5e3e77649 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -83,6 +83,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, private static final String TAG = "DeviceStateProviderImpl"; private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true; + private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false; @VisibleForTesting static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE, @@ -152,7 +153,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, private final DeviceState[] mOrderedStates; // Map of state identifier to a boolean supplier that returns true when all required conditions // are met for the device to be in the state. - private final SparseArray mStateConditions; + private final SparseArray mStateConditions = new SparseArray<>(); @Nullable @GuardedBy("mLock") @@ -177,6 +178,11 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier)); mOrderedStates = orderedStates; + setStateConditions(deviceStates, stateConditions); + } + + private void setStateConditions(@NonNull List deviceStates, + @NonNull List stateConditions) { // Whether or not this instance should register to receive lid switch notifications from // InputManagerInternal. If there are no device state conditions that are based on the lid // switch there is no need to register for a callback. @@ -185,7 +191,6 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from. final ArraySet sensorsToListenTo = new ArraySet<>(); - mStateConditions = new SparseArray<>(); for (int i = 0; i < stateConditions.size(); i++) { final int state = deviceStates.get(i).getIdentifier(); final Conditions conditions = stateConditions.get(i); @@ -194,12 +199,20 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, continue; } + // Whether or not all the required hardware components could be found that match the + // requirements from the config. + boolean allRequiredComponentsFound = true; + // Whether or not this condition requires the lid switch. + boolean lidSwitchRequired = false; + // Set of sensors required for this condition. + ArraySet sensorsRequired = new ArraySet<>(); + List suppliers = new ArrayList<>(); LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch(); if (lidSwitchCondition != null) { suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen())); - shouldListenToLidSwitch = true; + lidSwitchRequired = true; } List sensorConditions = conditions.getSensor(); @@ -210,22 +223,33 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName); if (foundSensor == null) { - throw new IllegalStateException("Failed to find Sensor with type: " - + expectedSensorType + " and name: " + expectedSensorName); + Slog.e(TAG, "Failed to find Sensor with type: " + expectedSensorType + + " and name: " + expectedSensorName); + allRequiredComponentsFound = false; + break; } suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue())); - sensorsToListenTo.add(foundSensor); + sensorsRequired.add(foundSensor); } - if (suppliers.size() > 1) { - mStateConditions.put(state, new AndBooleanSupplier(suppliers)); - } else if (suppliers.size() > 0) { - // No need to wrap with an AND supplier if there is only 1. - mStateConditions.put(state, suppliers.get(0)); + if (allRequiredComponentsFound) { + shouldListenToLidSwitch |= lidSwitchRequired; + sensorsToListenTo.addAll(sensorsRequired); + + if (suppliers.size() > 1) { + mStateConditions.put(state, new AndBooleanSupplier(suppliers)); + } else if (suppliers.size() > 0) { + // No need to wrap with an AND supplier if there is only 1. + mStateConditions.put(state, suppliers.get(0)); + } else { + // There are no conditions for this state. Default to always true. + mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER); + } } else { - // There are no conditions for this state. Default to always true. - mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER); + // Failed to setup this condition. This can happen if a sensor is missing. Default + // this state to always false. + mStateConditions.put(state, FALSE_BOOLEAN_SUPPLIER); } } 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 d13687ce32548..4d2d2f1a4b7d8 100644 --- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -307,6 +307,56 @@ public final class DeviceStateProviderImplTest { assertEquals(1, mIntegerCaptor.getValue().intValue()); } + @Test + public void create_invalidSensor() throws Exception { + Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE); + when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of()); + + String configString = "\n" + + " \n" + + " 1\n" + + " CLOSED\n" + + " \n" + + " \n" + + " " + sensor.getStringType() + "\n" + + " " + sensor.getName() + "\n" + + " \n" + + " 90\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 2\n" + + " HALF_OPENED\n" + + " \n" + + " \n" + + " " + sensor.getStringType() + "\n" + + " " + sensor.getName() + "\n" + + " \n" + + " 90\n" + + " 180\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString); + DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext, + config); + + DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class); + provider.setListener(listener); + + verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); + assertArrayEquals( + new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"), + }, mDeviceStateArrayCaptor.getValue()); + // onStateChanged() should be called because the provider could not find the sensor. + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + } + private static Sensor newSensor(String name, String type) throws Exception { Constructor constructor = Sensor.class.getDeclaredConstructor(); constructor.setAccessible(true);