Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7efefc63 authored by Christopher Tate's avatar Christopher Tate
Browse files

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
parent d7be67b7
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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);
+23 −0
Original line number Diff line number Diff line
@@ -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) {
+35 −0
Original line number Diff line number Diff line
@@ -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) {
+18 −0
Original line number Diff line number Diff line
@@ -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;