summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/AppStateTracker.java23
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java156
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
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