Implement demotion of an app's FGses

The OS now has an operation for wholesale shutting down all of an
application's foreground services.

Bug: 192504071
Test: atest CtsAppTestCases:android.app.cts.ServiceTest
Test: ApiDemos + shell command
Change-Id: I67d4469d5c20c25c717024fad80849c080c98190
This commit is contained in:
Christopher Tate
2021-10-21 11:46:07 -07:00
parent d7be67b717
commit 7efefc636b
5 changed files with 82 additions and 0 deletions

View File

@@ -517,6 +517,11 @@ public abstract class ActivityManagerInternal {
public abstract void onForegroundServiceNotificationUpdate(boolean shown,
Notification notification, int id, String pkg, @UserIdInt int userId);
/**
* Un-foreground all foreground services in the given app.
*/
public abstract void makeServicesNonForeground(String pkg, @UserIdInt int userId);
/**
* If the given app has any FGSs whose notifications are in the given channel,
* stop them.

View File

@@ -278,6 +278,7 @@ interface IActivityManager {
List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
boolean clearApplicationUserData(in String packageName, boolean keepState,
in IPackageDataObserver observer, int userId);
void makeServicesNonForeground(in String packageName, int userId);
@UnsupportedAppUsage
void forceStopPackage(in String packageName, int userId);
boolean killPids(in int[] pids, in String reason, boolean secure);

View File

@@ -5110,6 +5110,29 @@ public final class ActiveServices {
return didSomething;
}
void makeServicesNonForegroundLocked(final String pkg, final @UserIdInt int userId) {
final ServiceMap smap = mServiceMap.get(userId);
if (smap != null) {
ArrayList<ServiceRecord> fgsList = new ArrayList<>();
for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) {
final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
fgsList.add(sr);
}
}
final int numServices = fgsList.size();
if (DEBUG_FOREGROUND_SERVICE) {
Slog.i(TAG_SERVICE, "Forcing " + numServices + " services out of foreground in u"
+ userId + "/" + pkg);
}
for (int i = 0; i < numServices; i++) {
final ServiceRecord sr = fgsList.get(i);
setServiceForegroundInnerLocked(sr, 0, null, Service.STOP_FOREGROUND_REMOVE, 0);
}
}
}
void forceStopPackageLocked(String packageName, int userId) {
ServiceMap smap = mServiceMap.get(userId);
if (smap != null && smap.mActiveForegroundApps.size() > 0) {

View File

@@ -3750,6 +3750,29 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
@Override
public void makeServicesNonForeground(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: makeServicesNonForeground() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final int callingPid = Binder.getCallingPid();
userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "makeServicesNonForeground", null);
final long callingId = Binder.clearCallingIdentity();
try {
makeServicesNonForegroundUnchecked(packageName, userId);
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
@@ -4058,6 +4081,13 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
private void makeServicesNonForegroundUnchecked(final String packageName,
final @UserIdInt int userId) {
synchronized (this) {
mServices.makeServicesNonForegroundLocked(packageName, userId);
}
}
@GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
@@ -16398,6 +16428,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
@Override
public void makeServicesNonForeground(String pkg, int userId) {
ActivityManagerService.this.makeServicesNonForegroundUnchecked(pkg, userId);
}
@Override
public void stopForegroundServicesForChannel(String pkg, int userId,
String channelId) {

View File

@@ -229,6 +229,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runBugReport(pw);
case "force-stop":
return runForceStop(pw);
case "stop-fgs":
return runStopForegroundServices(pw);
case "fgs-notification-rate-limit":
return runFgsNotificationRateLimit(pw);
case "crash":
@@ -1105,6 +1107,22 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
int runStopForegroundServices(PrintWriter pw) throws RemoteException {
int userId = UserHandle.USER_SYSTEM;
String opt;
while ((opt = getNextOption()) != null) {
if (opt.equals("--user")) {
userId = UserHandle.parseUserArg(getNextArgRequired());
} else {
getErrPrintWriter().println("Error: Unknown option: " + opt);
return -1;
}
}
mInterface.makeServicesNonForeground(getNextArgRequired(), userId);
return 0;
}
int runFgsNotificationRateLimit(PrintWriter pw) throws RemoteException {
final String toggleValue = getNextArgRequired();
final boolean enable;