Update DeviceStateProviderImpl to read from device state config file.
This change introduces the device_state_configuration.xml file schema and updates DeviceStateProviderImpl to read the set of supported states from the configuration file. The file schema and provider only support the lid switch condition, support for hinge angle will be added in a follow-up change. Test: atest DeviceStateProviderImplTest Test: manual - place config on device and verify device state changes with lid switch Bug: 159401800 Change-Id: I4472e23a5c8dbdacfe56aa2570eafa84033a7bfd
This commit is contained in:
@@ -74,6 +74,7 @@ java_library_static {
|
||||
":platform-compat-config",
|
||||
":display-device-config",
|
||||
":cec-config",
|
||||
":device-state-config",
|
||||
"java/com/android/server/EventLogTags.logtags",
|
||||
"java/com/android/server/am/EventLogTags.logtags",
|
||||
"java/com/android/server/wm/EventLogTags.logtags",
|
||||
|
||||
@@ -30,7 +30,7 @@ public final class DeviceStatePolicyImpl implements DeviceStatePolicy {
|
||||
private final DeviceStateProvider mProvider;
|
||||
|
||||
public DeviceStatePolicyImpl() {
|
||||
mProvider = new DeviceStateProviderImpl();
|
||||
mProvider = DeviceStateProviderImpl.create();
|
||||
}
|
||||
|
||||
public DeviceStateProvider getDeviceStateProvider() {
|
||||
|
||||
@@ -16,30 +16,285 @@
|
||||
|
||||
package com.android.server.policy;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.hardware.input.InputManagerInternal;
|
||||
import android.os.Environment;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.devicestate.DeviceStateProvider;
|
||||
import com.android.server.policy.devicestate.config.Conditions;
|
||||
import com.android.server.policy.devicestate.config.DeviceState;
|
||||
import com.android.server.policy.devicestate.config.DeviceStateConfig;
|
||||
import com.android.server.policy.devicestate.config.LidSwitchCondition;
|
||||
import com.android.server.policy.devicestate.config.XmlParser;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import javax.xml.datatype.DatatypeConfigurationException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link DeviceStateProvider}. Currently only supports
|
||||
* {@link #DEFAULT_DEVICE_STATE}.
|
||||
*
|
||||
* @see DeviceStatePolicyImpl
|
||||
* Implementation of {@link DeviceStateProvider} that reads the set of supported device states
|
||||
* from a configuration file provided at either /vendor/etc/devicestate or
|
||||
* /data/system/devicestate/. By default, the provider supports {@link #DEFAULT_DEVICE_STATE} when
|
||||
* no configuration is provided.
|
||||
*/
|
||||
final class DeviceStateProviderImpl implements DeviceStateProvider {
|
||||
private static final int DEFAULT_DEVICE_STATE = 0;
|
||||
public final class DeviceStateProviderImpl implements DeviceStateProvider,
|
||||
InputManagerInternal.LidSwitchCallback {
|
||||
private static final String TAG = "DeviceStateProviderImpl";
|
||||
|
||||
private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int DEFAULT_DEVICE_STATE = 0;
|
||||
|
||||
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
|
||||
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
|
||||
private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
|
||||
|
||||
/** Interface that allows reading the device state configuration. */
|
||||
interface ReadableConfig {
|
||||
@NonNull
|
||||
InputStream openRead() throws IOException;
|
||||
}
|
||||
|
||||
/** Returns a new {@link DeviceStateProviderImpl} instance. */
|
||||
public static DeviceStateProviderImpl create() {
|
||||
File configFile = getConfigurationFile();
|
||||
if (configFile == null) {
|
||||
return createFromConfig(null);
|
||||
}
|
||||
return createFromConfig(new ReadableFileConfig(configFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link DeviceStateProviderImpl} instance.
|
||||
*
|
||||
* @param readableConfig the config the provider instance should read supported states from.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static DeviceStateProviderImpl createFromConfig(@Nullable ReadableConfig readableConfig) {
|
||||
SparseArray<Conditions> conditionsForState = new SparseArray<>();
|
||||
if (readableConfig != null) {
|
||||
DeviceStateConfig config = parseConfig(readableConfig);
|
||||
if (config != null) {
|
||||
for (DeviceState stateConfig : config.getDeviceState()) {
|
||||
int state = stateConfig.getIdentifier().intValue();
|
||||
Conditions conditions = stateConfig.getConditions();
|
||||
conditionsForState.put(state, conditions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conditionsForState.size() == 0) {
|
||||
conditionsForState.put(DEFAULT_DEVICE_STATE, null);
|
||||
}
|
||||
return new DeviceStateProviderImpl(conditionsForState);
|
||||
}
|
||||
|
||||
// Lock for internal state.
|
||||
private final Object mLock = new Object();
|
||||
// List of supported states in ascending order.
|
||||
private final int[] mOrderedStates;
|
||||
// Map of state 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;
|
||||
|
||||
@Nullable
|
||||
@GuardedBy("mLock")
|
||||
private Listener mListener = null;
|
||||
@GuardedBy("mLock")
|
||||
private int mLastReportedState = INVALID_DEVICE_STATE;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsLidOpen;
|
||||
|
||||
private DeviceStateProviderImpl(SparseArray<Conditions> conditionsForState) {
|
||||
mOrderedStates = new int[conditionsForState.size()];
|
||||
for (int i = 0; i < conditionsForState.size(); i++) {
|
||||
mOrderedStates[i] = conditionsForState.keyAt(i);
|
||||
}
|
||||
|
||||
// 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.
|
||||
boolean shouldListenToLidSwitch = false;
|
||||
|
||||
mStateConditions = new SparseArray<>();
|
||||
for (int i = 0; i < mOrderedStates.length; i++) {
|
||||
int state = mOrderedStates[i];
|
||||
Conditions conditions = conditionsForState.get(state);
|
||||
if (conditions == null) {
|
||||
mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
|
||||
continue;
|
||||
}
|
||||
|
||||
LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch();
|
||||
if (lidSwitchCondition == null) {
|
||||
// We currently only support the lid switch so if it doesn't exist the condition
|
||||
// is always true.
|
||||
mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
|
||||
continue;
|
||||
}
|
||||
|
||||
mStateConditions.put(state, new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
|
||||
shouldListenToLidSwitch = true;
|
||||
}
|
||||
|
||||
if (shouldListenToLidSwitch) {
|
||||
InputManagerInternal inputManager = LocalServices.getService(
|
||||
InputManagerInternal.class);
|
||||
inputManager.registerLidSwitchCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(Listener listener) {
|
||||
if (mListener != null) {
|
||||
throw new RuntimeException("Provider already has a listener set.");
|
||||
synchronized (mLock) {
|
||||
if (mListener != null) {
|
||||
throw new RuntimeException("Provider already has a listener set.");
|
||||
}
|
||||
mListener = listener;
|
||||
}
|
||||
notifySupportedStatesChanged();
|
||||
notifyDeviceStateChangedIfNeeded();
|
||||
}
|
||||
|
||||
/** Notifies the listener that the set of supported device states has changed. */
|
||||
private void notifySupportedStatesChanged() {
|
||||
int[] supportedStates;
|
||||
synchronized (mLock) {
|
||||
if (mListener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
supportedStates = Arrays.copyOf(mOrderedStates, mOrderedStates.length);
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
mListener.onSupportedDeviceStatesChanged(new int[]{ DEFAULT_DEVICE_STATE });
|
||||
mListener.onStateChanged(DEFAULT_DEVICE_STATE);
|
||||
mListener.onSupportedDeviceStatesChanged(supportedStates);
|
||||
}
|
||||
|
||||
/** Computes the current device state and notifies the listener of a change, if needed. */
|
||||
void notifyDeviceStateChangedIfNeeded() {
|
||||
int stateToReport = INVALID_DEVICE_STATE;
|
||||
synchronized (mLock) {
|
||||
if (mListener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newState = mOrderedStates[0];
|
||||
for (int i = 1; i < mOrderedStates.length; i++) {
|
||||
int state = mOrderedStates[i];
|
||||
if (mStateConditions.get(state).getAsBoolean()) {
|
||||
newState = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newState != mLastReportedState) {
|
||||
mLastReportedState = newState;
|
||||
stateToReport = newState;
|
||||
}
|
||||
}
|
||||
|
||||
if (stateToReport != INVALID_DEVICE_STATE) {
|
||||
mListener.onStateChanged(stateToReport);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
|
||||
synchronized (mLock) {
|
||||
mIsLidOpen = lidOpen;
|
||||
}
|
||||
notifyDeviceStateChangedIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link BooleanSupplier} that returns {@code true} if the expected lid
|
||||
* switch open state matches {@link #mIsLidOpen}.
|
||||
*/
|
||||
private final class LidSwitchBooleanSupplier implements BooleanSupplier {
|
||||
private final boolean mExpectedOpen;
|
||||
|
||||
LidSwitchBooleanSupplier(boolean expectedOpen) {
|
||||
mExpectedOpen = expectedOpen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAsBoolean() {
|
||||
synchronized (mLock) {
|
||||
return mIsLidOpen == mExpectedOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device state configuration file that should be used, or {@code null} if no file
|
||||
* is present on the device.
|
||||
* <p>
|
||||
* Defaults to returning a config file present in the data/ dir at
|
||||
* {@link #DATA_CONFIG_FILE_PATH}, and then falls back to the config file in the vendor/ dir
|
||||
* at {@link #VENDOR_CONFIG_FILE_PATH} if no config file is found in the data/ dir.
|
||||
*/
|
||||
@Nullable
|
||||
private static File getConfigurationFile() {
|
||||
final File configFileFromDataDir = Environment.buildPath(Environment.getDataDirectory(),
|
||||
DATA_CONFIG_FILE_PATH, CONFIG_FILE_NAME);
|
||||
if (configFileFromDataDir.exists()) {
|
||||
return configFileFromDataDir;
|
||||
}
|
||||
|
||||
final File configFileFromVendorDir = Environment.buildPath(Environment.getVendorDirectory(),
|
||||
VENDOR_CONFIG_FILE_PATH, CONFIG_FILE_NAME);
|
||||
if (configFileFromVendorDir.exists()) {
|
||||
return configFileFromVendorDir;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse the provided file into a {@link DeviceStateConfig} object. Returns
|
||||
* {@code null} if the file could not be successfully parsed.
|
||||
*/
|
||||
@Nullable
|
||||
private static DeviceStateConfig parseConfig(@NonNull ReadableConfig readableConfig) {
|
||||
try (InputStream in = readableConfig.openRead();
|
||||
InputStream bin = new BufferedInputStream(in)) {
|
||||
return XmlParser.read(bin);
|
||||
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
|
||||
Slog.e(TAG, "Encountered an error while reading device state config", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Implementation of {@link ReadableConfig} that reads config data from a file. */
|
||||
private static final class ReadableFileConfig implements ReadableConfig {
|
||||
@NonNull
|
||||
private final File mFile;
|
||||
|
||||
private ReadableFileConfig(@NonNull File file) {
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openRead() throws IOException {
|
||||
return new FileInputStream(mFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,3 +28,10 @@ xsd_config {
|
||||
api_dir: "cec-config/schema",
|
||||
package_name: "com.android.server.hdmi.cec.config",
|
||||
}
|
||||
|
||||
xsd_config {
|
||||
name: "device-state-config",
|
||||
srcs: ["device-state-config/device-state-config.xsd"],
|
||||
api_dir: "device-state-config/schema",
|
||||
package_name: "com.android.server.policy.devicestate.config",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<xs:schema version="2.0"
|
||||
elementFormDefault="qualified"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:element name="device-state-config">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="device-state" type="deviceState" maxOccurs="256" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:complexType name="deviceState">
|
||||
<xs:sequence>
|
||||
<xs:element name="identifier">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="0" />
|
||||
<xs:maxInclusive value="255" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="conditions" type="conditions" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="conditions">
|
||||
<xs:sequence>
|
||||
<xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="lidSwitchCondition">
|
||||
<xs:sequence>
|
||||
<xs:element name="open" type="xs:boolean" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
||||
39
services/core/xsd/device-state-config/schema/current.txt
Normal file
39
services/core/xsd/device-state-config/schema/current.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
// Signature format: 2.0
|
||||
package com.android.server.policy.devicestate.config {
|
||||
|
||||
public class Conditions {
|
||||
ctor public Conditions();
|
||||
method public com.android.server.policy.devicestate.config.LidSwitchCondition getLidSwitch();
|
||||
method public void setLidSwitch(com.android.server.policy.devicestate.config.LidSwitchCondition);
|
||||
}
|
||||
|
||||
public class DeviceState {
|
||||
ctor public DeviceState();
|
||||
method public com.android.server.policy.devicestate.config.Conditions getConditions();
|
||||
method public java.math.BigInteger getIdentifier();
|
||||
method public String getName();
|
||||
method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
|
||||
method public void setIdentifier(java.math.BigInteger);
|
||||
method public void setName(String);
|
||||
}
|
||||
|
||||
public class DeviceStateConfig {
|
||||
ctor public DeviceStateConfig();
|
||||
method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
|
||||
}
|
||||
|
||||
public class LidSwitchCondition {
|
||||
ctor public LidSwitchCondition();
|
||||
method public boolean getOpen();
|
||||
method public void setOpen(boolean);
|
||||
}
|
||||
|
||||
public class XmlParser {
|
||||
ctor public XmlParser();
|
||||
method public static com.android.server.policy.devicestate.config.DeviceStateConfig read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||
method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||
method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1
services/core/xsd/device-state-config/schema/removed.txt
Normal file
1
services/core/xsd/device-state-config/schema/removed.txt
Normal file
@@ -0,0 +1 @@
|
||||
// Signature format: 2.0
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.policy;
|
||||
|
||||
|
||||
import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.hardware.input.InputManagerInternal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.devicestate.DeviceStateProvider;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DeviceStateProviderImpl}.
|
||||
* <p/>
|
||||
* Run with <code>atest DeviceStateProviderImplTest</code>.
|
||||
*/
|
||||
public final class DeviceStateProviderImplTest {
|
||||
private final ArgumentCaptor<int[]> mIntArrayCaptor = ArgumentCaptor.forClass(int[].class);
|
||||
private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
LocalServices.removeServiceForTest(InputManagerInternal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create_noConfig() {
|
||||
assertDefaultProviderValues(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create_emptyFile() {
|
||||
String configString = "";
|
||||
DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
|
||||
|
||||
assertDefaultProviderValues(config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create_emptyConfig() {
|
||||
String configString = "<device-state-config></device-state-config>";
|
||||
DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
|
||||
|
||||
assertDefaultProviderValues(config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create_invalidConfig() {
|
||||
String configString = "<device-state-config>\n"
|
||||
+ " </device-state>\n"
|
||||
+ "</device-state-config>\n";
|
||||
DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
|
||||
|
||||
assertDefaultProviderValues(config);
|
||||
}
|
||||
|
||||
private void assertDefaultProviderValues(
|
||||
@Nullable DeviceStateProviderImpl.ReadableConfig config) {
|
||||
DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(config);
|
||||
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
|
||||
assertArrayEquals(new int[] { DEFAULT_DEVICE_STATE }, mIntArrayCaptor.getValue());
|
||||
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(DEFAULT_DEVICE_STATE, mIntegerCaptor.getValue().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create_lidSwitch() {
|
||||
String configString = "<device-state-config>\n"
|
||||
+ " <device-state>\n"
|
||||
+ " <identifier>1</identifier>\n"
|
||||
+ " <conditions>\n"
|
||||
+ " <lid-switch>\n"
|
||||
+ " <open>true</open>\n"
|
||||
+ " </lid-switch>\n"
|
||||
+ " </conditions>\n"
|
||||
+ " </device-state>\n"
|
||||
+ " <device-state>\n"
|
||||
+ " <identifier>2</identifier>\n"
|
||||
+ " <conditions>\n"
|
||||
+ " <lid-switch>\n"
|
||||
+ " <open>false</open>\n"
|
||||
+ " </lid-switch>\n"
|
||||
+ " </conditions>\n"
|
||||
+ " </device-state>\n"
|
||||
+ "</device-state-config>\n";
|
||||
DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
|
||||
DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(config);
|
||||
|
||||
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
|
||||
provider.setListener(listener);
|
||||
|
||||
verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
|
||||
assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
|
||||
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(2, mIntegerCaptor.getValue().intValue());
|
||||
|
||||
Mockito.clearInvocations(listener);
|
||||
|
||||
provider.notifyLidSwitchChanged(0, true /* lidOpen */);
|
||||
|
||||
verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
|
||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||
assertEquals(1, mIntegerCaptor.getValue().intValue());
|
||||
}
|
||||
|
||||
private static final class TestReadableConfig implements
|
||||
DeviceStateProviderImpl.ReadableConfig {
|
||||
private final byte[] mData;
|
||||
|
||||
TestReadableConfig(String configFileData) {
|
||||
mData = configFileData.getBytes();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InputStream openRead() throws IOException {
|
||||
return new ByteArrayInputStream(mData);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user