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
This commit is contained in:
Christopher Tate
2021-11-03 12:15:13 -07:00
parent 8d85ed8a32
commit 59ee488c49
5 changed files with 130 additions and 3 deletions

View File

@@ -26,6 +26,7 @@ import android.app.GrantedUriPermission;
import android.app.IApplicationThread;
import android.app.IActivityController;
import android.app.IAppTask;
import android.app.IForegroundServiceObserver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
@@ -279,6 +280,8 @@ interface IActivityManager {
boolean clearApplicationUserData(in String packageName, boolean keepState,
in IPackageDataObserver observer, 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
void forceStopPackage(in String packageName, int userId);
boolean killPids(in int[] pids, in String reason, boolean secure);

View File

@@ -0,0 +1,31 @@
/*
* 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);
}

View File

@@ -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 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 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 IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS

View File

@@ -90,6 +90,7 @@ import android.app.AppOpsManager;
import android.app.ForegroundServiceDidNotStartInTimeException;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.IApplicationThread;
import android.app.IForegroundServiceObserver;
import android.app.IServiceConnection;
import android.app.Notification;
import android.app.NotificationManager;
@@ -128,6 +129,7 @@ import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -266,6 +268,12 @@ public final class ActiveServices {
*/
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.
*/
@@ -1382,6 +1390,10 @@ public final class ActiveServices {
return false;
}
/**
* Put the named service into the foreground mode
*/
@GuardedBy("mAm")
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, int flags, int foregroundServiceType) {
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.
*/
@GuardedBy("mAm")
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
Notification notification, int flags, int foregroundServiceType) {
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,
// so we need to call it again.
signalForegroundServiceObserversLocked(r);
r.postNotification();
if (r.app != null) {
updateServiceForegroundLocked(psr, true);
@@ -2060,6 +2074,7 @@ public final class ActiveServices {
r.mFgsExitTime > r.mFgsEnterTime
? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
r.mFgsNotificationWasDeferred = false;
signalForegroundServiceObserversLocked(r);
resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
@@ -4552,7 +4567,8 @@ public final class ActiveServices {
}
cancelForegroundNotificationLocked(r);
if (r.isForeground) {
final boolean exitingFg = r.isForeground;
if (exitingFg) {
decActiveForegroundAppLocked(smap, r);
synchronized (mAm.mProcessStats.mLock) {
ServiceState stracker = r.getTracker();
@@ -4578,6 +4594,11 @@ public final class ActiveServices {
r.foregroundId = 0;
r.foregroundNoti = null;
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.
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) {
ServiceMap smap = mServiceMap.get(userId);
if (smap != null && smap.mActiveForegroundApps.size() > 0) {

View File

@@ -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.INTERACT_ACROSS_USERS;
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_FOREGROUND_SERVICES_FROM_BACKGROUND;
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.IActivityManager;
import android.app.IApplicationThread;
import android.app.IForegroundServiceObserver;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
@@ -3753,12 +3755,12 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void makeServicesNonForeground(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
if (checkCallingPermission(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;
+ " requires " + MANAGE_ACTIVITY_TASKS;
Slog.w(TAG, 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
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)