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:
@@ -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);
|
||||
|
||||
31
core/java/android/app/IForegroundServiceObserver.aidl
Normal file
31
core/java/android/app/IForegroundServiceObserver.aidl
Normal 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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user