Allow top focused activities to call requestState

Previously you could only modify the device state
if you held the CONTROL_DEVICE_STATE permission. This
change allows top focused activities to do the same

Bug: 192671754
Test: atest DeviceStateManagerTests
Change-Id: Iba0c1800a1fb17024020e657b76a54eec9c89348
This commit is contained in:
Kenneth Ford
2021-08-05 23:23:57 +00:00
parent 7dde391fc1
commit 1f70abe98d
4 changed files with 53 additions and 14 deletions

View File

@@ -1133,10 +1133,10 @@ package android.hardware.camera2 {
package android.hardware.devicestate {
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 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);
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0

View File

@@ -86,12 +86,13 @@ public final class DeviceStateManager {
* However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
*
* @throws IllegalArgumentException if the requested state is unsupported.
* @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
* permission is not held.
* @throws SecurityException if the caller is neither the current top-focused activity nor if
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
*
* @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,
@Nullable @CallbackExecutor Executor executor,
@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
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
*
* @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
* permission is not held.
* @throws SecurityException if the caller is neither the current top-focused activity nor if
* 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) {
mGlobal.cancelRequest(request);
}

View File

@@ -48,9 +48,12 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
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.policy.DeviceStatePolicyImpl;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowProcessController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -101,6 +104,9 @@ public final class DeviceStateManagerService extends SystemService {
private final BinderService mBinderService;
@NonNull
private final OverrideRequestController mOverrideRequestController;
@VisibleForTesting
@NonNull
public ActivityTaskManagerInternal mActivityTaskManagerInternal;
// All supported device states keyed by identifier.
@GuardedBy("mLock")
@@ -153,6 +159,7 @@ public final class DeviceStateManagerService extends SystemService {
mDeviceStatePolicy = policy;
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
mBinderService = new BinderService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
}
@Override
@@ -778,14 +785,21 @@ public final class DeviceStateManagerService extends SystemService {
@Override // Binder call
public void requestState(IBinder token, int state, int flags) {
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
"Permission required to request device state.");
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,
"Permission required to request device state, "
+ "or the call must come from the top focused app.");
}
if (token == null) {
throw new IllegalArgumentException("Request token must not be null.");
}
final int callingPid = Binder.getCallingPid();
final long callingIdentity = Binder.clearCallingIdentity();
try {
requestStateInternal(state, flags, callingPid, token);
@@ -796,14 +810,21 @@ public final class DeviceStateManagerService extends SystemService {
@Override // Binder call
public void cancelRequest(IBinder token) {
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
"Permission required to clear requested device state.");
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,
"Permission required to cancel device state, "
+ "or the call must come from the top focused app.");
}
if (token == null) {
throw new IllegalArgumentException("Request token must not be null.");
}
final int callingPid = Binder.getCallingPid();
final long callingIdentity = Binder.clearCallingIdentity();
try {
cancelRequestInternal(callingPid, token);

View File

@@ -18,6 +18,7 @@ package com.android.server.devicestate;
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.assertFalse;
import static org.testng.Assert.assertNotNull;
@@ -35,6 +36,11 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
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 org.junit.Before;
@@ -63,6 +69,8 @@ public final class DeviceStateManagerServiceTest {
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 TestDeviceStateProvider mProvider;
private DeviceStateManagerService mService;
@@ -72,6 +80,14 @@ public final class DeviceStateManagerServiceTest {
mProvider = new TestDeviceStateProvider();
mPolicy = new TestDeviceStatePolicy(mProvider);
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.
}