diff options
4 files changed, 146 insertions, 46 deletions
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java index 16be680b1770..2affb74b485c 100644 --- a/services/core/java/com/android/server/AppStateTracker.java +++ b/services/core/java/com/android/server/AppStateTracker.java @@ -266,6 +266,12 @@ public class AppStateTracker { // we need to deliver the allow-while-idle alarms for this uid, package unblockAllUnrestrictedAlarms(); } + + if (!sender.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)) { + Slog.v(TAG, "Package " + packageName + "/" + uid + + " toggled into fg service restriction"); + stopForegroundServicesForUidPackage(uid, packageName); + } } /** @@ -359,6 +365,13 @@ public class AppStateTracker { } /** + * Called when an app goes into forced app standby and its foreground + * services need to be removed from that state. + */ + public void stopForegroundServicesForUidPackage(int uid, String packageName) { + } + + /** * Called when the job restrictions for multiple UIDs might have changed, so the alarm * manager should re-evaluate all restrictions for all blocked jobs. */ @@ -1062,6 +1075,16 @@ public class AppStateTracker { } /** + * @return whether foreground services should be suppressed in the background + * due to forced app standby for the given app + */ + public boolean areForegroundServicesRestricted(int uid, @NonNull String packageName) { + synchronized (mLock) { + return isRunAnyRestrictedLocked(uid, packageName); + } + } + + /** * @return whether force-app-standby is effective for a UID package-name. */ private boolean isRestricted(int uid, @NonNull String packageName, diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index eb4e32e47489..73b1e333d02b 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -56,6 +56,8 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; +import com.android.server.AppStateTracker; +import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService.ItemMatcher; import com.android.server.am.ActivityManagerService.NeededUriGrants; import com.android.server.am.proto.ActiveServicesProto; @@ -165,6 +167,44 @@ public final class ActiveServices { }; /** + * Watch for apps being put into forced app standby, so we can step their fg + * services down. + */ + class ForcedStandbyListener extends AppStateTracker.Listener { + @Override + public void stopForegroundServicesForUidPackage(final int uid, final String packageName) { + synchronized (mAm) { + final ServiceMap smap = getServiceMapLocked(UserHandle.getUserId(uid)); + final int N = smap.mServicesByName.size(); + final ArrayList<ServiceRecord> toStop = new ArrayList<>(N); + for (int i = 0; i < N; i++) { + final ServiceRecord r = smap.mServicesByName.valueAt(i); + if (uid == r.serviceInfo.applicationInfo.uid + || packageName.equals(r.serviceInfo.packageName)) { + if (r.isForeground) { + toStop.add(r); + } + } + } + + // Now stop them all + final int numToStop = toStop.size(); + if (numToStop > 0 && DEBUG_FOREGROUND_SERVICE) { + Slog.i(TAG, "Package " + packageName + "/" + uid + + " entering FAS with foreground services"); + } + for (int i = 0; i < numToStop; i++) { + final ServiceRecord r = toStop.get(i); + if (DEBUG_FOREGROUND_SERVICE) { + Slog.i(TAG, " Stopping fg for service " + r); + } + setServiceForegroundInnerLocked(r, 0, null, 0); + } + } + } + } + + /** * Information about an app that is currently running one or more foreground services. * (This maps directly to the running apps we show in the notification.) */ @@ -302,6 +342,11 @@ public final class ActiveServices { ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8; } + void systemServicesReady() { + AppStateTracker ast = LocalServices.getService(AppStateTracker.class); + ast.addListener(new ForcedStandbyListener()); + } + ServiceRecord getServiceByNameLocked(ComponentName name, int callingUser) { // TODO: Deal with global services if (DEBUG_MU) @@ -327,6 +372,12 @@ public final class ActiveServices { return getServiceMapLocked(callingUser).mServicesByName; } + private boolean appRestrictedAnyInBackground(final int uid, final String packageName) { + final int mode = mAm.mAppOpsService.checkOperation( + AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName); + return (mode != AppOpsManager.MODE_ALLOWED); + } + ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId) throws TransactionTooLargeException { @@ -365,13 +416,24 @@ public final class ActiveServices { return null; } + // If the app has strict background restrictions, we treat any service + // start analogously to the legacy-app forced-restrictions case. + boolean forcedStandby = false; + if (appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { + if (DEBUG_FOREGROUND_SERVICE) { + Slog.d(TAG, "Forcing bg-only service start only for " + + r.name.flattenToShortString()); + } + forcedStandby = true; + } + // If this isn't a direct-to-foreground start, check our ability to kick off an // arbitrary service - if (!r.startRequested && !fgRequired) { + if (forcedStandby || (!r.startRequested && !fgRequired)) { // Before going further -- if this app is not allowed to start services in the // background, then at this point we aren't going to let it period. final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName, - r.appInfo.targetSdkVersion, callingPid, false, false); + r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.name.flattenToShortString() @@ -625,7 +687,7 @@ public final class ActiveServices { ServiceRecord service = services.mServicesByName.valueAt(i); if (service.appInfo.uid == uid && service.startRequested) { if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName, - service.appInfo.targetSdkVersion, -1, false, false) + service.appInfo.targetSdkVersion, -1, false, false, false) != ActivityManager.APP_START_MODE_NORMAL) { if (stopping == null) { stopping = new ArrayList<>(); @@ -1019,7 +1081,10 @@ public final class ActiveServices { } } - private void setServiceForegroundInnerLocked(ServiceRecord r, int id, + /** + * @param id Notification ID. Zero === exit foreground state for the given service. + */ + private void setServiceForegroundInnerLocked(final ServiceRecord r, int id, Notification notification, int flags) { if (id != 0) { if (notification == null) { @@ -1061,44 +1126,56 @@ public final class ActiveServices { mAm.mHandler.removeMessages( ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r); } - if (r.foregroundId != id) { - cancelForegroundNotificationLocked(r); - r.foregroundId = id; - } - notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; - r.foregroundNoti = notification; - if (!r.isForeground) { - final ServiceMap smap = getServiceMapLocked(r.userId); - if (smap != null) { - ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName); - if (active == null) { - active = new ActiveForegroundApp(); - active.mPackageName = r.packageName; - active.mUid = r.appInfo.uid; - active.mShownWhileScreenOn = mScreenOn; - if (r.app != null) { - active.mAppOnTop = active.mShownWhileTop = - r.app.uidRecord.curProcState - <= ActivityManager.PROCESS_STATE_TOP; + + // Apps under strict background restrictions simply don't get to have foreground + // services, so now that we've enforced the startForegroundService() contract + // we only do the machinery of making the service foreground when the app + // is not restricted. + if (!appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { + if (r.foregroundId != id) { + cancelForegroundNotificationLocked(r); + r.foregroundId = id; + } + notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; + r.foregroundNoti = notification; + if (!r.isForeground) { + final ServiceMap smap = getServiceMapLocked(r.userId); + if (smap != null) { + ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName); + if (active == null) { + active = new ActiveForegroundApp(); + active.mPackageName = r.packageName; + active.mUid = r.appInfo.uid; + active.mShownWhileScreenOn = mScreenOn; + if (r.app != null) { + active.mAppOnTop = active.mShownWhileTop = + r.app.uidRecord.curProcState + <= ActivityManager.PROCESS_STATE_TOP; + } + active.mStartTime = active.mStartVisibleTime + = SystemClock.elapsedRealtime(); + smap.mActiveForegroundApps.put(r.packageName, active); + requestUpdateActiveForegroundAppsLocked(smap, 0); } - active.mStartTime = active.mStartVisibleTime - = SystemClock.elapsedRealtime(); - smap.mActiveForegroundApps.put(r.packageName, active); - requestUpdateActiveForegroundAppsLocked(smap, 0); + active.mNumActive++; } - active.mNumActive++; + r.isForeground = true; + StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, + r.appInfo.uid, r.shortName, + StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER); + } + r.postNotification(); + if (r.app != null) { + updateServiceForegroundLocked(r.app, true); + } + getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r); + mAm.notifyPackageUse(r.serviceInfo.packageName, + PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE); + } else { + if (DEBUG_FOREGROUND_SERVICE) { + Slog.d(TAG, "Suppressing startForeground() for FAS " + r); } - r.isForeground = true; - StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName, - StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER); - } - r.postNotification(); - if (r.app != null) { - updateServiceForegroundLocked(r.app, true); } - getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r); - mAm.notifyPackageUse(r.serviceInfo.packageName, - PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE); } else { if (r.isForeground) { final ServiceMap smap = getServiceMapLocked(r.userId); @@ -1106,7 +1183,8 @@ public final class ActiveServices { decActiveForegroundAppLocked(smap, r); } r.isForeground = false; - StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName, + StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, + r.appInfo.uid, r.shortName, StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT); if (r.app != null) { mAm.updateLruProcessLocked(r.app, false, null); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 04d46aa75ac4..9f9844705baf 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -122,7 +122,6 @@ import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICAT import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; - import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; @@ -203,7 +202,6 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; - import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -388,8 +386,8 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.View; import android.view.WindowManager; - import android.view.autofill.AutofillManagerInternal; + import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -2859,6 +2857,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { mService.mBatteryStatsService.systemServicesReady(); + mService.mServices.systemServicesReady(); } } @@ -9101,7 +9100,7 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean isAppStartModeDisabled(int uid, String packageName) { synchronized (this) { - return getAppStartModeLocked(uid, packageName, 0, -1, false, true) + return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false) == ActivityManager.APP_START_MODE_DISABLED; } } @@ -9177,12 +9176,12 @@ public class ActivityManagerService extends IActivityManager.Stub } int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk, - int callingPid, boolean alwaysRestrict, boolean disabledOnly) { + int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) { UidRecord uidRec = mActiveUids.get(uid); if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg=" + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle=" + (uidRec != null ? uidRec.idle : false)); - if (uidRec == null || alwaysRestrict || uidRec.idle) { + if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) { boolean ephemeral; if (uidRec == null) { ephemeral = getPackageManagerInternalLocked().isPackageEphemeral( diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index ea90db3f5968..9ae3861d3318 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1258,7 +1258,7 @@ public final class BroadcastQueue { if (!skip) { final int allowed = mService.getAppStartModeLocked( info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, - info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false); + info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { // We won't allow this receiver to be launched if the app has been // completely disabled from launches, or it was not explicitly sent |