From 7dde391fc1153d008b1bc84d40a184ace6667ca3 Mon Sep 17 00:00:00 2001 From: Darryl L Johnson Date: Fri, 23 Jul 2021 17:06:17 -0700 Subject: [PATCH] Allow override requests to outlive the requesting process. This change introduces a flag, FLAG_CANCEL_STICKY_REQUESTS, on the device states that are provided by the DeviceStateProvider. If the flag is set on a device state it indicates that the device state should cancel any outstanding sticky requests when entered. If at least one device state is configured to be a terminal device state (has the flag set) the service will be configured to allow sticky requests, otherwise requests will continue to be cancelled when the requesting process dies. Fixes: 192671286 Test: atest DeviceStateManagerServiceTest Test: atest DeviceStateProviderImplTest Test: atest DeviceStateTest Test: atest OverrideRequestControllerTest Change-Id: I7a6bdc9334342fa818920b56a4f0c22629bb101f --- .../devicestate/DeviceStateManager.java | 8 +- .../server/devicestate/DeviceState.java | 33 ++++- .../DeviceStateManagerService.java | 12 ++ .../OverrideRequestController.java | 114 +++++++++++------- .../policy/DeviceStateProviderImpl.java | 24 +++- .../device-state-config.xsd | 9 ++ .../device-state-config/schema/current.txt | 7 ++ .../DeviceStateManagerServiceTest.java | 9 +- .../server/devicestate/DeviceStateTest.java | 10 +- .../OverrideRequestControllerTest.java | 26 ++++ .../policy/DeviceStateProviderImplTest.java | 23 ++-- 11 files changed, 210 insertions(+), 65 deletions(-) diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index 52dad3efefb89..bb3bffdbe6783 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -75,13 +75,13 @@ public final class DeviceStateManager { /** * Submits a {@link DeviceStateRequest request} to modify the device state. *

- * By default, the request is kept active until a call to - * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs: + * By default, the request is kept active until one of the following occurs: *

* However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}. * diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java index e693bcc93f8f5..7fe24ff1f069b 100644 --- a/services/core/java/com/android/server/devicestate/DeviceState.java +++ b/services/core/java/com/android/server/devicestate/DeviceState.java @@ -19,11 +19,14 @@ package com.android.server.devicestate; import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -39,6 +42,19 @@ import java.util.Objects; * @see DeviceStateManagerService */ public final class DeviceState { + /** + * Flag that indicates sticky requests should be cancelled when this device state becomes the + * base device state. + */ + public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0; + + /** @hide */ + @IntDef(prefix = {"FLAG_"}, flag = true, value = { + FLAG_CANCEL_STICKY_REQUESTS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeviceStateFlags {} + /** Unique identifier for the device state. */ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) private final int mIdentifier; @@ -47,14 +63,19 @@ public final class DeviceState { @NonNull private final String mName; + @DeviceStateFlags + private final int mFlags; + public DeviceState( @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier, - @NonNull String name) { + @NonNull String name, + @DeviceStateFlags int flags) { Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE, "identifier"); mIdentifier = identifier; mName = name; + mFlags = flags; } /** Returns the unique identifier for the device state. */ @@ -69,6 +90,11 @@ public final class DeviceState { return mName; } + @DeviceStateFlags + public int getFlags() { + return mFlags; + } + @Override public String toString() { return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}'; @@ -80,11 +106,12 @@ public final class DeviceState { if (o == null || getClass() != o.getClass()) return false; DeviceState that = (DeviceState) o; return mIdentifier == that.mIdentifier - && Objects.equals(mName, that.mName); + && Objects.equals(mName, that.mName) + && mFlags == that.mFlags; } @Override public int hashCode() { - return Objects.hash(mIdentifier, mName); + return Objects.hash(mIdentifier, mName, mFlags); } } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 42fd9642c2db7..b0c36aac73d80 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -275,12 +275,21 @@ public final class DeviceStateManagerService extends SystemService { synchronized (mLock) { final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked(); + // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS + // set. If set to true, the OverrideRequestController will be configured to allow sticky + // requests. + boolean hasTerminalDeviceState = false; mDeviceStates.clear(); for (int i = 0; i < supportedDeviceStates.length; i++) { DeviceState state = supportedDeviceStates[i]; + if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) { + hasTerminalDeviceState = true; + } mDeviceStates.put(state.getIdentifier(), state); } + mOverrideRequestController.setStickyRequestsAllowed(hasTerminalDeviceState); + final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked(); if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) { return; @@ -338,6 +347,9 @@ public final class DeviceStateManagerService extends SystemService { } mBaseState = Optional.of(baseState); + if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) { + mOverrideRequestController.cancelStickyRequests(); + } mOverrideRequestController.handleBaseStateChanged(); updatePendingStateLocked(); diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java index 042bea5fcfa37..05c9eb2c5bbe8 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java @@ -84,10 +84,28 @@ final class OverrideRequestController { // List of override requests with the most recent override request at the end. private final ArrayList mRequests = new ArrayList<>(); + private boolean mStickyRequestsAllowed; + // List of override requests that have outlived their process and will only be cancelled through + // a call to cancelStickyRequests(). + private final ArrayList mStickyRequests = new ArrayList<>(); + OverrideRequestController(@NonNull StatusChangeListener listener) { mListener = listener; } + /** + * Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call + * to {@link #handleProcessDied(int)} will not result in the request being cancelled + * immediately. Instead, the request will be marked sticky and must be cancelled with a call + * to {@link #cancelStickyRequests()}. + */ + void setStickyRequestsAllowed(boolean stickyRequestsAllowed) { + mStickyRequestsAllowed = stickyRequestsAllowed; + if (!mStickyRequestsAllowed) { + cancelStickyRequests(); + } + } + /** * Adds a request to the top of the stack and notifies the listener of all changes to request * status as a result of this operation. @@ -122,6 +140,18 @@ final class OverrideRequestController { mListener.onStatusChanged(request, STATUS_CANCELED); } + /** + * Cancels all requests that are currently marked sticky and notifies the listener of all + * changes to request status as a result of this operation. + * + * @see #setStickyRequestsAllowed(boolean) + */ + void cancelStickyRequests() { + mTmpRequestsToCancel.clear(); + mTmpRequestsToCancel.addAll(mStickyRequests); + cancelRequestsLocked(mTmpRequestsToCancel); + } + /** * Returns {@code true} if this controller is current managing a request with the specified * {@code token}, {@code false} otherwise. @@ -140,6 +170,7 @@ final class OverrideRequestController { return; } + mTmpRequestsToCancel.clear(); OverrideRequest prevActiveRequest = getLast(mRequests); for (OverrideRequest request : mRequests) { if (request.getPid() == pid) { @@ -147,18 +178,14 @@ final class OverrideRequestController { } } - mRequests.removeAll(mTmpRequestsToCancel); - if (!mRequests.isEmpty()) { - OverrideRequest newActiveRequest = getLast(mRequests); - if (newActiveRequest != prevActiveRequest) { - mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE); - } + if (mStickyRequestsAllowed) { + // Do not cancel the requests now because sticky requests are allowed. These + // requests will be cancelled on a call to cancelStickyRequests(). + mStickyRequests.addAll(mTmpRequestsToCancel); + return; } - for (int i = 0; i < mTmpRequestsToCancel.size(); i++) { - mListener.onStatusChanged(mTmpRequestsToCancel.get(i), STATUS_CANCELED); - } - mTmpRequestsToCancel.clear(); + cancelRequestsLocked(mTmpRequestsToCancel); } /** @@ -173,6 +200,7 @@ final class OverrideRequestController { return false; } + mTmpRequestsToCancel.clear(); OverrideRequest prevActiveRequest = getLast(mRequests); for (int i = 0; i < mRequests.size(); i++) { OverrideRequest request = mRequests.get(i); @@ -181,21 +209,8 @@ final class OverrideRequestController { } } - mRequests.removeAll(mTmpRequestsToCancel); - OverrideRequest newActiveRequest = null; - if (!mRequests.isEmpty()) { - newActiveRequest = getLast(mRequests); - if (newActiveRequest != prevActiveRequest) { - mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE); - } - } - - for (int i = 0; i < mTmpRequestsToCancel.size(); i++) { - mListener.onStatusChanged(mTmpRequestsToCancel.get(i), STATUS_CANCELED); - } - mTmpRequestsToCancel.clear(); - - return newActiveRequest != prevActiveRequest; + final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel); + return newActiveRequest; } /** @@ -210,7 +225,7 @@ final class OverrideRequestController { return false; } - OverrideRequest prevActiveRequest = getLast(mRequests); + mTmpRequestsToCancel.clear(); for (int i = 0; i < mRequests.size(); i++) { OverrideRequest request = mRequests.get(i); if (!contains(newSupportedStates, request.getRequestedState())) { @@ -218,21 +233,8 @@ final class OverrideRequestController { } } - mRequests.removeAll(mTmpRequestsToCancel); - OverrideRequest newActiveRequest = null; - if (!mRequests.isEmpty()) { - newActiveRequest = getLast(mRequests); - if (newActiveRequest != prevActiveRequest) { - mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE); - } - } - - for (int i = 0; i < mTmpRequestsToCancel.size(); i++) { - mListener.onStatusChanged(mTmpRequestsToCancel.get(i), STATUS_CANCELED); - } - mTmpRequestsToCancel.clear(); - - return newActiveRequest != prevActiveRequest; + final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel); + return newActiveRequest; } void dumpInternal(PrintWriter pw) { @@ -249,6 +251,36 @@ final class OverrideRequestController { } } + /** + * Handles cancelling a set of requests. If the set of requests to cancel will lead to a new + * request becoming active this request will also be notified of its change in state. + * + * @return {@code true} if calling this method has lead to a new active request, {@code false} + * otherwise. + */ + private boolean cancelRequestsLocked(List requestsToCancel) { + if (requestsToCancel.isEmpty()) { + return false; + } + + OverrideRequest prevActiveRequest = getLast(mRequests); + boolean causedNewRequestToBecomeActive = false; + mRequests.removeAll(requestsToCancel); + mStickyRequests.removeAll(requestsToCancel); + if (!mRequests.isEmpty()) { + OverrideRequest newActiveRequest = getLast(mRequests); + if (newActiveRequest != prevActiveRequest) { + mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE); + causedNewRequestToBecomeActive = true; + } + } + + for (int i = 0; i < requestsToCancel.size(); i++) { + mListener.onStatusChanged(requestsToCancel.get(i), STATUS_CANCELED); + } + return causedNewRequestToBecomeActive; + } + private int getRequestIndex(@NonNull IBinder token) { final int numberOfRequests = mRequests.size(); if (numberOfRequests == 0) { diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index edd5f5f415c62..ff6511f065771 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -41,6 +41,7 @@ import com.android.server.devicestate.DeviceState; import com.android.server.devicestate.DeviceStateProvider; import com.android.server.policy.devicestate.config.Conditions; import com.android.server.policy.devicestate.config.DeviceStateConfig; +import com.android.server.policy.devicestate.config.Flags; import com.android.server.policy.devicestate.config.LidSwitchCondition; import com.android.server.policy.devicestate.config.NumericRange; import com.android.server.policy.devicestate.config.SensorCondition; @@ -87,7 +88,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, @VisibleForTesting static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE, - "DEFAULT"); + "DEFAULT", 0 /* flags */); private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/"; private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/"; @@ -131,7 +132,26 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, config.getDeviceState()) { final int state = stateConfig.getIdentifier().intValue(); final String name = stateConfig.getName() == null ? "" : stateConfig.getName(); - deviceStateList.add(new DeviceState(state, name)); + + int flags = 0; + final Flags configFlags = stateConfig.getFlags(); + if (configFlags != null) { + List configFlagStrings = configFlags.getFlag(); + for (int i = 0; i < configFlagStrings.size(); i++) { + final String configFlagString = configFlagStrings.get(i); + switch (configFlagString) { + case "FLAG_CANCEL_STICKY_REQUESTS": + flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS; + break; + default: + Slog.w(TAG, "Parsed unknown flag with name: " + + configFlagString); + break; + } + } + } + + deviceStateList.add(new DeviceState(state, name, flags)); final Conditions condition = stateConfig.getConditions(); conditionsList.add(condition); diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd index 94a398f2cdb7f..86f41769008d1 100644 --- a/services/core/xsd/device-state-config/device-state-config.xsd +++ b/services/core/xsd/device-state-config/device-state-config.xsd @@ -40,10 +40,19 @@ + + + + + + + + + diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt index 08fccf8ad9498..a98d4e569cd66 100644 --- a/services/core/xsd/device-state-config/schema/current.txt +++ b/services/core/xsd/device-state-config/schema/current.txt @@ -11,9 +11,11 @@ package com.android.server.policy.devicestate.config { public class DeviceState { ctor public DeviceState(); method public com.android.server.policy.devicestate.config.Conditions getConditions(); + method public com.android.server.policy.devicestate.config.Flags getFlags(); method public java.math.BigInteger getIdentifier(); method @Nullable public String getName(); method public void setConditions(com.android.server.policy.devicestate.config.Conditions); + method public void setFlags(com.android.server.policy.devicestate.config.Flags); method public void setIdentifier(java.math.BigInteger); method public void setName(@Nullable String); } @@ -23,6 +25,11 @@ package com.android.server.policy.devicestate.config { method public java.util.List getDeviceState(); } + public class Flags { + ctor public Flags(); + method @Nullable public java.util.List getFlag(); + } + public class LidSwitchCondition { ctor public LidSwitchCondition(); method public boolean getOpen(); diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 5ebe8321127c0..c8f81742822f6 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -55,10 +55,13 @@ import javax.annotation.Nullable; @Presubmit @RunWith(AndroidJUnit4.class) public final class DeviceStateManagerServiceTest { - private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT"); - private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER"); + private static final DeviceState DEFAULT_DEVICE_STATE = + new DeviceState(0, "DEFAULT", 0 /* flags */); + private static final DeviceState OTHER_DEVICE_STATE = + new DeviceState(1, "OTHER", 0 /* flags */); // A device state that is not reported as being supported for the default test provider. - private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(255, "UNSUPPORTED"); + private static final DeviceState UNSUPPORTED_DEVICE_STATE = + new DeviceState(255, "UNSUPPORTED", 0 /* flags */); private TestDeviceStatePolicy mPolicy; private TestDeviceStateProvider mProvider; diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java index b5c8053ad77ea..e286cb27cc418 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java @@ -41,24 +41,26 @@ public final class DeviceStateTest { @Test public void testConstruct() { final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */, - "CLOSED" /* name */); + "CLOSED" /* name */, DeviceState.FLAG_CANCEL_STICKY_REQUESTS /* flags */); assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE); assertEquals(state.getName(), "CLOSED"); + assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS); } @Test public void testConstruct_nullName() { final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */, - null /* name */); + null /* name */, 0/* flags */); assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE); assertNull(state.getName()); + assertEquals(state.getFlags(), 0); } @Test public void testConstruct_tooLargeIdentifier() { assertThrows(IllegalArgumentException.class, () -> { final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */, - null /* name */); + null /* name */, 0 /* flags */); }); } @@ -66,7 +68,7 @@ public final class DeviceStateTest { public void testConstruct_tooSmallIdentifier() { assertThrows(IllegalArgumentException.class, () -> { final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */, - null /* name */); + null /* name */, 0 /* flags */); }); } } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java index 6f6632fd033f7..c9cf2f06640d8 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java @@ -164,6 +164,32 @@ public final class OverrideRequestControllerTest { assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); } + @Test + public void handleProcessDied_stickyRequests() { + mController.setStickyRequestsAllowed(true); + + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */, + 0 /* requestedState */, 0 /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.handleProcessDied(1); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.cancelStickyRequests(); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + } + @Test public void handleNewSupportedStates() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 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 4d2d2f1a4b7d8..8e2c1f051279d 100644 --- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -150,8 +150,9 @@ public final class DeviceStateProviderImplTest { provider.setListener(listener); verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); - final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""), - new DeviceState(2, "") }; + final DeviceState[] expectedStates = new DeviceState[]{ + new DeviceState(1, "", 0 /* flags */), + new DeviceState(2, "", 0 /* flags */) }; assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue()); verify(listener).onStateChanged(mIntegerCaptor.capture()); @@ -187,8 +188,9 @@ public final class DeviceStateProviderImplTest { provider.setListener(listener); verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); - final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""), - new DeviceState(2, "CLOSED") }; + final DeviceState[] expectedStates = new DeviceState[]{ + new DeviceState(1, "", 0 /* flags */), + new DeviceState(2, "CLOSED", 0 /* flags */) }; assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue()); // onStateChanged() should not be called because the provider has not yet been notified of @@ -264,8 +266,11 @@ public final class DeviceStateProviderImplTest { verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); assertArrayEquals( - new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"), - new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue()); + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */) }, + mDeviceStateArrayCaptor.getValue()); // onStateChanged() should not be called because the provider has not yet been notified of // the initial sensor state. verify(listener, never()).onStateChanged(mIntegerCaptor.capture()); @@ -350,8 +355,10 @@ public final class DeviceStateProviderImplTest { verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); assertArrayEquals( - new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"), - }, mDeviceStateArrayCaptor.getValue()); + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */) + }, mDeviceStateArrayCaptor.getValue()); // onStateChanged() should be called because the provider could not find the sensor. verify(listener).onStateChanged(mIntegerCaptor.capture()); assertEquals(1, mIntegerCaptor.getValue().intValue());