Merge changes from topic "top-focused-DevStMan" into sc-v2-dev
* changes: Allow top focused activities to call requestState Allow override requests to outlive the requesting process. Refactor request lifecycle logic into OverrideRequestController.
This commit is contained in:
@@ -1133,10 +1133,10 @@ package android.hardware.camera2 {
|
|||||||
package android.hardware.devicestate {
|
package android.hardware.devicestate {
|
||||||
|
|
||||||
public final class DeviceStateManager {
|
public final class DeviceStateManager {
|
||||||
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
|
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
|
||||||
method @NonNull public int[] getSupportedStates();
|
method @NonNull public int[] getSupportedStates();
|
||||||
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
|
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
|
||||||
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
|
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
|
||||||
method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
|
method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
|
||||||
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
|
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
|
||||||
field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
|
field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
|
||||||
|
|||||||
@@ -75,23 +75,24 @@ public final class DeviceStateManager {
|
|||||||
/**
|
/**
|
||||||
* Submits a {@link DeviceStateRequest request} to modify the device state.
|
* Submits a {@link DeviceStateRequest request} to modify the device state.
|
||||||
* <p>
|
* <p>
|
||||||
* By default, the request is kept active until a call to
|
* By default, the request is kept active until one of the following occurs:
|
||||||
* {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
|
|
||||||
* <ul>
|
* <ul>
|
||||||
|
* <li>The system deems the request can no longer be honored, for example if the requested
|
||||||
|
* state becomes unsupported.
|
||||||
|
* <li>A call to {@link #cancelRequest(DeviceStateRequest)}.
|
||||||
* <li>Another processes submits a request succeeding this request in which case the request
|
* <li>Another processes submits a request succeeding this request in which case the request
|
||||||
* will be suspended until the interrupting request is canceled.
|
* will be suspended until the interrupting request is canceled.
|
||||||
* <li>The requested state has become unsupported.
|
|
||||||
* <li>The process submitting the request dies.
|
|
||||||
* </ul>
|
* </ul>
|
||||||
* However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
|
* However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the requested state is unsupported.
|
* @throws IllegalArgumentException if the requested state is unsupported.
|
||||||
* @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
|
* @throws SecurityException if the caller is neither the current top-focused activity nor if
|
||||||
* permission is not held.
|
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
|
||||||
*
|
*
|
||||||
* @see DeviceStateRequest
|
* @see DeviceStateRequest
|
||||||
*/
|
*/
|
||||||
@RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
|
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
|
||||||
|
conditional = true)
|
||||||
public void requestState(@NonNull DeviceStateRequest request,
|
public void requestState(@NonNull DeviceStateRequest request,
|
||||||
@Nullable @CallbackExecutor Executor executor,
|
@Nullable @CallbackExecutor Executor executor,
|
||||||
@Nullable DeviceStateRequest.Callback callback) {
|
@Nullable DeviceStateRequest.Callback callback) {
|
||||||
@@ -105,10 +106,11 @@ public final class DeviceStateManager {
|
|||||||
* This method is noop if the {@code request} has not been submitted with a call to
|
* This method is noop if the {@code request} has not been submitted with a call to
|
||||||
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
|
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
|
||||||
*
|
*
|
||||||
* @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
|
* @throws SecurityException if the caller is neither the current top-focused activity nor if
|
||||||
* permission is not held.
|
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
|
||||||
*/
|
*/
|
||||||
@RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
|
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
|
||||||
|
conditional = true)
|
||||||
public void cancelRequest(@NonNull DeviceStateRequest request) {
|
public void cancelRequest(@NonNull DeviceStateRequest request) {
|
||||||
mGlobal.cancelRequest(request);
|
mGlobal.cancelRequest(request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ package com.android.server.devicestate;
|
|||||||
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
|
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
|
||||||
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
|
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.IntRange;
|
import android.annotation.IntRange;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +42,19 @@ import java.util.Objects;
|
|||||||
* @see DeviceStateManagerService
|
* @see DeviceStateManagerService
|
||||||
*/
|
*/
|
||||||
public final class DeviceState {
|
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. */
|
/** Unique identifier for the device state. */
|
||||||
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
|
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
|
||||||
private final int mIdentifier;
|
private final int mIdentifier;
|
||||||
@@ -47,14 +63,19 @@ public final class DeviceState {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private final String mName;
|
private final String mName;
|
||||||
|
|
||||||
|
@DeviceStateFlags
|
||||||
|
private final int mFlags;
|
||||||
|
|
||||||
public DeviceState(
|
public DeviceState(
|
||||||
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
|
@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,
|
Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
|
||||||
"identifier");
|
"identifier");
|
||||||
|
|
||||||
mIdentifier = identifier;
|
mIdentifier = identifier;
|
||||||
mName = name;
|
mName = name;
|
||||||
|
mFlags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the unique identifier for the device state. */
|
/** Returns the unique identifier for the device state. */
|
||||||
@@ -69,6 +90,11 @@ public final class DeviceState {
|
|||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeviceStateFlags
|
||||||
|
public int getFlags() {
|
||||||
|
return mFlags;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
|
return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
|
||||||
@@ -80,11 +106,12 @@ public final class DeviceState {
|
|||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
DeviceState that = (DeviceState) o;
|
DeviceState that = (DeviceState) o;
|
||||||
return mIdentifier == that.mIdentifier
|
return mIdentifier == that.mIdentifier
|
||||||
&& Objects.equals(mName, that.mName);
|
&& Objects.equals(mName, that.mName)
|
||||||
|
&& mFlags == that.mFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(mIdentifier, mName);
|
return Objects.hash(mIdentifier, mName, mFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,13 @@ package com.android.server.devicestate;
|
|||||||
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
|
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
|
||||||
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
|
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
|
||||||
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
|
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
|
||||||
import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
|
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
|
||||||
|
|
||||||
|
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
|
||||||
|
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
|
||||||
|
import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.IntRange;
|
import android.annotation.IntRange;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -30,12 +35,12 @@ import android.hardware.devicestate.DeviceStateManager;
|
|||||||
import android.hardware.devicestate.IDeviceStateManager;
|
import android.hardware.devicestate.IDeviceStateManager;
|
||||||
import android.hardware.devicestate.IDeviceStateManagerCallback;
|
import android.hardware.devicestate.IDeviceStateManagerCallback;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
import android.os.ShellCallback;
|
import android.os.ShellCallback;
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
@@ -43,14 +48,21 @@ import com.android.internal.annotations.GuardedBy;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
|
import com.android.server.LocalServices;
|
||||||
|
import com.android.server.ServiceThread;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
import com.android.server.policy.DeviceStatePolicyImpl;
|
import com.android.server.policy.DeviceStatePolicyImpl;
|
||||||
|
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||||
|
import com.android.server.wm.WindowProcessController;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A system service that manages the state of a device with user-configurable hardware like a
|
* A system service that manages the state of a device with user-configurable hardware like a
|
||||||
@@ -81,10 +93,20 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
// Internal system service thread used to dispatch calls to the policy and to registered
|
||||||
|
// callbacks though its handler (mHandler). Provides a guarantee of callback order when
|
||||||
|
// leveraging mHandler and also enables posting messages with the service lock held.
|
||||||
|
private final HandlerThread mHandlerThread;
|
||||||
|
private final Handler mHandler;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final DeviceStatePolicy mDeviceStatePolicy;
|
private final DeviceStatePolicy mDeviceStatePolicy;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final BinderService mBinderService;
|
private final BinderService mBinderService;
|
||||||
|
@NonNull
|
||||||
|
private final OverrideRequestController mOverrideRequestController;
|
||||||
|
@VisibleForTesting
|
||||||
|
@NonNull
|
||||||
|
public ActivityTaskManagerInternal mActivityTaskManagerInternal;
|
||||||
|
|
||||||
// All supported device states keyed by identifier.
|
// All supported device states keyed by identifier.
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -109,17 +131,16 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private Optional<DeviceState> mBaseState = Optional.empty();
|
private Optional<DeviceState> mBaseState = Optional.empty();
|
||||||
|
|
||||||
|
// The current active override request. When set the device state specified here will take
|
||||||
|
// precedence over mBaseState.
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
@NonNull
|
||||||
|
private Optional<OverrideRequest> mActiveOverride = Optional.empty();
|
||||||
|
|
||||||
// List of processes registered to receive notifications about changes to device state and
|
// List of processes registered to receive notifications about changes to device state and
|
||||||
// request status indexed by process id.
|
// request status indexed by process id.
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
|
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
|
||||||
// List of override requests with the highest precedence request at the end.
|
|
||||||
@GuardedBy("mLock")
|
|
||||||
private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
|
|
||||||
// Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
|
|
||||||
// of a change in status.
|
|
||||||
@GuardedBy("mLock")
|
|
||||||
private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
|
|
||||||
|
|
||||||
public DeviceStateManagerService(@NonNull Context context) {
|
public DeviceStateManagerService(@NonNull Context context) {
|
||||||
this(context, new DeviceStatePolicyImpl(context));
|
this(context, new DeviceStatePolicyImpl(context));
|
||||||
@@ -128,9 +149,17 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
|
DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
|
||||||
super(context);
|
super(context);
|
||||||
|
// Service thread assigned THREAD_PRIORITY_DISPLAY because this service indirectly drives
|
||||||
|
// display (on/off) and window (position) events through its callbacks.
|
||||||
|
mHandlerThread = new ServiceThread(TAG, THREAD_PRIORITY_DISPLAY, false /* allowIo */);
|
||||||
|
mHandlerThread.start();
|
||||||
|
mHandler = mHandlerThread.getThreadHandler();
|
||||||
|
mOverrideRequestController = new OverrideRequestController(
|
||||||
|
this::onOverrideRequestStatusChangedLocked);
|
||||||
mDeviceStatePolicy = policy;
|
mDeviceStatePolicy = policy;
|
||||||
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
|
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
|
||||||
mBinderService = new BinderService();
|
mBinderService = new BinderService();
|
||||||
|
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -138,6 +167,11 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
|
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Handler getHandler() {
|
||||||
|
return mHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current state the system is in. Note that the system may be in the process of
|
* Returns the current state the system is in. Note that the system may be in the process of
|
||||||
* configuring a different state.
|
* configuring a different state.
|
||||||
@@ -191,12 +225,10 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
@NonNull
|
@NonNull
|
||||||
Optional<DeviceState> getOverrideState() {
|
Optional<DeviceState> getOverrideState() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mRequestRecords.isEmpty()) {
|
if (mActiveOverride.isPresent()) {
|
||||||
return Optional.empty();
|
return getStateLocked(mActiveOverride.get().getRequestedState());
|
||||||
}
|
}
|
||||||
|
return Optional.empty();
|
||||||
OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
|
|
||||||
return Optional.of(topRequest.mRequestedState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,43 +279,41 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
|
private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
|
||||||
boolean updatedPendingState;
|
|
||||||
boolean hasBaseState;
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
|
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();
|
mDeviceStates.clear();
|
||||||
for (int i = 0; i < supportedDeviceStates.length; i++) {
|
for (int i = 0; i < supportedDeviceStates.length; i++) {
|
||||||
DeviceState state = supportedDeviceStates[i];
|
DeviceState state = supportedDeviceStates[i];
|
||||||
|
if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
|
||||||
|
hasTerminalDeviceState = true;
|
||||||
|
}
|
||||||
mDeviceStates.put(state.getIdentifier(), state);
|
mDeviceStates.put(state.getIdentifier(), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mOverrideRequestController.setStickyRequestsAllowed(hasTerminalDeviceState);
|
||||||
|
|
||||||
final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
|
final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
|
||||||
if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
|
if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int requestSize = mRequestRecords.size();
|
mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers);
|
||||||
for (int i = 0; i < requestSize; i++) {
|
updatePendingStateLocked();
|
||||||
OverrideRequestRecord request = mRequestRecords.get(i);
|
|
||||||
if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
|
if (!mPendingState.isPresent()) {
|
||||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
|
// If the change in the supported states didn't result in a change of the pending
|
||||||
}
|
// state commitPendingState() will never be called and the callbacks will never be
|
||||||
|
// notified of the change.
|
||||||
|
notifyDeviceStateInfoChangedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedPendingState = updatePendingStateLocked();
|
mHandler.post(this::notifyPolicyIfNeeded);
|
||||||
hasBaseState = mBaseState.isPresent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasBaseState && !updatedPendingState) {
|
|
||||||
// If the change in the supported states didn't result in a change of the pending state
|
|
||||||
// commitPendingState() will never be called and the callbacks will never be notified
|
|
||||||
// of the change.
|
|
||||||
notifyDeviceStateInfoChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyRequestsOfStatusChangeIfNeeded();
|
|
||||||
notifyPolicyIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -311,7 +341,6 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
* @see #isSupportedStateLocked(int)
|
* @see #isSupportedStateLocked(int)
|
||||||
*/
|
*/
|
||||||
private void setBaseState(int identifier) {
|
private void setBaseState(int identifier) {
|
||||||
boolean updatedPendingState;
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
final Optional<DeviceState> baseStateOptional = getStateLocked(identifier);
|
final Optional<DeviceState> baseStateOptional = getStateLocked(identifier);
|
||||||
if (!baseStateOptional.isPresent()) {
|
if (!baseStateOptional.isPresent()) {
|
||||||
@@ -325,26 +354,21 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
mBaseState = Optional.of(baseState);
|
mBaseState = Optional.of(baseState);
|
||||||
|
|
||||||
final int requestSize = mRequestRecords.size();
|
if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
|
||||||
for (int i = 0; i < requestSize; i++) {
|
mOverrideRequestController.cancelStickyRequests();
|
||||||
OverrideRequestRecord request = mRequestRecords.get(i);
|
|
||||||
if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
|
|
||||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
mOverrideRequestController.handleBaseStateChanged();
|
||||||
|
updatePendingStateLocked();
|
||||||
|
|
||||||
updatedPendingState = updatePendingStateLocked();
|
if (!mPendingState.isPresent()) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!updatedPendingState) {
|
|
||||||
// If the change in base state didn't result in a change of the pending state
|
// If the change in base state didn't result in a change of the pending state
|
||||||
// commitPendingState() will never be called and the callbacks will never be notified
|
// commitPendingState() will never be called and the callbacks will never be
|
||||||
// of the change.
|
// notified of the change.
|
||||||
notifyDeviceStateInfoChanged();
|
notifyDeviceStateInfoChangedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyRequestsOfStatusChangeIfNeeded();
|
mHandler.post(this::notifyPolicyIfNeeded);
|
||||||
notifyPolicyIfNeeded();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -362,8 +386,8 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final DeviceState stateToConfigure;
|
final DeviceState stateToConfigure;
|
||||||
if (!mRequestRecords.isEmpty()) {
|
if (mActiveOverride.isPresent()) {
|
||||||
stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
|
stateToConfigure = getStateLocked(mActiveOverride.get().getRequestedState()).get();
|
||||||
} else if (mBaseState.isPresent()
|
} else if (mBaseState.isPresent()
|
||||||
&& isSupportedStateLocked(mBaseState.get().getIdentifier())) {
|
&& isSupportedStateLocked(mBaseState.get().getIdentifier())) {
|
||||||
// Base state could have recently become unsupported after a change in supported states.
|
// Base state could have recently become unsupported after a change in supported states.
|
||||||
@@ -429,108 +453,106 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private void commitPendingState() {
|
private void commitPendingState() {
|
||||||
// Update the current state.
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
final DeviceState newState = mPendingState.get();
|
final DeviceState newState = mPendingState.get();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Committing state: " + newState);
|
Slog.d(TAG, "Committing state: " + newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mRequestRecords.isEmpty()) {
|
|
||||||
final OverrideRequestRecord topRequest =
|
|
||||||
mRequestRecords.get(mRequestRecords.size() - 1);
|
|
||||||
if (topRequest.mRequestedState.getIdentifier() == newState.getIdentifier()) {
|
|
||||||
// The top request could have come in while the service was awaiting callback
|
|
||||||
// from the policy. In that case we only set it to active if it matches the
|
|
||||||
// current committed state, otherwise it will be set to active when its
|
|
||||||
// requested state is committed.
|
|
||||||
topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
|
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
|
||||||
newState.getIdentifier(), !mCommittedState.isPresent());
|
newState.getIdentifier(), !mCommittedState.isPresent());
|
||||||
|
|
||||||
mCommittedState = Optional.of(newState);
|
mCommittedState = Optional.of(newState);
|
||||||
mPendingState = Optional.empty();
|
mPendingState = Optional.empty();
|
||||||
updatePendingStateLocked();
|
updatePendingStateLocked();
|
||||||
}
|
|
||||||
|
|
||||||
// Notify callbacks of a change.
|
// Notify callbacks of a change.
|
||||||
notifyDeviceStateInfoChanged();
|
notifyDeviceStateInfoChangedAsync();
|
||||||
|
|
||||||
// Notify the top request that it's active.
|
// The top request could have come in while the service was awaiting callback
|
||||||
notifyRequestsOfStatusChangeIfNeeded();
|
// from the policy. In that case we only set it to active if it matches the
|
||||||
|
// current committed state, otherwise it will be set to active when its
|
||||||
|
// requested state is committed.
|
||||||
|
OverrideRequest activeRequest = mActiveOverride.orElse(null);
|
||||||
|
if (activeRequest != null
|
||||||
|
&& activeRequest.getRequestedState() == newState.getIdentifier()) {
|
||||||
|
ProcessRecord processRecord = mProcessRecords.get(activeRequest.getPid());
|
||||||
|
if (processRecord != null) {
|
||||||
|
processRecord.notifyRequestActiveAsync(activeRequest.getToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to configure the next state if needed.
|
// Try to configure the next state if needed.
|
||||||
notifyPolicyIfNeeded();
|
mHandler.post(this::notifyPolicyIfNeeded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyDeviceStateInfoChanged() {
|
private void notifyDeviceStateInfoChangedAsync() {
|
||||||
if (Thread.holdsLock(mLock)) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Attempting to notify callbacks with service lock held.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the lock and copy the process records and the current info.
|
|
||||||
ArrayList<ProcessRecord> registeredProcesses;
|
|
||||||
DeviceStateInfo info;
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mProcessRecords.size() == 0) {
|
if (mProcessRecords.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registeredProcesses = new ArrayList<>();
|
ArrayList<ProcessRecord> registeredProcesses = new ArrayList<>();
|
||||||
for (int i = 0; i < mProcessRecords.size(); i++) {
|
for (int i = 0; i < mProcessRecords.size(); i++) {
|
||||||
registeredProcesses.add(mProcessRecords.valueAt(i));
|
registeredProcesses.add(mProcessRecords.valueAt(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
info = getDeviceStateInfoLocked();
|
DeviceStateInfo info = getDeviceStateInfoLocked();
|
||||||
}
|
|
||||||
|
|
||||||
// After releasing the lock, send the notifications out.
|
|
||||||
for (int i = 0; i < registeredProcesses.size(); i++) {
|
for (int i = 0; i < registeredProcesses.size(); i++) {
|
||||||
registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
|
registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies all dirty requests (requests that have a change in status, but have not yet been
|
|
||||||
* notified) that their status has changed.
|
|
||||||
*/
|
|
||||||
private void notifyRequestsOfStatusChangeIfNeeded() {
|
|
||||||
if (Thread.holdsLock(mLock)) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Attempting to notify requests with service lock held.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArraySet<OverrideRequestRecord> dirtyRequests;
|
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
|
||||||
synchronized (mLock) {
|
@OverrideRequestController.RequestStatus int status) {
|
||||||
if (mRequestsPendingStatusChange.isEmpty()) {
|
if (status == STATUS_ACTIVE) {
|
||||||
|
mActiveOverride = Optional.of(request);
|
||||||
|
} else if (status == STATUS_SUSPENDED || status == STATUS_CANCELED) {
|
||||||
|
if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
|
||||||
|
mActiveOverride = Optional.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown request status: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean updatedPendingState = updatePendingStateLocked();
|
||||||
|
|
||||||
|
ProcessRecord processRecord = mProcessRecords.get(request.getPid());
|
||||||
|
if (processRecord == null) {
|
||||||
|
// If the process is no longer registered with the service, for example if it has died,
|
||||||
|
// there is no need to notify it of a change in request status.
|
||||||
|
mHandler.post(this::notifyPolicyIfNeeded);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
|
if (status == STATUS_ACTIVE) {
|
||||||
mRequestsPendingStatusChange.clear();
|
if (!updatedPendingState && !mPendingState.isPresent()) {
|
||||||
|
// If the pending state was not updated and there is not currently a pending state
|
||||||
|
// then this newly active request will never be notified of a change in state.
|
||||||
|
// Schedule the notification now.
|
||||||
|
processRecord.notifyRequestActiveAsync(request.getToken());
|
||||||
|
}
|
||||||
|
} else if (status == STATUS_SUSPENDED) {
|
||||||
|
processRecord.notifyRequestSuspendedAsync(request.getToken());
|
||||||
|
} else {
|
||||||
|
processRecord.notifyRequestCanceledAsync(request.getToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
// After releasing the lock, send the notifications out.
|
mHandler.post(this::notifyPolicyIfNeeded);
|
||||||
for (int i = 0; i < dirtyRequests.size(); i++) {
|
|
||||||
dirtyRequests.valueAt(i).notifyStatusIfNeeded();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
|
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
|
||||||
DeviceStateInfo currentInfo;
|
|
||||||
ProcessRecord record;
|
|
||||||
// Grab the lock to register the callback and get the current state.
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mProcessRecords.contains(pid)) {
|
if (mProcessRecords.contains(pid)) {
|
||||||
throw new SecurityException("The calling process has already registered an"
|
throw new SecurityException("The calling process has already registered an"
|
||||||
+ " IDeviceStateManagerCallback.");
|
+ " IDeviceStateManagerCallback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
record = new ProcessRecord(callback, pid);
|
ProcessRecord record = new ProcessRecord(callback, pid, this::handleProcessDied,
|
||||||
|
mHandlerThread.getThreadHandler());
|
||||||
try {
|
try {
|
||||||
callback.asBinder().linkToDeath(record, 0);
|
callback.asBinder().linkToDeath(record, 0);
|
||||||
} catch (RemoteException ex) {
|
} catch (RemoteException ex) {
|
||||||
@@ -538,34 +560,21 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
mProcessRecords.put(pid, record);
|
mProcessRecords.put(pid, record);
|
||||||
|
|
||||||
currentInfo = mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null;
|
DeviceStateInfo currentInfo = mCommittedState.isPresent()
|
||||||
}
|
? getDeviceStateInfoLocked() : null;
|
||||||
|
|
||||||
if (currentInfo != null) {
|
if (currentInfo != null) {
|
||||||
// If there is not a committed state we'll wait to notify the process of the initial
|
// If there is not a committed state we'll wait to notify the process of the initial
|
||||||
// value.
|
// value.
|
||||||
record.notifyDeviceStateInfoAsync(currentInfo);
|
record.notifyDeviceStateInfoAsync(currentInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleProcessDied(ProcessRecord processRecord) {
|
private void handleProcessDied(ProcessRecord processRecord) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
// Cancel all requests from this process.
|
|
||||||
final int requestCount = processRecord.mRequestRecords.size();
|
|
||||||
for (int i = 0; i < requestCount; i++) {
|
|
||||||
final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
|
|
||||||
// Cancel the request but don't mark it as dirty since there's no need to send
|
|
||||||
// notifications if the process has died.
|
|
||||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
|
|
||||||
false /* markDirty */);
|
|
||||||
}
|
|
||||||
|
|
||||||
mProcessRecords.remove(processRecord.mPid);
|
mProcessRecords.remove(processRecord.mPid);
|
||||||
|
mOverrideRequestController.handleProcessDied(processRecord.mPid);
|
||||||
updatePendingStateLocked();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyPolicyIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestStateInternal(int state, int flags, int callingPid,
|
private void requestStateInternal(int state, int flags, int callingPid,
|
||||||
@@ -577,7 +586,7 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
+ " has no registered callback.");
|
+ " has no registered callback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (processRecord.mRequestRecords.get(token) != null) {
|
if (mOverrideRequestController.hasRequest(token)) {
|
||||||
throw new IllegalStateException("Request has already been made for the supplied"
|
throw new IllegalStateException("Request has already been made for the supplied"
|
||||||
+ " token: " + token);
|
+ " token: " + token);
|
||||||
}
|
}
|
||||||
@@ -588,27 +597,9 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
+ " is not supported.");
|
+ " is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
|
OverrideRequest request = new OverrideRequest(token, callingPid, state, flags);
|
||||||
? null : mRequestRecords.get(mRequestRecords.size() - 1);
|
mOverrideRequestController.addRequest(request);
|
||||||
if (topRecord != null) {
|
|
||||||
topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final OverrideRequestRecord request =
|
|
||||||
new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
|
|
||||||
mRequestRecords.add(request);
|
|
||||||
processRecord.mRequestRecords.put(request.mToken, request);
|
|
||||||
|
|
||||||
final boolean updatedPendingState = updatePendingStateLocked();
|
|
||||||
if (!updatedPendingState && !mPendingState.isPresent()) {
|
|
||||||
// We don't set the status of the new request to ACTIVE if the request updated the
|
|
||||||
// pending state as it will be set in commitPendingState().
|
|
||||||
request.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE, true /* markDirty */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyRequestsOfStatusChangeIfNeeded();
|
|
||||||
notifyPolicyIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
|
private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
|
||||||
@@ -619,18 +610,8 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
+ " has no registered callback.");
|
+ " has no registered callback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
|
mOverrideRequestController.cancelRequest(token);
|
||||||
if (request == null) {
|
|
||||||
throw new IllegalStateException("No known request for the given token");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
|
|
||||||
|
|
||||||
updatePendingStateLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyRequestsOfStatusChangeIfNeeded();
|
|
||||||
notifyPolicyIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dumpInternal(PrintWriter pw) {
|
private void dumpInternal(PrintWriter pw) {
|
||||||
@@ -650,16 +631,7 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
pw.println(" " + i + ": mPid=" + processRecord.mPid);
|
pw.println(" " + i + ": mPid=" + processRecord.mPid);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int requestCount = mRequestRecords.size();
|
mOverrideRequestController.dumpInternal(pw);
|
||||||
pw.println();
|
|
||||||
pw.println("Override requests: size=" + requestCount);
|
|
||||||
for (int i = 0; i < requestCount; i++) {
|
|
||||||
OverrideRequestRecord requestRecord = mRequestRecords.get(i);
|
|
||||||
pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
|
|
||||||
+ ", mRequestedState=" + requestRecord.mRequestedState
|
|
||||||
+ ", mFlags=" + requestRecord.mFlags
|
|
||||||
+ ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,142 +655,107 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ProcessRecord implements IBinder.DeathRecipient {
|
private static final class ProcessRecord implements IBinder.DeathRecipient {
|
||||||
|
public interface DeathListener {
|
||||||
|
void onProcessDied(ProcessRecord record);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int STATUS_ACTIVE = 0;
|
||||||
|
|
||||||
|
private static final int STATUS_SUSPENDED = 1;
|
||||||
|
|
||||||
|
private static final int STATUS_CANCELED = 2;
|
||||||
|
|
||||||
|
@IntDef(prefix = {"STATUS_"}, value = {
|
||||||
|
STATUS_ACTIVE,
|
||||||
|
STATUS_SUSPENDED,
|
||||||
|
STATUS_CANCELED
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
private @interface RequestStatus {}
|
||||||
|
|
||||||
private final IDeviceStateManagerCallback mCallback;
|
private final IDeviceStateManagerCallback mCallback;
|
||||||
private final int mPid;
|
private final int mPid;
|
||||||
|
private final DeathListener mDeathListener;
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
|
private final WeakHashMap<IBinder, Integer> mLastNotifiedStatus = new WeakHashMap<>();
|
||||||
|
|
||||||
ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
|
ProcessRecord(IDeviceStateManagerCallback callback, int pid, DeathListener deathListener,
|
||||||
|
Handler handler) {
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
mPid = pid;
|
mPid = pid;
|
||||||
|
mDeathListener = deathListener;
|
||||||
|
mHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
handleProcessDied(this);
|
mDeathListener.onProcessDied(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
|
public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
|
||||||
|
mHandler.post(() -> {
|
||||||
try {
|
try {
|
||||||
mCallback.onDeviceStateInfoChanged(info);
|
mCallback.onDeviceStateInfoChanged(info);
|
||||||
} catch (RemoteException ex) {
|
} catch (RemoteException ex) {
|
||||||
Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
|
Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
|
||||||
ex);
|
ex);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyRequestActiveAsync(OverrideRequestRecord request) {
|
public void notifyRequestActiveAsync(IBinder token) {
|
||||||
try {
|
@RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
|
||||||
mCallback.onRequestActive(request.mToken);
|
if (lastStatus != null
|
||||||
} catch (RemoteException ex) {
|
&& (lastStatus == STATUS_ACTIVE || lastStatus == STATUS_CANCELED)) {
|
||||||
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
|
|
||||||
try {
|
|
||||||
mCallback.onRequestSuspended(request.mToken);
|
|
||||||
} catch (RemoteException ex) {
|
|
||||||
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
|
|
||||||
try {
|
|
||||||
mCallback.onRequestCanceled(request.mToken);
|
|
||||||
} catch (RemoteException ex) {
|
|
||||||
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A record describing a request to override the state of the device. */
|
|
||||||
private final class OverrideRequestRecord {
|
|
||||||
public static final int STATUS_UNKNOWN = 0;
|
|
||||||
public static final int STATUS_ACTIVE = 1;
|
|
||||||
public static final int STATUS_SUSPENDED = 2;
|
|
||||||
public static final int STATUS_CANCELED = 3;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String statusToString(int status) {
|
|
||||||
switch (status) {
|
|
||||||
case STATUS_ACTIVE:
|
|
||||||
return "ACTIVE";
|
|
||||||
case STATUS_SUSPENDED:
|
|
||||||
return "SUSPENDED";
|
|
||||||
case STATUS_CANCELED:
|
|
||||||
return "CANCELED";
|
|
||||||
case STATUS_UNKNOWN:
|
|
||||||
return "UNKNOWN";
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ProcessRecord mProcessRecord;
|
|
||||||
@NonNull
|
|
||||||
private final IBinder mToken;
|
|
||||||
@NonNull
|
|
||||||
private final DeviceState mRequestedState;
|
|
||||||
private final int mFlags;
|
|
||||||
|
|
||||||
private int mStatus = STATUS_UNKNOWN;
|
|
||||||
private int mLastNotifiedStatus = STATUS_UNKNOWN;
|
|
||||||
|
|
||||||
OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
|
|
||||||
@NonNull DeviceState requestedState, int flags) {
|
|
||||||
mProcessRecord = processRecord;
|
|
||||||
mToken = token;
|
|
||||||
mRequestedState = requestedState;
|
|
||||||
mFlags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatusLocked(int status) {
|
|
||||||
setStatusLocked(status, true /* markDirty */);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatusLocked(int status, boolean markDirty) {
|
|
||||||
if (mStatus != status) {
|
|
||||||
if (mStatus == STATUS_CANCELED) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Can not alter the status of a request after set to CANCELED.");
|
|
||||||
}
|
|
||||||
|
|
||||||
mStatus = status;
|
|
||||||
|
|
||||||
if (mStatus == STATUS_CANCELED) {
|
|
||||||
mRequestRecords.remove(this);
|
|
||||||
mProcessRecord.mRequestRecords.remove(mToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (markDirty) {
|
|
||||||
mRequestsPendingStatusChange.add(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyStatusIfNeeded() {
|
|
||||||
int stateToReport;
|
|
||||||
synchronized (mLock) {
|
|
||||||
if (mLastNotifiedStatus == mStatus) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stateToReport = mStatus;
|
mLastNotifiedStatus.put(token, STATUS_ACTIVE);
|
||||||
mLastNotifiedStatus = mStatus;
|
mHandler.post(() -> {
|
||||||
|
try {
|
||||||
|
mCallback.onRequestActive(token);
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateToReport == STATUS_ACTIVE) {
|
public void notifyRequestSuspendedAsync(IBinder token) {
|
||||||
mProcessRecord.notifyRequestActiveAsync(this);
|
@RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
|
||||||
} else if (stateToReport == STATUS_SUSPENDED) {
|
if (lastStatus != null
|
||||||
mProcessRecord.notifyRequestSuspendedAsync(this);
|
&& (lastStatus == STATUS_SUSPENDED || lastStatus == STATUS_CANCELED)) {
|
||||||
} else if (stateToReport == STATUS_CANCELED) {
|
return;
|
||||||
mProcessRecord.notifyRequestCanceledAsync(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
|
||||||
|
mHandler.post(() -> {
|
||||||
|
try {
|
||||||
|
mCallback.onRequestSuspended(token);
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyRequestCanceledAsync(IBinder token) {
|
||||||
|
@RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
|
||||||
|
if (lastStatus != null && lastStatus == STATUS_CANCELED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastNotifiedStatus.put(token, STATUS_CANCELED);
|
||||||
|
mHandler.post(() -> {
|
||||||
|
try {
|
||||||
|
mCallback.onRequestCanceled(token);
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,14 +785,21 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
|
|
||||||
@Override // Binder call
|
@Override // Binder call
|
||||||
public void requestState(IBinder token, int state, int flags) {
|
public void requestState(IBinder token, int state, int flags) {
|
||||||
|
final int callingPid = Binder.getCallingPid();
|
||||||
|
// Allow top processes to request a device state change
|
||||||
|
// If the calling process ID is not the top app, then we check if this process
|
||||||
|
// holds a permission to CONTROL_DEVICE_STATE
|
||||||
|
final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
|
||||||
|
if (topApp.getPid() != callingPid) {
|
||||||
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
||||||
"Permission required to request device state.");
|
"Permission required to request device state, "
|
||||||
|
+ "or the call must come from the top focused app.");
|
||||||
|
}
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
throw new IllegalArgumentException("Request token must not be null.");
|
throw new IllegalArgumentException("Request token must not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final int callingPid = Binder.getCallingPid();
|
|
||||||
final long callingIdentity = Binder.clearCallingIdentity();
|
final long callingIdentity = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
requestStateInternal(state, flags, callingPid, token);
|
requestStateInternal(state, flags, callingPid, token);
|
||||||
@@ -866,14 +810,21 @@ public final class DeviceStateManagerService extends SystemService {
|
|||||||
|
|
||||||
@Override // Binder call
|
@Override // Binder call
|
||||||
public void cancelRequest(IBinder token) {
|
public void cancelRequest(IBinder token) {
|
||||||
|
final int callingPid = Binder.getCallingPid();
|
||||||
|
// Allow top processes to cancel a device state change
|
||||||
|
// If the calling process ID is not the top app, then we check if this process
|
||||||
|
// holds a permission to CONTROL_DEVICE_STATE
|
||||||
|
final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
|
||||||
|
if (topApp.getPid() != callingPid) {
|
||||||
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
||||||
"Permission required to clear requested device state.");
|
"Permission required to cancel device state, "
|
||||||
|
+ "or the call must come from the top focused app.");
|
||||||
|
}
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
throw new IllegalArgumentException("Request token must not be null.");
|
throw new IllegalArgumentException("Request token must not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final int callingPid = Binder.getCallingPid();
|
|
||||||
final long callingIdentity = Binder.clearCallingIdentity();
|
final long callingIdentity = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
cancelRequestInternal(callingPid, token);
|
cancelRequestInternal(callingPid, token);
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.hardware.devicestate.DeviceStateRequest;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to override the state managed by {@link DeviceStateManagerService}.
|
||||||
|
*
|
||||||
|
* @see OverrideRequestController
|
||||||
|
*/
|
||||||
|
final class OverrideRequest {
|
||||||
|
private final IBinder mToken;
|
||||||
|
private final int mPid;
|
||||||
|
private final int mRequestedState;
|
||||||
|
@DeviceStateRequest.RequestFlags
|
||||||
|
private final int mFlags;
|
||||||
|
|
||||||
|
OverrideRequest(IBinder token, int pid, int requestedState,
|
||||||
|
@DeviceStateRequest.RequestFlags int flags) {
|
||||||
|
mToken = token;
|
||||||
|
mPid = pid;
|
||||||
|
mRequestedState = requestedState;
|
||||||
|
mFlags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
IBinder getToken() {
|
||||||
|
return mToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPid() {
|
||||||
|
return mPid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRequestedState() {
|
||||||
|
return mRequestedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeviceStateRequest.RequestFlags
|
||||||
|
int getFlags() {
|
||||||
|
return mFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,322 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.IntDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.hardware.devicestate.DeviceStateRequest;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the lifecycle of override requests.
|
||||||
|
* <p>
|
||||||
|
* New requests are added with {@link #addRequest(OverrideRequest)} and are kept active until
|
||||||
|
* either:
|
||||||
|
* <ul>
|
||||||
|
* <li>A new request is added with {@link #addRequest(OverrideRequest)}, in which case the
|
||||||
|
* request will become suspended.</li>
|
||||||
|
* <li>The request is cancelled with {@link #cancelRequest(IBinder)} or as a side effect
|
||||||
|
* of other methods calls, such as {@link #handleProcessDied(int)}.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
final class OverrideRequestController {
|
||||||
|
static final int STATUS_UNKNOWN = 0;
|
||||||
|
/**
|
||||||
|
* The request is the top-most request.
|
||||||
|
*/
|
||||||
|
static final int STATUS_ACTIVE = 1;
|
||||||
|
/**
|
||||||
|
* The request is still present but is being superseded by another request.
|
||||||
|
*/
|
||||||
|
static final int STATUS_SUSPENDED = 2;
|
||||||
|
/**
|
||||||
|
* The request is not longer valid.
|
||||||
|
*/
|
||||||
|
static final int STATUS_CANCELED = 3;
|
||||||
|
|
||||||
|
@IntDef(prefix = {"STATUS_"}, value = {
|
||||||
|
STATUS_UNKNOWN,
|
||||||
|
STATUS_ACTIVE,
|
||||||
|
STATUS_SUSPENDED,
|
||||||
|
STATUS_CANCELED
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@interface RequestStatus {}
|
||||||
|
|
||||||
|
static String statusToString(@RequestStatus int status) {
|
||||||
|
switch (status) {
|
||||||
|
case STATUS_ACTIVE:
|
||||||
|
return "ACTIVE";
|
||||||
|
case STATUS_SUSPENDED:
|
||||||
|
return "SUSPENDED";
|
||||||
|
case STATUS_CANCELED:
|
||||||
|
return "CANCELED";
|
||||||
|
case STATUS_UNKNOWN:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown status: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final StatusChangeListener mListener;
|
||||||
|
private final List<OverrideRequest> mTmpRequestsToCancel = new ArrayList<>();
|
||||||
|
|
||||||
|
// List of override requests with the most recent override request at the end.
|
||||||
|
private final ArrayList<OverrideRequest> 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<OverrideRequest> 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.
|
||||||
|
*/
|
||||||
|
void addRequest(@NonNull OverrideRequest request) {
|
||||||
|
mRequests.add(request);
|
||||||
|
mListener.onStatusChanged(request, STATUS_ACTIVE);
|
||||||
|
|
||||||
|
if (mRequests.size() > 1) {
|
||||||
|
OverrideRequest prevRequest = mRequests.get(mRequests.size() - 2);
|
||||||
|
mListener.onStatusChanged(prevRequest, STATUS_SUSPENDED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the request with the specified {@code token} and notifies the listener of all changes
|
||||||
|
* to request status as a result of this operation.
|
||||||
|
*/
|
||||||
|
void cancelRequest(@NonNull IBinder token) {
|
||||||
|
int index = getRequestIndex(token);
|
||||||
|
if (index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverrideRequest request = mRequests.remove(index);
|
||||||
|
if (index == mRequests.size() && mRequests.size() > 0) {
|
||||||
|
// We removed the current active request so we need to set the new active request
|
||||||
|
// before cancelling this request.
|
||||||
|
OverrideRequest newTop = getLast(mRequests);
|
||||||
|
mListener.onStatusChanged(newTop, STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
boolean hasRequest(@NonNull IBinder token) {
|
||||||
|
return getRequestIndex(token) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the controller that the process with the specified {@code pid} has died. The
|
||||||
|
* controller will notify the listener of all changes to request status as a result of this
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
void handleProcessDied(int pid) {
|
||||||
|
if (mRequests.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTmpRequestsToCancel.clear();
|
||||||
|
OverrideRequest prevActiveRequest = getLast(mRequests);
|
||||||
|
for (OverrideRequest request : mRequests) {
|
||||||
|
if (request.getPid() == pid) {
|
||||||
|
mTmpRequestsToCancel.add(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRequestsLocked(mTmpRequestsToCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the controller that the base state has changed. The controller will notify the
|
||||||
|
* listener of all changes to request status as a result of this change.
|
||||||
|
*
|
||||||
|
* @return {@code true} if calling this method has lead to a new active request, {@code false}
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
boolean handleBaseStateChanged() {
|
||||||
|
if (mRequests.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTmpRequestsToCancel.clear();
|
||||||
|
OverrideRequest prevActiveRequest = getLast(mRequests);
|
||||||
|
for (int i = 0; i < mRequests.size(); i++) {
|
||||||
|
OverrideRequest request = mRequests.get(i);
|
||||||
|
if ((request.getFlags() & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) {
|
||||||
|
mTmpRequestsToCancel.add(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
|
||||||
|
return newActiveRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the controller that the set of supported states has changed. The controller will
|
||||||
|
* notify the listener of all changes to request status as a result of this change.
|
||||||
|
*
|
||||||
|
* @return {@code true} if calling this method has lead to a new active request, {@code false}
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
boolean handleNewSupportedStates(int[] newSupportedStates) {
|
||||||
|
if (mRequests.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTmpRequestsToCancel.clear();
|
||||||
|
for (int i = 0; i < mRequests.size(); i++) {
|
||||||
|
OverrideRequest request = mRequests.get(i);
|
||||||
|
if (!contains(newSupportedStates, request.getRequestedState())) {
|
||||||
|
mTmpRequestsToCancel.add(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
|
||||||
|
return newActiveRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dumpInternal(PrintWriter pw) {
|
||||||
|
final int requestCount = mRequests.size();
|
||||||
|
pw.println();
|
||||||
|
pw.println("Override requests: size=" + requestCount);
|
||||||
|
for (int i = 0; i < requestCount; i++) {
|
||||||
|
OverrideRequest overrideRequest = mRequests.get(i);
|
||||||
|
int status = (i == requestCount - 1) ? STATUS_ACTIVE : STATUS_SUSPENDED;
|
||||||
|
pw.println(" " + i + ": mPid=" + overrideRequest.getPid()
|
||||||
|
+ ", mRequestedState=" + overrideRequest.getRequestedState()
|
||||||
|
+ ", mFlags=" + overrideRequest.getFlags()
|
||||||
|
+ ", mStatus=" + statusToString(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<OverrideRequest> 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) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfRequests; i++) {
|
||||||
|
OverrideRequest request = mRequests.get(i);
|
||||||
|
if (request.getToken() == token) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static <T> T getLast(List<T> list) {
|
||||||
|
return list.size() > 0 ? list.get(list.size() - 1) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean contains(int[] array, int value) {
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
if (array[i] == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface StatusChangeListener {
|
||||||
|
/**
|
||||||
|
* Notifies the listener of a change in request status. If a change within the controller
|
||||||
|
* causes one request to become active and one to become either suspended or cancelled, this
|
||||||
|
* method is guaranteed to be called with the active request first before the suspended or
|
||||||
|
* cancelled request.
|
||||||
|
*/
|
||||||
|
void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ import com.android.server.devicestate.DeviceState;
|
|||||||
import com.android.server.devicestate.DeviceStateProvider;
|
import com.android.server.devicestate.DeviceStateProvider;
|
||||||
import com.android.server.policy.devicestate.config.Conditions;
|
import com.android.server.policy.devicestate.config.Conditions;
|
||||||
import com.android.server.policy.devicestate.config.DeviceStateConfig;
|
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.LidSwitchCondition;
|
||||||
import com.android.server.policy.devicestate.config.NumericRange;
|
import com.android.server.policy.devicestate.config.NumericRange;
|
||||||
import com.android.server.policy.devicestate.config.SensorCondition;
|
import com.android.server.policy.devicestate.config.SensorCondition;
|
||||||
@@ -87,7 +88,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
|
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 VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
|
||||||
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
|
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
|
||||||
@@ -131,7 +132,26 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
|
|||||||
config.getDeviceState()) {
|
config.getDeviceState()) {
|
||||||
final int state = stateConfig.getIdentifier().intValue();
|
final int state = stateConfig.getIdentifier().intValue();
|
||||||
final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
|
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<String> 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();
|
final Conditions condition = stateConfig.getConditions();
|
||||||
conditionsList.add(condition);
|
conditionsList.add(condition);
|
||||||
|
|||||||
@@ -40,10 +40,19 @@
|
|||||||
<xs:element name="name" type="xs:string" minOccurs="0">
|
<xs:element name="name" type="xs:string" minOccurs="0">
|
||||||
<xs:annotation name="nullable" />
|
<xs:annotation name="nullable" />
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="flags" type="flags" />
|
||||||
<xs:element name="conditions" type="conditions" />
|
<xs:element name="conditions" type="conditions" />
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="flags">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="flag" type="xs:string" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:annotation name="nullable" />
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="conditions">
|
<xs:complexType name="conditions">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0">
|
<xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0">
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ package com.android.server.policy.devicestate.config {
|
|||||||
public class DeviceState {
|
public class DeviceState {
|
||||||
ctor public DeviceState();
|
ctor public DeviceState();
|
||||||
method public com.android.server.policy.devicestate.config.Conditions getConditions();
|
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 public java.math.BigInteger getIdentifier();
|
||||||
method @Nullable public String getName();
|
method @Nullable public String getName();
|
||||||
method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
|
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 setIdentifier(java.math.BigInteger);
|
||||||
method public void setName(@Nullable String);
|
method public void setName(@Nullable String);
|
||||||
}
|
}
|
||||||
@@ -23,6 +25,11 @@ package com.android.server.policy.devicestate.config {
|
|||||||
method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
|
method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Flags {
|
||||||
|
ctor public Flags();
|
||||||
|
method @Nullable public java.util.List<java.lang.String> getFlag();
|
||||||
|
}
|
||||||
|
|
||||||
public class LidSwitchCondition {
|
public class LidSwitchCondition {
|
||||||
ctor public LidSwitchCondition();
|
ctor public LidSwitchCondition();
|
||||||
method public boolean getOpen();
|
method public boolean getOpen();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.server.devicestate;
|
|||||||
|
|
||||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
import static org.testng.Assert.assertFalse;
|
import static org.testng.Assert.assertFalse;
|
||||||
import static org.testng.Assert.assertNotNull;
|
import static org.testng.Assert.assertNotNull;
|
||||||
@@ -35,6 +36,11 @@ import android.platform.test.annotations.Presubmit;
|
|||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||||
|
import com.android.server.wm.WindowProcessController;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -55,10 +61,15 @@ import javax.annotation.Nullable;
|
|||||||
@Presubmit
|
@Presubmit
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class DeviceStateManagerServiceTest {
|
public final class DeviceStateManagerServiceTest {
|
||||||
private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
|
private static final DeviceState DEFAULT_DEVICE_STATE =
|
||||||
private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER");
|
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.
|
// 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 static final int FAKE_PROCESS_ID = 100;
|
||||||
|
|
||||||
private TestDeviceStatePolicy mPolicy;
|
private TestDeviceStatePolicy mPolicy;
|
||||||
private TestDeviceStateProvider mProvider;
|
private TestDeviceStateProvider mProvider;
|
||||||
@@ -69,6 +80,25 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
mProvider = new TestDeviceStateProvider();
|
mProvider = new TestDeviceStateProvider();
|
||||||
mPolicy = new TestDeviceStatePolicy(mProvider);
|
mPolicy = new TestDeviceStatePolicy(mProvider);
|
||||||
mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
|
mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
|
||||||
|
|
||||||
|
// Necessary to allow us to check for top app process id in tests
|
||||||
|
mService.mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
|
||||||
|
WindowProcessController windowProcessController = mock(WindowProcessController.class);
|
||||||
|
when(mService.mActivityTaskManagerInternal.getTopApp())
|
||||||
|
.thenReturn(windowProcessController);
|
||||||
|
when(windowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
|
||||||
|
|
||||||
|
flushHandler(); // Flush the handler to ensure the initial values are committed.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushHandler() {
|
||||||
|
flushHandler(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushHandler(int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
mService.getHandler().runWithScissors(() -> {}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,6 +110,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
|
||||||
assertEquals(mService.getPendingState(), Optional.empty());
|
assertEquals(mService.getPendingState(), Optional.empty());
|
||||||
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
|
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
|
||||||
@@ -92,6 +123,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
mPolicy.blockConfigure();
|
mPolicy.blockConfigure();
|
||||||
|
|
||||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
|
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
|
||||||
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
|
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
|
||||||
@@ -99,6 +131,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
|
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
|
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
|
||||||
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
@@ -106,6 +139,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mPolicy.resumeConfigure();
|
mPolicy.resumeConfigure();
|
||||||
|
flushHandler();
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
assertEquals(mService.getPendingState(), Optional.empty());
|
assertEquals(mService.getPendingState(), Optional.empty());
|
||||||
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
@@ -149,6 +183,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
|
|
||||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
// The current committed and requests states do not change because the current state remains
|
// The current committed and requests states do not change because the current state remains
|
||||||
// supported.
|
// supported.
|
||||||
@@ -166,6 +201,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
|
||||||
// An initial callback will be triggered on registration, so we clear it here.
|
// An initial callback will be triggered on registration, so we clear it here.
|
||||||
|
flushHandler();
|
||||||
callback.clearLastNotifiedInfo();
|
callback.clearLastNotifiedInfo();
|
||||||
|
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
@@ -174,6 +210,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
|
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
|
||||||
OTHER_DEVICE_STATE });
|
OTHER_DEVICE_STATE });
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
// The current committed and requests states do not change because the current state remains
|
// The current committed and requests states do not change because the current state remains
|
||||||
// supported.
|
// supported.
|
||||||
@@ -203,12 +240,14 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
|
||||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
assertEquals(callback.getLastNotifiedInfo().baseState,
|
assertEquals(callback.getLastNotifiedInfo().baseState,
|
||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
assertEquals(callback.getLastNotifiedInfo().currentState,
|
assertEquals(callback.getLastNotifiedInfo().currentState,
|
||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
|
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
assertEquals(callback.getLastNotifiedInfo().baseState,
|
assertEquals(callback.getLastNotifiedInfo().baseState,
|
||||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
assertEquals(callback.getLastNotifiedInfo().currentState,
|
assertEquals(callback.getLastNotifiedInfo().currentState,
|
||||||
@@ -216,6 +255,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mPolicy.blockConfigure();
|
mPolicy.blockConfigure();
|
||||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
// The callback should not have been notified of the state change as the policy is still
|
// The callback should not have been notified of the state change as the policy is still
|
||||||
// pending callback.
|
// pending callback.
|
||||||
assertEquals(callback.getLastNotifiedInfo().baseState,
|
assertEquals(callback.getLastNotifiedInfo().baseState,
|
||||||
@@ -224,6 +264,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mPolicy.resumeConfigure();
|
mPolicy.resumeConfigure();
|
||||||
|
flushHandler();
|
||||||
// Now that the policy is finished processing the callback should be notified of the state
|
// Now that the policy is finished processing the callback should be notified of the state
|
||||||
// change.
|
// change.
|
||||||
assertEquals(callback.getLastNotifiedInfo().baseState,
|
assertEquals(callback.getLastNotifiedInfo().baseState,
|
||||||
@@ -236,6 +277,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
public void registerCallback_emitsInitialValue() throws RemoteException {
|
public void registerCallback_emitsInitialValue() throws RemoteException {
|
||||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
flushHandler();
|
||||||
assertNotNull(callback.getLastNotifiedInfo());
|
assertNotNull(callback.getLastNotifiedInfo());
|
||||||
assertEquals(callback.getLastNotifiedInfo().baseState,
|
assertEquals(callback.getLastNotifiedInfo().baseState,
|
||||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
@@ -247,6 +289,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
public void requestState() throws RemoteException {
|
public void requestState() throws RemoteException {
|
||||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
final IBinder token = new Binder();
|
final IBinder token = new Binder();
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
@@ -254,6 +297,10 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
||||||
0 /* flags */);
|
0 /* flags */);
|
||||||
|
// Flush the handler twice. The first flush ensures the request is added and the policy is
|
||||||
|
// notified, while the second flush ensures the callback is notified once the change is
|
||||||
|
// committed.
|
||||||
|
flushHandler(2 /* count */);
|
||||||
|
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||||
@@ -271,6 +318,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mService.getBinderService().cancelRequest(token);
|
mService.getBinderService().cancelRequest(token);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
TestDeviceStateManagerCallback.STATUS_CANCELED);
|
TestDeviceStateManagerCallback.STATUS_CANCELED);
|
||||||
@@ -291,6 +339,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
public void requestState_pendingStateAtRequest() throws RemoteException {
|
public void requestState_pendingStateAtRequest() throws RemoteException {
|
||||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
mPolicy.blockConfigure();
|
mPolicy.blockConfigure();
|
||||||
|
|
||||||
@@ -303,6 +352,10 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mService.getBinderService().requestState(firstRequestToken,
|
mService.getBinderService().requestState(firstRequestToken,
|
||||||
OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */);
|
OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */);
|
||||||
|
// Flush the handler twice. The first flush ensures the request is added and the policy is
|
||||||
|
// notified, while the second flush ensures the callback is notified once the change is
|
||||||
|
// committed.
|
||||||
|
flushHandler(2 /* count */);
|
||||||
|
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
|
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
|
||||||
@@ -312,8 +365,8 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mService.getBinderService().requestState(secondRequestToken,
|
mService.getBinderService().requestState(secondRequestToken,
|
||||||
DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */);
|
DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */);
|
||||||
|
|
||||||
mPolicy.resumeConfigureOnce();
|
mPolicy.resumeConfigureOnce();
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
// First request status is now suspended as there is another pending request.
|
// First request status is now suspended as there is another pending request.
|
||||||
assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
|
assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
|
||||||
@@ -330,6 +383,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mPolicy.resumeConfigure();
|
mPolicy.resumeConfigure();
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
|
||||||
assertEquals(mService.getPendingState(), Optional.empty());
|
assertEquals(mService.getPendingState(), Optional.empty());
|
||||||
@@ -339,6 +393,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
// Now cancel the second request to make the first request active.
|
// Now cancel the second request to make the first request active.
|
||||||
mService.getBinderService().cancelRequest(secondRequestToken);
|
mService.getBinderService().cancelRequest(secondRequestToken);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
|
assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
|
||||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||||
@@ -356,6 +411,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
public void requestState_sameAsBaseState() throws RemoteException {
|
public void requestState_sameAsBaseState() throws RemoteException {
|
||||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
final IBinder token = new Binder();
|
final IBinder token = new Binder();
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
@@ -363,6 +419,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
|
mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
|
||||||
0 /* flags */);
|
0 /* flags */);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||||
@@ -372,6 +429,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
|
public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
|
||||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
final IBinder token = new Binder();
|
final IBinder token = new Binder();
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
@@ -379,6 +437,10 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
||||||
DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
|
DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
|
||||||
|
// Flush the handler twice. The first flush ensures the request is added and the policy is
|
||||||
|
// notified, while the second flush ensures the callback is notified once the change is
|
||||||
|
// committed.
|
||||||
|
flushHandler(2 /* count */);
|
||||||
|
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||||
@@ -391,6 +453,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
// Request is canceled because the base state changed.
|
// Request is canceled because the base state changed.
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
@@ -407,6 +470,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
public void requestState_becomesUnsupported() throws RemoteException {
|
public void requestState_becomesUnsupported() throws RemoteException {
|
||||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||||
mService.getBinderService().registerCallback(callback);
|
mService.getBinderService().registerCallback(callback);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
final IBinder token = new Binder();
|
final IBinder token = new Binder();
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
@@ -414,6 +478,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
|
|
||||||
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
||||||
0 /* flags */);
|
0 /* flags */);
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||||
@@ -425,6 +490,7 @@ public final class DeviceStateManagerServiceTest {
|
|||||||
OTHER_DEVICE_STATE.getIdentifier());
|
OTHER_DEVICE_STATE.getIdentifier());
|
||||||
|
|
||||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
||||||
|
flushHandler();
|
||||||
|
|
||||||
// Request is canceled because the state is no longer supported.
|
// Request is canceled because the state is no longer supported.
|
||||||
assertEquals(callback.getLastNotifiedStatus(token),
|
assertEquals(callback.getLastNotifiedStatus(token),
|
||||||
|
|||||||
@@ -41,24 +41,26 @@ public final class DeviceStateTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testConstruct() {
|
public void testConstruct() {
|
||||||
final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
|
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.getIdentifier(), MINIMUM_DEVICE_STATE);
|
||||||
assertEquals(state.getName(), "CLOSED");
|
assertEquals(state.getName(), "CLOSED");
|
||||||
|
assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstruct_nullName() {
|
public void testConstruct_nullName() {
|
||||||
final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
|
final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
|
||||||
null /* name */);
|
null /* name */, 0/* flags */);
|
||||||
assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
|
assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
|
||||||
assertNull(state.getName());
|
assertNull(state.getName());
|
||||||
|
assertEquals(state.getFlags(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstruct_tooLargeIdentifier() {
|
public void testConstruct_tooLargeIdentifier() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */,
|
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() {
|
public void testConstruct_tooSmallIdentifier() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
|
final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
|
||||||
null /* name */);
|
null /* name */, 0 /* flags */);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.OverrideRequestController.STATUS_ACTIVE;
|
||||||
|
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
|
||||||
|
import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertNull;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.hardware.devicestate.DeviceStateRequest;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.platform.test.annotations.Presubmit;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link OverrideRequestController}.
|
||||||
|
* <p/>
|
||||||
|
* Run with <code>atest OverrideRequestControllerTest</code>.
|
||||||
|
*/
|
||||||
|
@Presubmit
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public final class OverrideRequestControllerTest {
|
||||||
|
private TestStatusChangeListener mStatusListener;
|
||||||
|
private OverrideRequestController mController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mStatusListener = new TestStatusChangeListener();
|
||||||
|
mController = new OverrideRequestController(mStatusListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addRequest() {
|
||||||
|
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */, 0 /* flags */);
|
||||||
|
assertNull(mStatusListener.getLastStatus(request));
|
||||||
|
|
||||||
|
mController.addRequest(request);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addRequest_suspendExistingRequest() {
|
||||||
|
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */, 0 /* flags */);
|
||||||
|
assertNull(mStatusListener.getLastStatus(firstRequest));
|
||||||
|
|
||||||
|
mController.addRequest(firstRequest);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
|
||||||
|
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */, 0 /* flags */);
|
||||||
|
assertNull(mStatusListener.getLastStatus(secondRequest));
|
||||||
|
|
||||||
|
mController.addRequest(secondRequest);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addRequest_cancelActiveRequest() {
|
||||||
|
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */, 0 /* flags */);
|
||||||
|
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* 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.cancelRequest(secondRequest.getToken());
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addRequest_cancelSuspendedRequest() {
|
||||||
|
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */, 0 /* flags */);
|
||||||
|
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* 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.cancelRequest(firstRequest.getToken());
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleBaseStateChanged() {
|
||||||
|
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */, 0 /* flags */);
|
||||||
|
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
0 /* requestedState */,
|
||||||
|
DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */);
|
||||||
|
|
||||||
|
mController.addRequest(firstRequest);
|
||||||
|
mController.addRequest(secondRequest);
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
|
||||||
|
|
||||||
|
mController.handleBaseStateChanged();
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleProcessDied() {
|
||||||
|
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_CANCELED);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
|
||||||
|
mController.handleProcessDied(0);
|
||||||
|
|
||||||
|
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 */,
|
||||||
|
1 /* requestedState */, 0 /* flags */);
|
||||||
|
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
|
||||||
|
2 /* requestedState */, 0 /* flags */);
|
||||||
|
|
||||||
|
mController.addRequest(firstRequest);
|
||||||
|
mController.addRequest(secondRequest);
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
|
||||||
|
|
||||||
|
mController.handleNewSupportedStates(new int[]{ 0, 1 });
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
|
||||||
|
|
||||||
|
mController.handleNewSupportedStates(new int[]{ 0 });
|
||||||
|
|
||||||
|
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TestStatusChangeListener implements
|
||||||
|
OverrideRequestController.StatusChangeListener {
|
||||||
|
private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(@NonNull OverrideRequest request, int newStatus) {
|
||||||
|
mLastStatusMap.put(request, newStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getLastStatus(OverrideRequest request) {
|
||||||
|
return mLastStatusMap.get(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -150,8 +150,9 @@ public final class DeviceStateProviderImplTest {
|
|||||||
provider.setListener(listener);
|
provider.setListener(listener);
|
||||||
|
|
||||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||||
final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""),
|
final DeviceState[] expectedStates = new DeviceState[]{
|
||||||
new DeviceState(2, "") };
|
new DeviceState(1, "", 0 /* flags */),
|
||||||
|
new DeviceState(2, "", 0 /* flags */) };
|
||||||
assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
|
assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
|
||||||
|
|
||||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||||
@@ -187,8 +188,9 @@ public final class DeviceStateProviderImplTest {
|
|||||||
provider.setListener(listener);
|
provider.setListener(listener);
|
||||||
|
|
||||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||||
final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""),
|
final DeviceState[] expectedStates = new DeviceState[]{
|
||||||
new DeviceState(2, "CLOSED") };
|
new DeviceState(1, "", 0 /* flags */),
|
||||||
|
new DeviceState(2, "CLOSED", 0 /* flags */) };
|
||||||
assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
|
assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
|
||||||
|
|
||||||
// onStateChanged() should not be called because the provider has not yet been notified of
|
// 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());
|
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||||
assertArrayEquals(
|
assertArrayEquals(
|
||||||
new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
|
new DeviceState[]{
|
||||||
new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue());
|
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
|
// onStateChanged() should not be called because the provider has not yet been notified of
|
||||||
// the initial sensor state.
|
// the initial sensor state.
|
||||||
verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
|
verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
|
||||||
@@ -350,7 +355,9 @@ public final class DeviceStateProviderImplTest {
|
|||||||
|
|
||||||
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
|
||||||
assertArrayEquals(
|
assertArrayEquals(
|
||||||
new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
|
new DeviceState[]{
|
||||||
|
new DeviceState(1, "CLOSED", 0 /* flags */),
|
||||||
|
new DeviceState(2, "HALF_OPENED", 0 /* flags */)
|
||||||
}, mDeviceStateArrayCaptor.getValue());
|
}, mDeviceStateArrayCaptor.getValue());
|
||||||
// onStateChanged() should be called because the provider could not find the sensor.
|
// onStateChanged() should be called because the provider could not find the sensor.
|
||||||
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
verify(listener).onStateChanged(mIntegerCaptor.capture());
|
||||||
|
|||||||
Reference in New Issue
Block a user