Merge "Add DeviceStateManagerService shell commands to get and override state."
This commit is contained in:
committed by
Android (Google) Code Review
commit
fa01004977
@@ -5160,6 +5160,12 @@
|
||||
<permission android:name="android.permission.INPUT_CONSUMER"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @hide Allows an application to control the system's device state managed by the
|
||||
{@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
|
||||
devices this would grant access to toggle between the folded and unfolded states. -->
|
||||
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- Attribution for Geofencing service. -->
|
||||
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
|
||||
<!-- Attribution for Country Detector. -->
|
||||
|
||||
@@ -338,6 +338,9 @@
|
||||
<!-- Permissions required for CTS test - NotificationManagerTest -->
|
||||
<uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
|
||||
|
||||
<!-- Allows overriding the system's device state from the shell -->
|
||||
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
|
||||
|
||||
<application android:label="@string/app_label"
|
||||
android:theme="@android:style/Theme.DeviceDefault.DayNight"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
package com.android.server.devicestate;
|
||||
|
||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.devicestate.IDeviceStateManager;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.util.IntArray;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -29,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.policy.DeviceStatePolicyImpl;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -65,15 +70,20 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
// The current committed device state.
|
||||
@GuardedBy("mLock")
|
||||
private int mCommittedState = INVALID_DEVICE_STATE;
|
||||
// The device state that is currently pending callback from the policy to be committed.
|
||||
// The device state that is currently awaiting callback from the policy to be committed.
|
||||
@GuardedBy("mLock")
|
||||
private int mPendingState = INVALID_DEVICE_STATE;
|
||||
// Whether or not the policy is currently waiting to be notified of the current pending state.
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsPolicyWaitingForState = false;
|
||||
// The device state that is currently requested and is next to be configured and committed.
|
||||
// Can be overwritten by an override state value if requested.
|
||||
@GuardedBy("mLock")
|
||||
private int mRequestedState = INVALID_DEVICE_STATE;
|
||||
// The most recently requested override state, or INVALID_DEVICE_STATE if no override is
|
||||
// requested.
|
||||
@GuardedBy("mLock")
|
||||
private int mRequestedOverrideState = INVALID_DEVICE_STATE;
|
||||
|
||||
public DeviceStateManagerService(@NonNull Context context) {
|
||||
this(context, new DeviceStatePolicyImpl());
|
||||
@@ -97,7 +107,6 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
*
|
||||
* @see #getPendingState()
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getCommittedState() {
|
||||
synchronized (mLock) {
|
||||
return mCommittedState;
|
||||
@@ -119,13 +128,61 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
* Returns the requested state. The service will configure the device to match the requested
|
||||
* state when possible.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getRequestedState() {
|
||||
synchronized (mLock) {
|
||||
return mRequestedState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the current device state with the provided state.
|
||||
*
|
||||
* @return {@code true} if the override state is valid and supported, {@code false} otherwise.
|
||||
*/
|
||||
boolean setOverrideState(int overrideState) {
|
||||
if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRequestedOverrideState = overrideState;
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyPolicyIfNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an override state set with {@link #setOverrideState(int)}.
|
||||
*/
|
||||
void clearOverrideState() {
|
||||
setOverrideState(INVALID_DEVICE_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current requested override state, or {@link #INVALID_DEVICE_STATE} is no override
|
||||
* state is requested.
|
||||
*/
|
||||
int getOverrideState() {
|
||||
synchronized (mLock) {
|
||||
return mRequestedOverrideState;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the list of currently supported device states. */
|
||||
int[] getSupportedStates() {
|
||||
synchronized (mLock) {
|
||||
// Copy array to prevent external modification of internal state.
|
||||
return Arrays.copyOf(mSupportedDeviceStates.toArray(), mSupportedDeviceStates.size());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSupportedStates(int[] supportedDeviceStates) {
|
||||
// Must ensure sorted as isSupportedStateLocked() impl uses binary search.
|
||||
Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
|
||||
@@ -135,11 +192,20 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
if (mRequestedState != INVALID_DEVICE_STATE
|
||||
&& !isSupportedStateLocked(mRequestedState)) {
|
||||
// The current requested state is no longer valid. We'll clear it here, though
|
||||
// we won't actually update the current state with a call to
|
||||
// updatePendingStateLocked() as doing so will not have any effect.
|
||||
// we won't actually update the current state until a callback comes from the
|
||||
// provider with the most recent state.
|
||||
mRequestedState = INVALID_DEVICE_STATE;
|
||||
}
|
||||
if (mRequestedOverrideState != INVALID_DEVICE_STATE
|
||||
&& !isSupportedStateLocked(mRequestedOverrideState)) {
|
||||
// The current override state is no longer valid. We'll clear it here and update
|
||||
// the committed state if necessary.
|
||||
mRequestedOverrideState = INVALID_DEVICE_STATE;
|
||||
}
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,27 +234,34 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to update the current configuring state with the current requested state. Must call
|
||||
* Tries to update the current pending state with the current requested state. Must call
|
||||
* {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
|
||||
* changed.
|
||||
*/
|
||||
private void updatePendingStateLocked() {
|
||||
if (mRequestedState == INVALID_DEVICE_STATE) {
|
||||
// No currently requested state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPendingState != INVALID_DEVICE_STATE) {
|
||||
// Have pending state, can not configure a new state until the state is committed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRequestedState == mCommittedState) {
|
||||
// No need to notify the policy as the committed state matches the requested state.
|
||||
int stateToConfigure;
|
||||
if (mRequestedOverrideState != INVALID_DEVICE_STATE) {
|
||||
stateToConfigure = mRequestedOverrideState;
|
||||
} else {
|
||||
stateToConfigure = mRequestedState;
|
||||
}
|
||||
|
||||
if (stateToConfigure == INVALID_DEVICE_STATE) {
|
||||
// No currently requested state.
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingState = mRequestedState;
|
||||
if (stateToConfigure == mCommittedState) {
|
||||
// The state requesting to be committed already matches the current committed state.
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingState = stateToConfigure;
|
||||
mIsPolicyWaitingForState = true;
|
||||
}
|
||||
|
||||
@@ -271,6 +344,11 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
|
||||
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
|
||||
private final class BinderService extends IDeviceStateManager.Stub {
|
||||
|
||||
@Override // Binder call
|
||||
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
|
||||
String[] args, ShellCallback callback, ResultReceiver result) {
|
||||
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
|
||||
.exec(this, in, out, err, args, callback, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.devicestate;
|
||||
|
||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
|
||||
import android.os.ShellCommand;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* ShellCommands for {@link DeviceStateManagerService}.
|
||||
*
|
||||
* Use with {@code adb shell cmd device_state ...}.
|
||||
*/
|
||||
public class DeviceStateManagerShellCommand extends ShellCommand {
|
||||
private final DeviceStateManagerService mInternal;
|
||||
|
||||
public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
|
||||
mInternal = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onCommand(String cmd) {
|
||||
if (cmd == null) {
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
|
||||
switch (cmd) {
|
||||
case "state":
|
||||
return runState(pw);
|
||||
case "print-states":
|
||||
return runPrintStates(pw);
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
private void printState(PrintWriter pw) {
|
||||
int committedState = mInternal.getCommittedState();
|
||||
int requestedState = mInternal.getRequestedState();
|
||||
int requestedOverrideState = mInternal.getOverrideState();
|
||||
|
||||
if (committedState == INVALID_DEVICE_STATE) {
|
||||
pw.println("Device state: (invalid)");
|
||||
} else {
|
||||
pw.println("Device state: " + committedState);
|
||||
}
|
||||
|
||||
if (requestedOverrideState != INVALID_DEVICE_STATE) {
|
||||
pw.println("----------------------");
|
||||
if (requestedState == INVALID_DEVICE_STATE) {
|
||||
pw.println("Base state: (invalid)");
|
||||
} else {
|
||||
pw.println("Base state: " + requestedState);
|
||||
}
|
||||
pw.println("Override state: " + committedState);
|
||||
}
|
||||
}
|
||||
|
||||
private int runState(PrintWriter pw) {
|
||||
final String nextArg = getNextArg();
|
||||
if (nextArg == null) {
|
||||
printState(pw);
|
||||
} else if ("reset".equals(nextArg)) {
|
||||
mInternal.clearOverrideState();
|
||||
} else {
|
||||
int requestedState;
|
||||
try {
|
||||
requestedState = Integer.parseInt(nextArg);
|
||||
} catch (NumberFormatException e) {
|
||||
getErrPrintWriter().println("Error: requested state should be an integer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean success = mInternal.setOverrideState(requestedState);
|
||||
if (!success) {
|
||||
getErrPrintWriter().println("Error: failed to set override state. Run:");
|
||||
getErrPrintWriter().println("");
|
||||
getErrPrintWriter().println(" print-states");
|
||||
getErrPrintWriter().println("");
|
||||
getErrPrintWriter().println("to get the list of currently supported device states");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runPrintStates(PrintWriter pw) {
|
||||
int[] states = mInternal.getSupportedStates();
|
||||
pw.print("Supported states: [ ");
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
pw.print(states[i]);
|
||||
if (i < states.length - 1) {
|
||||
pw.print(", ");
|
||||
}
|
||||
}
|
||||
pw.println(" ]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHelp() {
|
||||
PrintWriter pw = getOutPrintWriter();
|
||||
pw.println("Device state manager (device_state) commands:");
|
||||
pw.println(" help");
|
||||
pw.println(" Print this help text.");
|
||||
pw.println(" state [reset|OVERRIDE_DEVICE_STATE]");
|
||||
pw.println(" Return or override device state.");
|
||||
pw.println(" print-states");
|
||||
pw.println(" Return list of currently supported device states.");
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,30 @@ public final class DeviceStateManagerServiceTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestOverrideState() {
|
||||
mService.setOverrideState(OTHER_DEVICE_STATE);
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
|
||||
|
||||
// Committed state is set back to the requested state once the override is cleared.
|
||||
mService.clearOverrideState();
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestOverrideState_unsupportedState() {
|
||||
mService.setOverrideState(UNSUPPORTED_DEVICE_STATE);
|
||||
// Committed state remains the same as the override state is unsupported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
@@ -146,6 +170,23 @@ public final class DeviceStateManagerServiceTest {
|
||||
assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged_unsupportedOverrideState() {
|
||||
mService.setOverrideState(OTHER_DEVICE_STATE);
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
|
||||
|
||||
// Committed state is set back to the requested state as the override state is no longer
|
||||
// supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
|
||||
private final DeviceStateProvider mProvider;
|
||||
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
|
||||
|
||||
Reference in New Issue
Block a user