Merge "Add DeviceStateManagerService and stub implementation of policy."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ac28321dc9
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.devicestate;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.util.IntArray;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.policy.DeviceStatePolicyImpl;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A system service that manages the state of a device with user-configurable hardware like a
|
||||
* foldable phone.
|
||||
* <p>
|
||||
* Device state is an abstract concept that allows mapping the current state of the device to the
|
||||
* state of the system. For example, system services (like
|
||||
* {@link com.android.server.display.DisplayManagerService display manager} and
|
||||
* {@link com.android.server.wm.WindowManagerService window manager}) and system UI may have
|
||||
* different behaviors depending on the physical state of the device. This is useful for
|
||||
* variable-state devices, like foldable or rollable devices, that can be configured by users into
|
||||
* differing hardware states, which each may have a different expected use case.
|
||||
* </p>
|
||||
* <p>
|
||||
* The {@link DeviceStateManagerService} is responsible for receiving state change requests from
|
||||
* the {@link DeviceStateProvider} to modify the current device state and communicating with the
|
||||
* {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
|
||||
* </p>
|
||||
*
|
||||
* @see DeviceStatePolicy
|
||||
*/
|
||||
public final class DeviceStateManagerService extends SystemService {
|
||||
/** Invalid device state. */
|
||||
public static final int INVALID_DEVICE_STATE = -1;
|
||||
|
||||
private static final String TAG = "DeviceStateManagerService";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@NonNull
|
||||
private final DeviceStatePolicy mDeviceStatePolicy;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private IntArray mSupportedDeviceStates;
|
||||
|
||||
// The current committed device state.
|
||||
@GuardedBy("mLock")
|
||||
private int mCommittedState = INVALID_DEVICE_STATE;
|
||||
// The device state that is currently pending callback from the policy to be committed.
|
||||
@GuardedBy("mLock")
|
||||
private int mPendingState = INVALID_DEVICE_STATE;
|
||||
// Whether or not the policy is currently waiting to be notified of the current pending state.
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsPolicyWaitingForState = false;
|
||||
// The device state that is currently requested and is next to be configured and committed.
|
||||
@GuardedBy("mLock")
|
||||
private int mRequestedState = INVALID_DEVICE_STATE;
|
||||
|
||||
public DeviceStateManagerService(@NonNull Context context) {
|
||||
this(context, new DeviceStatePolicyImpl());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
|
||||
super(context);
|
||||
mDeviceStatePolicy = policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state the system is in. Note that the system may be in the process of
|
||||
* configuring a different state.
|
||||
*
|
||||
* @see #getPendingState()
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getCommittedState() {
|
||||
synchronized (mLock) {
|
||||
return mCommittedState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state the system is currently configuring, or {@link #INVALID_DEVICE_STATE} if
|
||||
* the system is not in the process of configuring a state.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getPendingState() {
|
||||
synchronized (mLock) {
|
||||
return mPendingState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested state. The service will configure the device to match the requested
|
||||
* state when possible.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getRequestedState() {
|
||||
synchronized (mLock) {
|
||||
return mRequestedState;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSupportedStates(int[] supportedDeviceStates) {
|
||||
// Must ensure sorted as isSupportedStateLocked() impl uses binary search.
|
||||
Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
|
||||
synchronized (mLock) {
|
||||
mSupportedDeviceStates = IntArray.wrap(supportedDeviceStates);
|
||||
|
||||
if (mRequestedState != INVALID_DEVICE_STATE
|
||||
&& !isSupportedStateLocked(mRequestedState)) {
|
||||
// The current requested state is no longer valid. We'll clear it here, though
|
||||
// we won't actually update the current state with a call to
|
||||
// updatePendingStateLocked() as doing so will not have any effect.
|
||||
mRequestedState = INVALID_DEVICE_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the provided state is supported. Requires that
|
||||
* {@link #mSupportedDeviceStates} is sorted prior to calling.
|
||||
*/
|
||||
private boolean isSupportedStateLocked(int state) {
|
||||
return mSupportedDeviceStates.binarySearch(state) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the system enter the provided {@code state}. The request may not be honored
|
||||
* under certain conditions, for example if the provided state is not supported.
|
||||
*
|
||||
* @see #isSupportedStateLocked(int)
|
||||
*/
|
||||
private void requestState(int state) {
|
||||
synchronized (mLock) {
|
||||
if (isSupportedStateLocked(state)) {
|
||||
mRequestedState = state;
|
||||
}
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to update the current configuring state with the current requested state. Must call
|
||||
* {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
|
||||
* changed.
|
||||
*/
|
||||
private void updatePendingStateLocked() {
|
||||
if (mRequestedState == INVALID_DEVICE_STATE) {
|
||||
// No currently requested state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPendingState != INVALID_DEVICE_STATE) {
|
||||
// Have pending state, can not configure a new state until the state is committed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRequestedState == mCommittedState) {
|
||||
// No need to notify the policy as the committed state matches the requested state.
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingState = mRequestedState;
|
||||
mIsPolicyWaitingForState = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the policy to configure the supplied state. Should not be called with {@link #mLock}
|
||||
* held.
|
||||
*/
|
||||
private void notifyPolicyIfNeeded() {
|
||||
if (Thread.holdsLock(mLock)) {
|
||||
Throwable error = new Throwable("Attempting to notify DeviceStatePolicy with service"
|
||||
+ " lock held");
|
||||
error.fillInStackTrace();
|
||||
Slog.w(TAG, error);
|
||||
}
|
||||
int state;
|
||||
synchronized (mLock) {
|
||||
if (!mIsPolicyWaitingForState) {
|
||||
return;
|
||||
}
|
||||
mIsPolicyWaitingForState = false;
|
||||
state = mPendingState;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Notifying policy to configure state: " + state);
|
||||
}
|
||||
mDeviceStatePolicy.configureDeviceForState(state, this::commitPendingState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits the current pending state after a callback from the {@link DeviceStatePolicy}.
|
||||
*
|
||||
* <pre>
|
||||
* ------------- ----------- -------------
|
||||
* Provider -> | Requested | -> | Pending | -> Policy -> | Committed |
|
||||
* ------------- ----------- -------------
|
||||
* </pre>
|
||||
* <p>
|
||||
* When a new state is requested it immediately enters the requested state. Once the policy is
|
||||
* available to accept a new state, which could also be immediately if there is no current
|
||||
* pending state at the point of request, the policy is notified and a callback is provided to
|
||||
* trigger the state to be committed.
|
||||
* </p>
|
||||
*/
|
||||
private void commitPendingState() {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Committing state: " + mPendingState);
|
||||
}
|
||||
mCommittedState = mPendingState;
|
||||
mPendingState = INVALID_DEVICE_STATE;
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
|
||||
@Override
|
||||
public void onSupportedDeviceStatesChanged(int[] newDeviceStates) {
|
||||
for (int i = 0; i < newDeviceStates.length; i++) {
|
||||
if (newDeviceStates[i] < 0) {
|
||||
throw new IllegalArgumentException("Supported device states includes invalid"
|
||||
+ " value: " + newDeviceStates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
updateSupportedStates(newDeviceStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(int state) {
|
||||
if (state < 0) {
|
||||
throw new IllegalArgumentException("Invalid state: " + state);
|
||||
}
|
||||
|
||||
requestState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.devicestate;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Interface for the component responsible for supplying the current device state as well as
|
||||
* configuring the state of the system in response to device state changes.
|
||||
*
|
||||
* @see DeviceStateManagerService
|
||||
*/
|
||||
public interface DeviceStatePolicy {
|
||||
/** Returns the provider of device states. */
|
||||
DeviceStateProvider getDeviceStateProvider();
|
||||
|
||||
/**
|
||||
* Configures the system into the provided state. Guaranteed not to be called again until the
|
||||
* {@code onComplete} callback has been executed.
|
||||
*
|
||||
* @param state the state the system should be configured for.
|
||||
* @param onComplete a callback that must be triggered once the system has been properly
|
||||
* configured to match the supplied state.
|
||||
*/
|
||||
void configureDeviceForState(int state, @NonNull Runnable onComplete);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.devicestate;
|
||||
|
||||
/**
|
||||
* Responsible for providing the set of currently supported device states and well as the current
|
||||
* device state.
|
||||
*
|
||||
* @see DeviceStatePolicy
|
||||
*/
|
||||
public interface DeviceStateProvider {
|
||||
/**
|
||||
* Registers a listener for changes in provider state.
|
||||
* <p>
|
||||
* It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(int[])} be called
|
||||
* followed by {@link Listener#onStateChanged(int)} with the initial values on successful
|
||||
* registration of the listener.
|
||||
*/
|
||||
void setListener(Listener listener);
|
||||
|
||||
/** Callback for changes in {@link DeviceStateProvider} state. */
|
||||
interface Listener {
|
||||
/**
|
||||
* Called to notify the listener of a change in supported device states. Required to be
|
||||
* called once on successful registration of the listener and then once on every
|
||||
* subsequent change in supported device states.
|
||||
* <p>
|
||||
* The set of device states can change based on the current hardware state of the device.
|
||||
* For example, if a device state depends on a particular peripheral device (display, etc)
|
||||
* it would only be reported as supported when the device is plugged. Otherwise, it should
|
||||
* not be included in the set of supported states.
|
||||
* <p>
|
||||
* All values provided must be greater than or equal to zero and there must always be at
|
||||
* least one supported device state.
|
||||
*
|
||||
* @param newDeviceStates array of supported device states.
|
||||
*
|
||||
* @throws IllegalArgumentException if the list of device states is empty or if one of the
|
||||
* provided states is less than 0.
|
||||
*/
|
||||
void onSupportedDeviceStatesChanged(int[] newDeviceStates);
|
||||
|
||||
/**
|
||||
* Called to notify the listener of a change in current device state. Required to be called
|
||||
* once on successful registration of the listener and then once on every subsequent change
|
||||
* in device state. Value must have been included in the set of supported device states
|
||||
* provided in the most recent call to {@link #onSupportedDeviceStatesChanged(int[])}.
|
||||
*
|
||||
* @param state the new device state.
|
||||
*
|
||||
* @throws IllegalArgumentException if the state is less than 0.
|
||||
*/
|
||||
void onStateChanged(int state);
|
||||
}
|
||||
}
|
||||
3
services/core/java/com/android/server/devicestate/OWNERS
Normal file
3
services/core/java/com/android/server/devicestate/OWNERS
Normal file
@@ -0,0 +1,3 @@
|
||||
ogunwale@google.com
|
||||
akulian@google.com
|
||||
darryljohnson@google.com
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 android.annotation.NonNull;
|
||||
|
||||
import com.android.server.devicestate.DeviceStatePolicy;
|
||||
import com.android.server.devicestate.DeviceStateProvider;
|
||||
|
||||
/**
|
||||
* Default empty implementation of {@link DeviceStatePolicy}.
|
||||
*
|
||||
* @see DeviceStateProviderImpl
|
||||
*/
|
||||
public final class DeviceStatePolicyImpl implements DeviceStatePolicy {
|
||||
private final DeviceStateProvider mProvider;
|
||||
|
||||
public DeviceStatePolicyImpl() {
|
||||
mProvider = new DeviceStateProviderImpl();
|
||||
}
|
||||
|
||||
public DeviceStateProvider getDeviceStateProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 android.annotation.Nullable;
|
||||
|
||||
import com.android.server.devicestate.DeviceStateProvider;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link DeviceStateProvider}. Currently only supports
|
||||
* {@link #DEFAULT_DEVICE_STATE}.
|
||||
*
|
||||
* @see DeviceStatePolicyImpl
|
||||
*/
|
||||
final class DeviceStateProviderImpl implements DeviceStateProvider {
|
||||
private static final int DEFAULT_DEVICE_STATE = 0;
|
||||
|
||||
@Nullable
|
||||
private Listener mListener = null;
|
||||
|
||||
@Override
|
||||
public void setListener(Listener listener) {
|
||||
if (mListener != null) {
|
||||
throw new RuntimeException("Provider already has a listener set.");
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
mListener.onSupportedDeviceStatesChanged(new int[]{ DEFAULT_DEVICE_STATE });
|
||||
mListener.onStateChanged(DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ import com.android.server.connectivity.IpConnectivityMetrics;
|
||||
import com.android.server.contentcapture.ContentCaptureManagerInternal;
|
||||
import com.android.server.coverage.CoverageService;
|
||||
import com.android.server.devicepolicy.DevicePolicyManagerService;
|
||||
import com.android.server.devicestate.DeviceStateManagerService;
|
||||
import com.android.server.display.DisplayManagerService;
|
||||
import com.android.server.display.color.ColorDisplayService;
|
||||
import com.android.server.dreams.DreamManagerService;
|
||||
@@ -1254,6 +1255,10 @@ public final class SystemServer {
|
||||
mSystemServiceManager.startService(AppIntegrityManagerService.class);
|
||||
t.traceEnd();
|
||||
|
||||
t.traceBegin("DeviceStateManagerService");
|
||||
mSystemServiceManager.startService(DeviceStateManagerService.class);
|
||||
t.traceEnd();
|
||||
|
||||
} catch (Throwable e) {
|
||||
Slog.e("System", "******************************************");
|
||||
Slog.e("System", "************ Failure starting core service");
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.devicestate;
|
||||
|
||||
import static com.android.server.devicestate.DeviceStateManagerService.INVALID_DEVICE_STATE;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DeviceStateManagerService}.
|
||||
* <p/>
|
||||
* Run with <code>atest DeviceStateManagerServiceTest</code>.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class DeviceStateManagerServiceTest {
|
||||
private static final int DEFAULT_DEVICE_STATE = 0;
|
||||
private static final int OTHER_DEVICE_STATE = 1;
|
||||
private static final int UNSUPPORTED_DEVICE_STATE = 999;
|
||||
|
||||
private TestDeviceStatePolicy mPolicy;
|
||||
private TestDeviceStateProvider mProvider;
|
||||
private DeviceStateManagerService mService;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mProvider = new TestDeviceStateProvider();
|
||||
mPolicy = new TestDeviceStatePolicy(mProvider);
|
||||
mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
|
||||
mService.onStart();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange_pendingState() {
|
||||
mPolicy.blockConfigure();
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
|
||||
|
||||
mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
|
||||
|
||||
mPolicy.resumeConfigure();
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange_unsupportedState() {
|
||||
mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE);
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange_invalidState() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
mProvider.notifyRequestState(INVALID_DEVICE_STATE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
|
||||
|
||||
// The current committed and requests states do not change because the current state remains
|
||||
// supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged_invalidState() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
mProvider.notifySupportedDeviceStates(new int []{ INVALID_DEVICE_STATE });
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged_unsupportedRequestedState() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new int []{ OTHER_DEVICE_STATE });
|
||||
|
||||
// The current requested state is cleared because it is no longer supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), INVALID_DEVICE_STATE);
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE);
|
||||
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
|
||||
}
|
||||
|
||||
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
|
||||
private final DeviceStateProvider mProvider;
|
||||
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
|
||||
private boolean mConfigureBlocked = false;
|
||||
private Runnable mPendingConfigureCompleteRunnable;
|
||||
|
||||
TestDeviceStatePolicy(DeviceStateProvider provider) {
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceStateProvider getDeviceStateProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
|
||||
public void blockConfigure() {
|
||||
mConfigureBlocked = true;
|
||||
}
|
||||
|
||||
public void resumeConfigure() {
|
||||
mConfigureBlocked = false;
|
||||
if (mPendingConfigureCompleteRunnable != null) {
|
||||
Runnable onComplete = mPendingConfigureCompleteRunnable;
|
||||
mPendingConfigureCompleteRunnable = null;
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
|
||||
public int getMostRecentRequestedStateToConfigure() {
|
||||
return mLastDeviceStateRequestedToConfigure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureDeviceForState(int state, Runnable onComplete) {
|
||||
if (mPendingConfigureCompleteRunnable != null) {
|
||||
throw new IllegalStateException("configureDeviceForState() called while configure"
|
||||
+ " is pending");
|
||||
}
|
||||
|
||||
mLastDeviceStateRequestedToConfigure = state;
|
||||
if (mConfigureBlocked) {
|
||||
mPendingConfigureCompleteRunnable = onComplete;
|
||||
return;
|
||||
}
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestDeviceStateProvider implements DeviceStateProvider {
|
||||
private int[] mSupportedDeviceStates = new int[]{ DEFAULT_DEVICE_STATE,
|
||||
OTHER_DEVICE_STATE };
|
||||
private int mCurrentDeviceState = DEFAULT_DEVICE_STATE;
|
||||
private Listener mListener;
|
||||
|
||||
@Override
|
||||
public void setListener(Listener listener) {
|
||||
if (mListener != null) {
|
||||
throw new IllegalArgumentException("Provider already has listener set.");
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates);
|
||||
mListener.onStateChanged(mCurrentDeviceState);
|
||||
}
|
||||
|
||||
public void notifySupportedDeviceStates(int[] supportedDeviceStates) {
|
||||
mSupportedDeviceStates = supportedDeviceStates;
|
||||
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
|
||||
}
|
||||
|
||||
public void notifyRequestState(int state) {
|
||||
mCurrentDeviceState = state;
|
||||
mListener.onStateChanged(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user