Loading services/core/java/com/android/server/am/ActiveServices.java +121 −21 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; import static android.os.PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN; import static android.os.PowerExemptionManager.REASON_ACTIVITY_STARTER; import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; Loading Loading @@ -1286,6 +1287,8 @@ public final class ActiveServices { return; } maybeStopShortFgsTimeoutLocked(service); final int uid = service.appInfo.uid; final String packageName = service.name.getPackageName(); final String serviceName = service.name.getClassName(); Loading Loading @@ -1469,6 +1472,8 @@ public final class ActiveServices { } } maybeStopShortFgsTimeoutLocked(r); final int uid = r.appInfo.uid; final String packageName = r.name.getPackageName(); final String serviceName = r.name.getClassName(); Loading Loading @@ -1836,6 +1841,14 @@ public final class ActiveServices { + String.format("0x%08X", manifestType) + " in service element of manifest file"); } if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) != 0 && foregroundServiceType != FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) { Slog.w(TAG_SERVICE, "startForeground(): FOREGROUND_SERVICE_TYPE_SHORT_SERVICE" + " is combined with other types. SHORT_SERVICE will be ignored."); // In this case, the service will be handled as a non-short, regular FGS // anyway, so we just remove the SHORT_SERVICE type. foregroundServiceType &= ~FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; } } boolean alreadyStartedOp = false; Loading Loading @@ -1886,14 +1899,21 @@ public final class ActiveServices { int fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_UNKNOWN; if (!ignoreForeground) { // TODO(short-service): There's a known long-standing bug that allows // a abound service to become "foreground" if setForeground() is called // (without actually "starting" it). // Unfortunately we can't just "fix" it because some apps are relying on it, // but this will cause a problem to short-fgs, so we should disallow it if // this happens and the type is SHORT_SERVICE. // // OTOH, if a valid short-service (which has to be "started"), happens to if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE && !r.startRequested) { // There's a long standing bug that allows a bound service to become // a foreground service *even when it's not started*. // Unfortunately, there are apps relying on this behavior, so we can't just // suddenly disallow it. // However, this would be very problematic if used with a short-FGS, so we // explicitly disallow this combination. // TODO(short-service): Change to another exception type? throw new IllegalStateException( "startForeground(SHORT_SERVICE) called on a service that's not" + " started."); } // If a valid short-service (which has to be "started"), happens to // also be bound, then we still _will_ apply a timeout, because it still has // to be stopped. if (r.mStartForegroundCount == 0) { Loading Loading @@ -1944,12 +1964,20 @@ public final class ActiveServices { // If the transition is _not_ allowed... keep the timeout? // // B. Short -> Short: // This should be the same as case A // If this is allowed, the new timeout should start. // Allowed, but the timeout won't reset. The original timeout is used. // C. Other -> short: // This should always be allowed. // A timeout should start. // For now, let's just disallow transition from / to SHORT_SERVICE. final boolean isNewTypeShortFgs = foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; if (r.isShortFgs() != isNewTypeShortFgs) { // TODO(short-service): We should (probably) allow it. throw new IllegalArgumentException("Changing FGS type from / to " + " SHORT_SERVICE is now allowed"); } // The second or later time startForeground() is called after service is // started. Check for app state again. setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(), Loading Loading @@ -2106,8 +2134,11 @@ public final class ActiveServices { mAm.notifyPackageUse(r.serviceInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE); // TODO(short-service): Start counting a timeout. // Note, we'll get here if setForeground(SHORT_SERVICE) is called on a // already short-fgs. // In that case, because ShortFgsInfo is already set, this method // will be noop. maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(r); } else { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Suppressing startForeground() for FAS " + r); Loading Loading @@ -2141,7 +2172,7 @@ public final class ActiveServices { decActiveForegroundAppLocked(smap, r); } // TODO(short-service): Stop the timeout. (any better place to do it?) maybeStopShortFgsTimeoutLocked(r); // Adjust notification handling before setting isForeground to false, because // that state is relevant to the notification policy side. Loading Loading @@ -2886,6 +2917,74 @@ public final class ActiveServices { psr.setHasReportedForegroundServices(anyForeground); } void unscheduleShortFgsTimeoutLocked(ServiceRecord sr) { mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG, sr); mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr); } /** * If {@code sr} is of a short-fgs, start a short-FGS timeout. */ private void maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(ServiceRecord sr) { if (!sr.isShortFgs()) { return; } if (DEBUG_SHORT_SERVICE) { Slog.i(TAG_SERVICE, "Short FGS started: " + sr); } if (sr.hasShortFgsInfo()) { sr.getShortFgsInfo().update(); } else { sr.setShortFgsInfo(SystemClock.uptimeMillis()); } unscheduleShortFgsTimeoutLocked(sr); // Do it just in case final Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr); mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getTimeoutTime()); } /** * Stop the timeout for a ServiceRecord, if it's of a short-FGS. */ private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) { if (DEBUG_SHORT_SERVICE) { Slog.i(TAG_SERVICE, "Stop short FGS timeout: " + sr); } sr.clearShortFgsInfo(); unscheduleShortFgsTimeoutLocked(sr); } void onShortFgsTimeout(ServiceRecord sr) { synchronized (mAm) { if (!sr.shouldTriggerShortFgsTimeout()) { return; } Slog.i(TAG_SERVICE, "Short FGS timed out: " + sr); try { sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId()); } catch (RemoteException e) { // TODO(short-service): Anything to do here? } // Schedule the ANR timeout. final Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG, sr); mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getAnrTime()); } } void onShortFgsAnrTimeout(ServiceRecord sr) { // TODO(short-service): Implement ANR, and change the WTF to e(). // Also make sure to call waitingOnAMSLockStarted(). synchronized (mAm) { if (!sr.shouldTriggerShortFgsAnr()) { return; } } Slog.wtf(TAG_SERVICE, "Short FGS ANR'ed (NOT IMPLEMENTED YET): " + sr); } private void updateAllowlistManagerLocked(ProcessServiceRecord psr) { psr.mAllowlistManager = false; for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) { Loading @@ -2897,6 +2996,7 @@ public final class ActiveServices { } } // TODO(short-service): Hmm what is it? Should we stop the timeout here? private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) { final ProcessServiceRecord psr = service.app.mServices; psr.stopService(service); Loading Loading @@ -4178,7 +4278,7 @@ public final class ActiveServices { /** * Reschedule service restarts based on if the extra delays are enabled or not. * * @param prevEnable The previous state of whether or not it's enabled. * @param prevEnabled The previous state of whether or not it's enabled. * @param curEnabled The current state of whether or not it's enabled. * @param now The uptimeMillis */ Loading Loading @@ -4863,13 +4963,6 @@ public final class ActiveServices { Slog.i(TAG, "Bring down service for " + debugReason + " :" + r.toString()); } // TODO(short-service): Hmm, when the app stops a short-fgs, we should stop the timeout // here. // However we have a couple if's here and if these conditions are met, we stop here // without bringing down the service. // We need to make sure this can't be used (somehow) to keep having a short-FGS running // while having the timeout stopped. if (isServiceNeededLocked(r, knowConn, hasConn)) { return; } Loading @@ -4886,6 +4979,13 @@ public final class ActiveServices { //Slog.i(TAG, "Bring down service:"); //r.dump(" "); if (r.isShortFgs()) { // FGS can be stopped without the app calling stopService() or stopSelf(), // due to force-app-standby, or from Task Manager. Slog.w(TAG_SERVICE, "Short FGS brought down without stopping: " + r); maybeStopShortFgsTimeoutLocked(r); } // Report to all of the connections that the service is no longer // available. ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); Loading services/core/java/com/android/server/am/ActivityManagerConstants.java +3 −3 Original line number Diff line number Diff line Loading @@ -954,7 +954,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_SHORT_FGS_TIMEOUT_DURATION = 60_000; /** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */ public static volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION; public volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION; /** * If a "short service" doesn't finish within this after the timeout ( Loading @@ -967,7 +967,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION = 5_000; /** @see #KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION */ public static volatile long mShortFgsProcStateExtraWaitDuration = public volatile long mShortFgsProcStateExtraWaitDuration = DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION; /** Loading @@ -983,7 +983,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION = 10_000; /** @see #KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION */ public static volatile long mShortFgsAnrExtraWaitDuration = public volatile long mShortFgsAnrExtraWaitDuration = DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION; private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = Loading services/core/java/com/android/server/am/ActivityManagerService.java +8 −0 Original line number Diff line number Diff line Loading @@ -1563,6 +1563,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final int WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG = 73; static final int DISPATCH_SENDING_BROADCAST_EVENT = 74; static final int DISPATCH_BINDING_SERVICE_EVENT = 75; static final int SERVICE_SHORT_FGS_TIMEOUT_MSG = 76; static final int SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG = 77; static final int FIRST_BROADCAST_QUEUE_MSG = 200; Loading Loading @@ -1897,6 +1899,12 @@ public class ActivityManagerService extends IActivityManager.Stub mBindServiceEventListeners.forEach(l -> l.onBindingService((String) msg.obj, msg.arg1)); } break; case SERVICE_SHORT_FGS_TIMEOUT_MSG: { mServices.onShortFgsTimeout((ServiceRecord) msg.obj); } break; case SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG: { mServices.onShortFgsAnrTimeout((ServiceRecord) msg.obj); } break; } } } Loading services/core/java/com/android/server/am/OomAdjuster.java +8 −1 Original line number Diff line number Diff line Loading @@ -1818,7 +1818,14 @@ public class OomAdjuster { newAdj = PERCEPTIBLE_APP_ADJ; newProcState = PROCESS_STATE_IMPORTANT_FOREGROUND; } else if (psr.hasForegroundServices() && !psr.hasNonShortForegroundServices()) { } else if (psr.hasForegroundServices()) { // If we get here, hasNonShortForegroundServices() must be false. // TODO(short-service): If all the short-fgs (there may be multiple) within the // process is post proc-state-demote time, then we need to skip this part. // ... Or, should we just ANR take care of timed-out short-FGS and shouldn't bother // lowering the procstate / oom-adj...?? (likely not.) // For short FGS. adjType = "fg-service-short"; // We use MEDIUM_APP_ADJ + 1 so we can tell apart EJ (which uses MEDIUM_APP_ADJ + 1) Loading services/core/java/com/android/server/am/ServiceRecord.java +164 −2 Original line number Diff line number Diff line Loading @@ -315,6 +315,87 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>(); // start() arguments that haven't yet been delivered. /** * Information specific to "SHORT_SERVICE" FGS. */ class ShortFgsInfo { /** Time FGS started */ private final long mStartTime; /** * Copied from {@link #mStartForegroundCount}. If this is different from the parent's, * that means this instance is stale. */ private int mStartForegroundCount; /** Service's "start ID" when this short-service started. */ private int mStartId; ShortFgsInfo(long startTime) { mStartTime = startTime; update(); } /** * Update {@link #mStartForegroundCount} and {@link #mStartId}. * (but not {@link #mStartTime}) */ public void update() { this.mStartForegroundCount = ServiceRecord.this.mStartForegroundCount; this.mStartId = getLastStartId(); } long getStartTime() { return mStartTime; } int getStartForegroundCount() { return mStartForegroundCount; } int getStartId() { return mStartId; } /** * @return whether this {@link ShortFgsInfo} is still "current" or not -- i.e. * it's "start foreground count" is the same as that of the ServiceRecord's. * * Note, we do _not_ check the "start id" here, because the start id increments if the * app calls startService() or startForegroundService() on the same service, * but that will _not_ update the ShortFgsInfo, and will not extend the timeout. * * TODO(short-service): Make sure, calling startService will not extend or remove the * timeout, in CTS. */ boolean isCurrent() { return this.mStartForegroundCount == ServiceRecord.this.mStartForegroundCount; } /** Time when Service.onTimeout() should be called */ long getTimeoutTime() { return mStartTime + ams.mConstants.mShortFgsTimeoutDuration; } /** Time when the procstate should be lowered. */ long getProcStateDemoteTime() { return mStartTime + ams.mConstants.mShortFgsTimeoutDuration + ams.mConstants.mShortFgsProcStateExtraWaitDuration; } /** Time when the app should be declared ANR. */ long getAnrTime() { return mStartTime + ams.mConstants.mShortFgsTimeoutDuration + ams.mConstants.mShortFgsAnrExtraWaitDuration; } } /** * Keep track of short-fgs specific information. This field gets cleared when the timeout * stops. */ private ShortFgsInfo mShortFgsInfo; void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) { final int N = list.size(); for (int i=0; i<N; i++) { Loading Loading @@ -456,6 +537,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } proto.end(token); // TODO(short-service) Add FGS info } void dump(PrintWriter pw, String prefix) { Loading Loading @@ -509,7 +592,24 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (isForeground || foregroundId != 0) { pw.print(prefix); pw.print("isForeground="); pw.print(isForeground); pw.print(" foregroundId="); pw.print(foregroundId); pw.printf(" types=%08X", foregroundServiceType); pw.print(" foregroundNoti="); pw.println(foregroundNoti); if (isShortFgs() && mShortFgsInfo != null) { pw.print(prefix); pw.print("isShortFgs=true"); pw.print(" startId="); pw.print(mShortFgsInfo.getStartId()); pw.print(" startForegroundCount="); pw.print(mShortFgsInfo.getStartForegroundCount()); pw.print(" startTime="); TimeUtils.formatDuration(mShortFgsInfo.getStartTime(), now, pw); pw.print(" timeout="); TimeUtils.formatDuration(mShortFgsInfo.getTimeoutTime(), now, pw); pw.print(" demoteTime="); TimeUtils.formatDuration(mShortFgsInfo.getProcStateDemoteTime(), now, pw); pw.print(" anrTime="); TimeUtils.formatDuration(mShortFgsInfo.getAnrTime(), now, pw); pw.println(); } } if (mIsFgsDelegate) { pw.print(prefix); pw.print("isFgsDelegate="); pw.println(mIsFgsDelegate); Loading Loading @@ -1238,4 +1338,66 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return isForeground && (foregroundServiceType == ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE); } public ShortFgsInfo getShortFgsInfo() { return isShortFgs() ? mShortFgsInfo : null; } /** * Call it when a short FGS starts. */ public void setShortFgsInfo(long uptimeNow) { this.mShortFgsInfo = new ShortFgsInfo(uptimeNow); } /** @return whether {@link #mShortFgsInfo} is set or not. */ public boolean hasShortFgsInfo() { return mShortFgsInfo != null; } /** * Call it when a short FGS stops. */ public void clearShortFgsInfo() { this.mShortFgsInfo = null; } /** * @return true if it's a short FGS that's still up and running, and should be timed out. */ public boolean shouldTriggerShortFgsTimeout() { if (!isAppAlive()) { return false; } if (!this.startRequested || !isShortFgs() || mShortFgsInfo == null || !mShortFgsInfo.isCurrent()) { return false; } return mShortFgsInfo.getTimeoutTime() < SystemClock.uptimeMillis(); } /** * @return true if it's a short FGS that's still up and running, and should be declared * an ANR. */ public boolean shouldTriggerShortFgsAnr() { if (!isAppAlive()) { return false; } if (!this.startRequested || !isShortFgs() || mShortFgsInfo == null || !mShortFgsInfo.isCurrent()) { return false; } return mShortFgsInfo.getAnrTime() < SystemClock.uptimeMillis(); } private boolean isAppAlive() { if (app == null) { return false; } if (app.getThread() == null || app.isKilled() || app.isKilledByAm()) { return false; } return true; } } Loading
services/core/java/com/android/server/am/ActiveServices.java +121 −21 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; import static android.os.PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN; import static android.os.PowerExemptionManager.REASON_ACTIVITY_STARTER; import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; Loading Loading @@ -1286,6 +1287,8 @@ public final class ActiveServices { return; } maybeStopShortFgsTimeoutLocked(service); final int uid = service.appInfo.uid; final String packageName = service.name.getPackageName(); final String serviceName = service.name.getClassName(); Loading Loading @@ -1469,6 +1472,8 @@ public final class ActiveServices { } } maybeStopShortFgsTimeoutLocked(r); final int uid = r.appInfo.uid; final String packageName = r.name.getPackageName(); final String serviceName = r.name.getClassName(); Loading Loading @@ -1836,6 +1841,14 @@ public final class ActiveServices { + String.format("0x%08X", manifestType) + " in service element of manifest file"); } if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) != 0 && foregroundServiceType != FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) { Slog.w(TAG_SERVICE, "startForeground(): FOREGROUND_SERVICE_TYPE_SHORT_SERVICE" + " is combined with other types. SHORT_SERVICE will be ignored."); // In this case, the service will be handled as a non-short, regular FGS // anyway, so we just remove the SHORT_SERVICE type. foregroundServiceType &= ~FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; } } boolean alreadyStartedOp = false; Loading Loading @@ -1886,14 +1899,21 @@ public final class ActiveServices { int fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_UNKNOWN; if (!ignoreForeground) { // TODO(short-service): There's a known long-standing bug that allows // a abound service to become "foreground" if setForeground() is called // (without actually "starting" it). // Unfortunately we can't just "fix" it because some apps are relying on it, // but this will cause a problem to short-fgs, so we should disallow it if // this happens and the type is SHORT_SERVICE. // // OTOH, if a valid short-service (which has to be "started"), happens to if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE && !r.startRequested) { // There's a long standing bug that allows a bound service to become // a foreground service *even when it's not started*. // Unfortunately, there are apps relying on this behavior, so we can't just // suddenly disallow it. // However, this would be very problematic if used with a short-FGS, so we // explicitly disallow this combination. // TODO(short-service): Change to another exception type? throw new IllegalStateException( "startForeground(SHORT_SERVICE) called on a service that's not" + " started."); } // If a valid short-service (which has to be "started"), happens to // also be bound, then we still _will_ apply a timeout, because it still has // to be stopped. if (r.mStartForegroundCount == 0) { Loading Loading @@ -1944,12 +1964,20 @@ public final class ActiveServices { // If the transition is _not_ allowed... keep the timeout? // // B. Short -> Short: // This should be the same as case A // If this is allowed, the new timeout should start. // Allowed, but the timeout won't reset. The original timeout is used. // C. Other -> short: // This should always be allowed. // A timeout should start. // For now, let's just disallow transition from / to SHORT_SERVICE. final boolean isNewTypeShortFgs = foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; if (r.isShortFgs() != isNewTypeShortFgs) { // TODO(short-service): We should (probably) allow it. throw new IllegalArgumentException("Changing FGS type from / to " + " SHORT_SERVICE is now allowed"); } // The second or later time startForeground() is called after service is // started. Check for app state again. setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(), Loading Loading @@ -2106,8 +2134,11 @@ public final class ActiveServices { mAm.notifyPackageUse(r.serviceInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE); // TODO(short-service): Start counting a timeout. // Note, we'll get here if setForeground(SHORT_SERVICE) is called on a // already short-fgs. // In that case, because ShortFgsInfo is already set, this method // will be noop. maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(r); } else { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Suppressing startForeground() for FAS " + r); Loading Loading @@ -2141,7 +2172,7 @@ public final class ActiveServices { decActiveForegroundAppLocked(smap, r); } // TODO(short-service): Stop the timeout. (any better place to do it?) maybeStopShortFgsTimeoutLocked(r); // Adjust notification handling before setting isForeground to false, because // that state is relevant to the notification policy side. Loading Loading @@ -2886,6 +2917,74 @@ public final class ActiveServices { psr.setHasReportedForegroundServices(anyForeground); } void unscheduleShortFgsTimeoutLocked(ServiceRecord sr) { mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG, sr); mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr); } /** * If {@code sr} is of a short-fgs, start a short-FGS timeout. */ private void maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(ServiceRecord sr) { if (!sr.isShortFgs()) { return; } if (DEBUG_SHORT_SERVICE) { Slog.i(TAG_SERVICE, "Short FGS started: " + sr); } if (sr.hasShortFgsInfo()) { sr.getShortFgsInfo().update(); } else { sr.setShortFgsInfo(SystemClock.uptimeMillis()); } unscheduleShortFgsTimeoutLocked(sr); // Do it just in case final Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr); mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getTimeoutTime()); } /** * Stop the timeout for a ServiceRecord, if it's of a short-FGS. */ private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) { if (DEBUG_SHORT_SERVICE) { Slog.i(TAG_SERVICE, "Stop short FGS timeout: " + sr); } sr.clearShortFgsInfo(); unscheduleShortFgsTimeoutLocked(sr); } void onShortFgsTimeout(ServiceRecord sr) { synchronized (mAm) { if (!sr.shouldTriggerShortFgsTimeout()) { return; } Slog.i(TAG_SERVICE, "Short FGS timed out: " + sr); try { sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId()); } catch (RemoteException e) { // TODO(short-service): Anything to do here? } // Schedule the ANR timeout. final Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG, sr); mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getAnrTime()); } } void onShortFgsAnrTimeout(ServiceRecord sr) { // TODO(short-service): Implement ANR, and change the WTF to e(). // Also make sure to call waitingOnAMSLockStarted(). synchronized (mAm) { if (!sr.shouldTriggerShortFgsAnr()) { return; } } Slog.wtf(TAG_SERVICE, "Short FGS ANR'ed (NOT IMPLEMENTED YET): " + sr); } private void updateAllowlistManagerLocked(ProcessServiceRecord psr) { psr.mAllowlistManager = false; for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) { Loading @@ -2897,6 +2996,7 @@ public final class ActiveServices { } } // TODO(short-service): Hmm what is it? Should we stop the timeout here? private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) { final ProcessServiceRecord psr = service.app.mServices; psr.stopService(service); Loading Loading @@ -4178,7 +4278,7 @@ public final class ActiveServices { /** * Reschedule service restarts based on if the extra delays are enabled or not. * * @param prevEnable The previous state of whether or not it's enabled. * @param prevEnabled The previous state of whether or not it's enabled. * @param curEnabled The current state of whether or not it's enabled. * @param now The uptimeMillis */ Loading Loading @@ -4863,13 +4963,6 @@ public final class ActiveServices { Slog.i(TAG, "Bring down service for " + debugReason + " :" + r.toString()); } // TODO(short-service): Hmm, when the app stops a short-fgs, we should stop the timeout // here. // However we have a couple if's here and if these conditions are met, we stop here // without bringing down the service. // We need to make sure this can't be used (somehow) to keep having a short-FGS running // while having the timeout stopped. if (isServiceNeededLocked(r, knowConn, hasConn)) { return; } Loading @@ -4886,6 +4979,13 @@ public final class ActiveServices { //Slog.i(TAG, "Bring down service:"); //r.dump(" "); if (r.isShortFgs()) { // FGS can be stopped without the app calling stopService() or stopSelf(), // due to force-app-standby, or from Task Manager. Slog.w(TAG_SERVICE, "Short FGS brought down without stopping: " + r); maybeStopShortFgsTimeoutLocked(r); } // Report to all of the connections that the service is no longer // available. ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); Loading
services/core/java/com/android/server/am/ActivityManagerConstants.java +3 −3 Original line number Diff line number Diff line Loading @@ -954,7 +954,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_SHORT_FGS_TIMEOUT_DURATION = 60_000; /** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */ public static volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION; public volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION; /** * If a "short service" doesn't finish within this after the timeout ( Loading @@ -967,7 +967,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION = 5_000; /** @see #KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION */ public static volatile long mShortFgsProcStateExtraWaitDuration = public volatile long mShortFgsProcStateExtraWaitDuration = DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION; /** Loading @@ -983,7 +983,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION = 10_000; /** @see #KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION */ public static volatile long mShortFgsAnrExtraWaitDuration = public volatile long mShortFgsAnrExtraWaitDuration = DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION; private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = Loading
services/core/java/com/android/server/am/ActivityManagerService.java +8 −0 Original line number Diff line number Diff line Loading @@ -1563,6 +1563,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final int WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG = 73; static final int DISPATCH_SENDING_BROADCAST_EVENT = 74; static final int DISPATCH_BINDING_SERVICE_EVENT = 75; static final int SERVICE_SHORT_FGS_TIMEOUT_MSG = 76; static final int SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG = 77; static final int FIRST_BROADCAST_QUEUE_MSG = 200; Loading Loading @@ -1897,6 +1899,12 @@ public class ActivityManagerService extends IActivityManager.Stub mBindServiceEventListeners.forEach(l -> l.onBindingService((String) msg.obj, msg.arg1)); } break; case SERVICE_SHORT_FGS_TIMEOUT_MSG: { mServices.onShortFgsTimeout((ServiceRecord) msg.obj); } break; case SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG: { mServices.onShortFgsAnrTimeout((ServiceRecord) msg.obj); } break; } } } Loading
services/core/java/com/android/server/am/OomAdjuster.java +8 −1 Original line number Diff line number Diff line Loading @@ -1818,7 +1818,14 @@ public class OomAdjuster { newAdj = PERCEPTIBLE_APP_ADJ; newProcState = PROCESS_STATE_IMPORTANT_FOREGROUND; } else if (psr.hasForegroundServices() && !psr.hasNonShortForegroundServices()) { } else if (psr.hasForegroundServices()) { // If we get here, hasNonShortForegroundServices() must be false. // TODO(short-service): If all the short-fgs (there may be multiple) within the // process is post proc-state-demote time, then we need to skip this part. // ... Or, should we just ANR take care of timed-out short-FGS and shouldn't bother // lowering the procstate / oom-adj...?? (likely not.) // For short FGS. adjType = "fg-service-short"; // We use MEDIUM_APP_ADJ + 1 so we can tell apart EJ (which uses MEDIUM_APP_ADJ + 1) Loading
services/core/java/com/android/server/am/ServiceRecord.java +164 −2 Original line number Diff line number Diff line Loading @@ -315,6 +315,87 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>(); // start() arguments that haven't yet been delivered. /** * Information specific to "SHORT_SERVICE" FGS. */ class ShortFgsInfo { /** Time FGS started */ private final long mStartTime; /** * Copied from {@link #mStartForegroundCount}. If this is different from the parent's, * that means this instance is stale. */ private int mStartForegroundCount; /** Service's "start ID" when this short-service started. */ private int mStartId; ShortFgsInfo(long startTime) { mStartTime = startTime; update(); } /** * Update {@link #mStartForegroundCount} and {@link #mStartId}. * (but not {@link #mStartTime}) */ public void update() { this.mStartForegroundCount = ServiceRecord.this.mStartForegroundCount; this.mStartId = getLastStartId(); } long getStartTime() { return mStartTime; } int getStartForegroundCount() { return mStartForegroundCount; } int getStartId() { return mStartId; } /** * @return whether this {@link ShortFgsInfo} is still "current" or not -- i.e. * it's "start foreground count" is the same as that of the ServiceRecord's. * * Note, we do _not_ check the "start id" here, because the start id increments if the * app calls startService() or startForegroundService() on the same service, * but that will _not_ update the ShortFgsInfo, and will not extend the timeout. * * TODO(short-service): Make sure, calling startService will not extend or remove the * timeout, in CTS. */ boolean isCurrent() { return this.mStartForegroundCount == ServiceRecord.this.mStartForegroundCount; } /** Time when Service.onTimeout() should be called */ long getTimeoutTime() { return mStartTime + ams.mConstants.mShortFgsTimeoutDuration; } /** Time when the procstate should be lowered. */ long getProcStateDemoteTime() { return mStartTime + ams.mConstants.mShortFgsTimeoutDuration + ams.mConstants.mShortFgsProcStateExtraWaitDuration; } /** Time when the app should be declared ANR. */ long getAnrTime() { return mStartTime + ams.mConstants.mShortFgsTimeoutDuration + ams.mConstants.mShortFgsAnrExtraWaitDuration; } } /** * Keep track of short-fgs specific information. This field gets cleared when the timeout * stops. */ private ShortFgsInfo mShortFgsInfo; void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) { final int N = list.size(); for (int i=0; i<N; i++) { Loading Loading @@ -456,6 +537,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } proto.end(token); // TODO(short-service) Add FGS info } void dump(PrintWriter pw, String prefix) { Loading Loading @@ -509,7 +592,24 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (isForeground || foregroundId != 0) { pw.print(prefix); pw.print("isForeground="); pw.print(isForeground); pw.print(" foregroundId="); pw.print(foregroundId); pw.printf(" types=%08X", foregroundServiceType); pw.print(" foregroundNoti="); pw.println(foregroundNoti); if (isShortFgs() && mShortFgsInfo != null) { pw.print(prefix); pw.print("isShortFgs=true"); pw.print(" startId="); pw.print(mShortFgsInfo.getStartId()); pw.print(" startForegroundCount="); pw.print(mShortFgsInfo.getStartForegroundCount()); pw.print(" startTime="); TimeUtils.formatDuration(mShortFgsInfo.getStartTime(), now, pw); pw.print(" timeout="); TimeUtils.formatDuration(mShortFgsInfo.getTimeoutTime(), now, pw); pw.print(" demoteTime="); TimeUtils.formatDuration(mShortFgsInfo.getProcStateDemoteTime(), now, pw); pw.print(" anrTime="); TimeUtils.formatDuration(mShortFgsInfo.getAnrTime(), now, pw); pw.println(); } } if (mIsFgsDelegate) { pw.print(prefix); pw.print("isFgsDelegate="); pw.println(mIsFgsDelegate); Loading Loading @@ -1238,4 +1338,66 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return isForeground && (foregroundServiceType == ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE); } public ShortFgsInfo getShortFgsInfo() { return isShortFgs() ? mShortFgsInfo : null; } /** * Call it when a short FGS starts. */ public void setShortFgsInfo(long uptimeNow) { this.mShortFgsInfo = new ShortFgsInfo(uptimeNow); } /** @return whether {@link #mShortFgsInfo} is set or not. */ public boolean hasShortFgsInfo() { return mShortFgsInfo != null; } /** * Call it when a short FGS stops. */ public void clearShortFgsInfo() { this.mShortFgsInfo = null; } /** * @return true if it's a short FGS that's still up and running, and should be timed out. */ public boolean shouldTriggerShortFgsTimeout() { if (!isAppAlive()) { return false; } if (!this.startRequested || !isShortFgs() || mShortFgsInfo == null || !mShortFgsInfo.isCurrent()) { return false; } return mShortFgsInfo.getTimeoutTime() < SystemClock.uptimeMillis(); } /** * @return true if it's a short FGS that's still up and running, and should be declared * an ANR. */ public boolean shouldTriggerShortFgsAnr() { if (!isAppAlive()) { return false; } if (!this.startRequested || !isShortFgs() || mShortFgsInfo == null || !mShortFgsInfo.isCurrent()) { return false; } return mShortFgsInfo.getAnrTime() < SystemClock.uptimeMillis(); } private boolean isAppAlive() { if (app == null) { return false; } if (app.getThread() == null || app.isKilled() || app.isKilledByAm()) { return false; } return true; } }