Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f89a5e33 authored by Darryl L Johnson's avatar Darryl L Johnson
Browse files

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
parent 062dde5e
Loading
Loading
Loading
Loading
+37 −13
Original line number Diff line number Diff line
@@ -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<BooleanSupplier> mStateConditions;
    private final SparseArray<BooleanSupplier> 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<DeviceState> deviceStates,
            @NonNull List<Conditions> 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<Sensor> 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<Sensor> sensorsRequired = new ArraySet<>();

            List<BooleanSupplier> suppliers = new ArrayList<>();

            LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch();
            if (lidSwitchCondition != null) {
                suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
                shouldListenToLidSwitch = true;
                lidSwitchRequired = true;
            }

            List<SensorCondition> sensorConditions = conditions.getSensor();
@@ -210,14 +223,20 @@ 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 (allRequiredComponentsFound) {
                shouldListenToLidSwitch |= lidSwitchRequired;
                sensorsToListenTo.addAll(sensorsRequired);

                if (suppliers.size() > 1) {
                    mStateConditions.put(state, new AndBooleanSupplier(suppliers));
                } else if (suppliers.size() > 0) {
@@ -227,6 +246,11 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
                    // There are no conditions for this state. Default to always true.
                    mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
                }
            } else {
                // 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);
            }
        }

        if (shouldListenToLidSwitch) {
+50 −0
Original line number Diff line number Diff line
@@ -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 = "<device-state-config>\n"
                + "    <device-state>\n"
                + "        <identifier>1</identifier>\n"
                + "        <name>CLOSED</name>\n"
                + "        <conditions>\n"
                + "            <sensor>\n"
                + "                <type>" + sensor.getStringType() + "</type>\n"
                + "                <name>" + sensor.getName() + "</name>\n"
                + "                <value>\n"
                + "                    <max>90</max>\n"
                + "                </value>\n"
                + "            </sensor>\n"
                + "        </conditions>\n"
                + "    </device-state>\n"
                + "    <device-state>\n"
                + "        <identifier>2</identifier>\n"
                + "        <name>HALF_OPENED</name>\n"
                + "        <conditions>\n"
                + "            <sensor>\n"
                + "                <type>" + sensor.getStringType() + "</type>\n"
                + "                <name>" + sensor.getName() + "</name>\n"
                + "                <value>\n"
                + "                    <min-inclusive>90</min-inclusive>\n"
                + "                    <max>180</max>\n"
                + "                </value>\n"
                + "            </sensor>\n"
                + "        </conditions>\n"
                + "    </device-state>\n"
                + "</device-state-config>\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<Sensor> constructor = Sensor.class.getDeclaredConstructor();
        constructor.setAccessible(true);