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

Commit 59ee488c authored by Christopher Tate's avatar Christopher Tate
Browse files

Infra for SysUI to monitor FGSes

There's now a way for it to track what apps currently have services in
the FGS state in real time.

Bug: 192504071
Test: manual
Test: atest CtsAppTestCases:android.app.cts.ServiceTest
Ignore-AOSP-First: new API should not be revealed in AOSP prior to drop
Change-Id: Ief1b6f8ef7deba6abbbf42f20b3f498861c1fe69
parent 8d85ed8a
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@ import android.app.GrantedUriPermission;
import android.app.IApplicationThread;
import android.app.IApplicationThread;
import android.app.IActivityController;
import android.app.IActivityController;
import android.app.IAppTask;
import android.app.IAppTask;
import android.app.IForegroundServiceObserver;
import android.app.IInstrumentationWatcher;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IServiceConnection;
@@ -279,6 +280,8 @@ interface IActivityManager {
    boolean clearApplicationUserData(in String packageName, boolean keepState,
    boolean clearApplicationUserData(in String packageName, boolean keepState,
            in IPackageDataObserver observer, int userId);
            in IPackageDataObserver observer, int userId);
    void makeServicesNonForeground(in String packageName, int userId);
    void makeServicesNonForeground(in String packageName, int userId);
    /** Returns {@code false} if the callback could not be registered, {@true} otherwise. */
    boolean registerForegroundServiceObserver(in IForegroundServiceObserver callback);
    @UnsupportedAppUsage
    @UnsupportedAppUsage
    void forceStopPackage(in String packageName, int userId);
    void forceStopPackage(in String packageName, int userId);
    boolean killPids(in int[] pids, in String reason, boolean secure);
    boolean killPids(in int[] pids, in String reason, boolean secure);
+31 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.app;

/**
 * Notify the client of all changes to services' foreground state.
 * @param serviceToken unique identifier for a service instance
 * @param packageName identifies the app hosting the service
 * @param userId identifies the started user in which the app is running
 * @param isForeground whether the service is in the "foreground" mode now, i.e.
 *     whether it is an FGS
 *
 * @hide
 */
oneway interface IForegroundServiceObserver {
    void onForegroundStateChanged(in IBinder serviceToken, in String packageName, int userId, boolean isForeground);
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,7 @@ per-file IActivityController.aidl = file:/services/core/java/com/android/server/
per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IForegroundServiceObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS
+70 −1
Original line number Original line Diff line number Diff line
@@ -90,6 +90,7 @@ import android.app.AppOpsManager;
import android.app.ForegroundServiceDidNotStartInTimeException;
import android.app.ForegroundServiceDidNotStartInTimeException;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.IApplicationThread;
import android.app.IApplicationThread;
import android.app.IForegroundServiceObserver;
import android.app.IServiceConnection;
import android.app.IServiceConnection;
import android.app.Notification;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager;
@@ -128,6 +129,7 @@ import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.Process;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemClock;
@@ -266,6 +268,12 @@ public final class ActiveServices {
     */
     */
    final SparseLongArray mFgsDeferralEligible = new SparseLongArray();
    final SparseLongArray mFgsDeferralEligible = new SparseLongArray();


    /**
     * Foreground service observers: track what apps have FGSes
     */
    final RemoteCallbackList<IForegroundServiceObserver> mFgsObservers =
            new RemoteCallbackList<>();

    /**
    /**
     * Map of services that are asked to be brought up (start/binding) but not ready to.
     * Map of services that are asked to be brought up (start/binding) but not ready to.
     */
     */
@@ -1382,6 +1390,10 @@ public final class ActiveServices {
        return false;
        return false;
    }
    }


    /**
     * Put the named service into the foreground mode
     */
    @GuardedBy("mAm")
    public void setServiceForegroundLocked(ComponentName className, IBinder token,
    public void setServiceForegroundLocked(ComponentName className, IBinder token,
            int id, Notification notification, int flags, int foregroundServiceType) {
            int id, Notification notification, int flags, int foregroundServiceType) {
        final int userId = UserHandle.getCallingUserId();
        final int userId = UserHandle.getCallingUserId();
@@ -1746,6 +1758,7 @@ public final class ActiveServices {
    /**
    /**
     * @param id Notification ID.  Zero === exit foreground state for the given service.
     * @param id Notification ID.  Zero === exit foreground state for the given service.
     */
     */
    @GuardedBy("mAm")
    private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
    private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
            Notification notification, int flags, int foregroundServiceType) {
            Notification notification, int flags, int foregroundServiceType) {
        if (id != 0) {
        if (id != 0) {
@@ -1981,6 +1994,7 @@ public final class ActiveServices {
                    }
                    }
                    // Even if the service is already a FGS, we need to update the notification,
                    // Even if the service is already a FGS, we need to update the notification,
                    // so we need to call it again.
                    // so we need to call it again.
                    signalForegroundServiceObserversLocked(r);
                    r.postNotification();
                    r.postNotification();
                    if (r.app != null) {
                    if (r.app != null) {
                        updateServiceForegroundLocked(psr, true);
                        updateServiceForegroundLocked(psr, true);
@@ -2060,6 +2074,7 @@ public final class ActiveServices {
                        r.mFgsExitTime > r.mFgsEnterTime
                        r.mFgsExitTime > r.mFgsEnterTime
                                ? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
                                ? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
                r.mFgsNotificationWasDeferred = false;
                r.mFgsNotificationWasDeferred = false;
                signalForegroundServiceObserversLocked(r);
                resetFgsRestrictionLocked(r);
                resetFgsRestrictionLocked(r);
                mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                if (r.app != null) {
                if (r.app != null) {
@@ -4552,7 +4567,8 @@ public final class ActiveServices {
        }
        }


        cancelForegroundNotificationLocked(r);
        cancelForegroundNotificationLocked(r);
        if (r.isForeground) {
        final boolean exitingFg = r.isForeground;
        if (exitingFg) {
            decActiveForegroundAppLocked(smap, r);
            decActiveForegroundAppLocked(smap, r);
            synchronized (mAm.mProcessStats.mLock) {
            synchronized (mAm.mProcessStats.mLock) {
                ServiceState stracker = r.getTracker();
                ServiceState stracker = r.getTracker();
@@ -4578,6 +4594,11 @@ public final class ActiveServices {
        r.foregroundId = 0;
        r.foregroundId = 0;
        r.foregroundNoti = null;
        r.foregroundNoti = null;
        resetFgsRestrictionLocked(r);
        resetFgsRestrictionLocked(r);
        // Signal FGS observers *after* changing the isForeground state, and
        // only if this was an actual state change.
        if (exitingFg) {
            signalForegroundServiceObserversLocked(r);
        }


        // Clear start entries.
        // Clear start entries.
        r.clearDeliveredStartsLocked();
        r.clearDeliveredStartsLocked();
@@ -5133,6 +5154,54 @@ public final class ActiveServices {
        }
        }
    }
    }


    @GuardedBy("mAm")
    private void signalForegroundServiceObserversLocked(ServiceRecord r) {
        final int num = mFgsObservers.beginBroadcast();
        for (int i = 0; i < num; i++) {
            try {
                mFgsObservers.getBroadcastItem(i).onForegroundStateChanged(r,
                        r.appInfo.packageName, r.userId, r.isForeground);
            } catch (RemoteException e) {
                // Will be unregistered automatically by RemoteCallbackList's dead-object
                // tracking, so nothing we need to do here.
            }
        }
        mFgsObservers.finishBroadcast();
    }

    @GuardedBy("mAm")
    boolean registerForegroundServiceObserverLocked(final int callingUid,
            IForegroundServiceObserver callback) {
        // We always tell the newly-registered observer about any current FGSes.  The
        // most common case for this is a SysUI crash & relaunch; it needs to
        // reconstruct its tracking of stoppable-FGS-hosting apps.
        try {
            final int mapSize = mServiceMap.size();
            for (int mapIndex = 0; mapIndex < mapSize; mapIndex++) {
                final ServiceMap smap = mServiceMap.valueAt(mapIndex);
                if (smap != null) {
                    final int numServices = smap.mServicesByInstanceName.size();
                    for (int i = 0; i < numServices; i++) {
                        final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
                        if (sr.isForeground && callingUid == sr.appInfo.uid) {
                            callback.onForegroundStateChanged(sr, sr.appInfo.packageName,
                                    sr.userId, true);
                        }
                    }
                }
            }
            // Callback is fine, go ahead and record it
            mFgsObservers.register(callback);
        } catch (RemoteException e) {
            // Whoops, something wrong with the callback.  Don't register it, and
            // report error back to the caller.
            Slog.e(TAG_SERVICE, "Bad FGS observer from uid " + callingUid);
            return false;
        }

        return true;
    }

    void forceStopPackageLocked(String packageName, int userId) {
    void forceStopPackageLocked(String packageName, int userId) {
        ServiceMap smap = mServiceMap.get(userId);
        ServiceMap smap = mServiceMap.get(userId);
        if (smap != null && smap.mActiveForegroundApps.size() > 0) {
        if (smap != null && smap.mActiveForegroundApps.size() > 0) {
+25 −2
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
@@ -167,6 +168,7 @@ import android.app.ContentProviderHolder;
import android.app.IActivityController;
import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IApplicationThread;
import android.app.IForegroundServiceObserver;
import android.app.IInstrumentationWatcher;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IProcessObserver;
@@ -3753,12 +3755,12 @@ public class ActivityManagerService extends IActivityManager.Stub
    @Override
    @Override
    public void makeServicesNonForeground(final String packageName, int userId) {
    public void makeServicesNonForeground(final String packageName, int userId) {
        if (checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
        if (checkCallingPermission(MANAGE_ACTIVITY_TASKS)
                != PackageManager.PERMISSION_GRANTED) {
                != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: makeServicesNonForeground() from pid="
            String msg = "Permission Denial: makeServicesNonForeground() from pid="
                    + Binder.getCallingPid()
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
                    + " requires " + MANAGE_ACTIVITY_TASKS;
            Slog.w(TAG, msg);
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
            throw new SecurityException(msg);
        }
        }
@@ -3774,6 +3776,27 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
        }
    }
    }
    @Override
    public boolean registerForegroundServiceObserver(IForegroundServiceObserver callback) {
        final int callingUid = Binder.getCallingUid();
        final int permActivityTasks = checkCallingPermission(MANAGE_ACTIVITY_TASKS);
        final int permAcrossUsersFull = checkCallingPermission(INTERACT_ACROSS_USERS_FULL);
        if (permActivityTasks != PackageManager.PERMISSION_GRANTED
                || permAcrossUsersFull != PERMISSION_GRANTED) {
            String msg = "Permission Denial: registerForegroundServiceObserver() from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + callingUid
                    + " requires " + MANAGE_ACTIVITY_TASKS
                    + " and " + INTERACT_ACROSS_USERS_FULL;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        synchronized (this) {
            return mServices.registerForegroundServiceObserverLocked(callingUid, callback);
        }
    }
    @Override
    @Override
    public void forceStopPackage(final String packageName, int userId) {
    public void forceStopPackage(final String packageName, int userId) {
        if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
        if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)