Add foreground check for controlling or requesting device state

In addition to checking if the app is on top, we should also
check if the app is in foreground. An app may be on top, but
can be hidden by keyguard.

Also note that we are doing both checks, becuase for example
in multi-window mode, multiple apps may be in the foreground.
However, only one of those is considered "top".

Bug: 270671994
Test: android.server.wm.jetpack.area.ExtensionRearDisplayPresentationKeyguardTest#testStartRearDisplayPresentation_whenKeyguardLocked_withoutShowWhenLocked
Change-Id: Iae4d67e55612e9df895a494bb1b891b546c262b9
This commit is contained in:
Kevin Chyn
2023-05-05 22:31:19 +00:00
parent 1de9f06456
commit 50f9e8baef

View File

@@ -17,6 +17,7 @@
package com.android.server.devicestate; package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE; import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
@@ -73,6 +74,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@@ -162,7 +164,7 @@ public final class DeviceStateManagerService extends SystemService {
@GuardedBy("mLock") @GuardedBy("mLock")
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
private Set<Integer> mDeviceStatesAvailableForAppRequests; private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
private Set<Integer> mFoldedDeviceStates; private Set<Integer> mFoldedDeviceStates;
@@ -879,8 +881,16 @@ public final class DeviceStateManagerService extends SystemService {
* @param callingPid Process ID that is requesting this state change * @param callingPid Process ID that is requesting this state change
* @param state state that is being requested. * @param state state that is being requested.
*/ */
private void assertCanRequestDeviceState(int callingPid, int state) { private void assertCanRequestDeviceState(int callingPid, int callingUid, int state) {
if (!isTopApp(callingPid) || !isStateAvailableForAppRequests(state)) { final boolean isTopApp = isTopApp(callingPid);
final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
final boolean isStateAvailableForAppRequests = isStateAvailableForAppRequests(state);
final boolean canRequestState = isTopApp
&& isForegroundApp
&& isStateAvailableForAppRequests;
if (!canRequestState) {
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 app " + "or the call must come from the top app "
@@ -893,15 +903,43 @@ public final class DeviceStateManagerService extends SystemService {
* not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission. * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
* *
* @param callingPid Process ID that is requesting this state change * @param callingPid Process ID that is requesting this state change
* @param callingUid UID that is requesting this state change
*/ */
private void assertCanControlDeviceState(int callingPid) { private void assertCanControlDeviceState(int callingPid, int callingUid) {
if (!isTopApp(callingPid)) { final boolean isTopApp = isTopApp(callingPid);
final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
final boolean canControlState = isTopApp && isForegroundApp;
if (!canControlState) {
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 app."); + "or the call must come from the top app.");
} }
} }
/**
* Checks if the caller is in the foreground. Note that callers may be the top app as returned
* from {@link #isTopApp(int)}, but not be in the foreground. For example, keyguard may be on
* top of the top app.
*/
private boolean isForegroundApp(int callingPid, int callingUid) {
try {
final List<ActivityManager.RunningAppProcessInfo> procs =
ActivityManager.getService().getRunningAppProcesses();
for (int i = 0; i < procs.size(); i++) {
ActivityManager.RunningAppProcessInfo proc = procs.get(i);
if (proc.pid == callingPid && proc.uid == callingUid
&& proc.importance <= IMPORTANCE_FOREGROUND) {
return true;
}
}
} catch (RemoteException e) {
Slog.w(TAG, "am.getRunningAppProcesses() failed", e);
}
return false;
}
private boolean isTopApp(int callingPid) { private boolean isTopApp(int callingPid) {
final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
return topApp != null && topApp.getPid() == callingPid; return topApp != null && topApp.getPid() == callingPid;
@@ -918,7 +956,6 @@ public final class DeviceStateManagerService extends SystemService {
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
private void readStatesAvailableForRequestFromApps() { private void readStatesAvailableForRequestFromApps() {
mDeviceStatesAvailableForAppRequests = new HashSet<>();
String[] availableAppStatesConfigIdentifiers = getContext().getResources() String[] availableAppStatesConfigIdentifiers = getContext().getResources()
.getStringArray(R.array.config_deviceStatesAvailableForAppRequests); .getStringArray(R.array.config_deviceStatesAvailableForAppRequests);
for (int i = 0; i < availableAppStatesConfigIdentifiers.length; i++) { for (int i = 0; i < availableAppStatesConfigIdentifiers.length; i++) {
@@ -1118,7 +1155,7 @@ public final class DeviceStateManagerService extends SystemService {
// Allow top processes to request a device state change // 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 // If the calling process ID is not the top app, then we check if this process
// holds a permission to CONTROL_DEVICE_STATE // holds a permission to CONTROL_DEVICE_STATE
assertCanRequestDeviceState(callingPid, state); assertCanRequestDeviceState(callingPid, callingUid, state);
if (token == null) { if (token == null) {
throw new IllegalArgumentException("Request token must not be null."); throw new IllegalArgumentException("Request token must not be null.");
@@ -1139,10 +1176,11 @@ public final class DeviceStateManagerService extends SystemService {
@Override // Binder call @Override // Binder call
public void cancelStateRequest() { public void cancelStateRequest() {
final int callingPid = Binder.getCallingPid(); final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
// Allow top processes to cancel a device state change // 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 // If the calling process ID is not the top app, then we check if this process
// holds a permission to CONTROL_DEVICE_STATE // holds a permission to CONTROL_DEVICE_STATE
assertCanControlDeviceState(callingPid); assertCanControlDeviceState(callingPid, callingUid);
final long callingIdentity = Binder.clearCallingIdentity(); final long callingIdentity = Binder.clearCallingIdentity();
try { try {