Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99c12773 authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Implement SHORT_SERVICE ANR"

parents c58c817e f273c10f
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -40,7 +40,8 @@ public class TimeoutRecord {
            TimeoutKind.SERVICE_START,
            TimeoutKind.SERVICE_EXEC,
            TimeoutKind.CONTENT_PROVIDER,
            TimeoutKind.APP_REGISTERED})
            TimeoutKind.APP_REGISTERED,
            TimeoutKind.SHORT_FGS_TIMEOUT})

    @Retention(RetentionPolicy.SOURCE)
    public @interface TimeoutKind {
@@ -51,6 +52,7 @@ public class TimeoutRecord {
        int SERVICE_EXEC = 5;
        int CONTENT_PROVIDER = 6;
        int APP_REGISTERED = 7;
        int SHORT_FGS_TIMEOUT = 8;
    }

    /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
@@ -144,4 +146,10 @@ public class TimeoutRecord {
    public static TimeoutRecord forApp(@NonNull String reason) {
        return TimeoutRecord.endingApproximatelyNow(TimeoutKind.APP_REGISTERED, reason);
    }

    /** Record for a "short foreground service" timeout. */
    @NonNull
    public static TimeoutRecord forShortFgsTimeout(String reason) {
        return TimeoutRecord.endingNow(TimeoutKind.SHORT_FGS_TIMEOUT, reason);
    }
}
+53 −32
Original line number Diff line number Diff line
@@ -228,7 +228,8 @@ public final class ActiveServices {
    private static final boolean DEBUG_DELAYED_SERVICE = DEBUG_SERVICE;
    private static final boolean DEBUG_DELAYED_STARTS = DEBUG_DELAYED_SERVICE;

    private static final boolean DEBUG_SHORT_SERVICE = DEBUG_SERVICE;
    // STOPSHIP(b/260012573) turn it off.
    private static final boolean DEBUG_SHORT_SERVICE = true; // DEBUG_SERVICE;

    private static final boolean LOG_SERVICE_START_STOP = DEBUG_SERVICE;

@@ -1912,6 +1913,36 @@ public final class ActiveServices {
                                "startForeground(SHORT_SERVICE) called on a service that's not"
                                + " started.");
                    }
                    // If the service is already an FGS, and the type is changing, then we
                    // may need to do some extra work here.
                    if (r.isForeground && (r.foregroundServiceType != foregroundServiceType)) {
                        // TODO(short-service): Consider transitions:
                        //   A. Short -> other types:
                        //     Apply the BG restriction again. Don't just allow it.
                        //     i.e. unless the app is in a situation where it's allowed to start
                        //     a FGS, this transition shouldn't be allowed.
                        //     ... But think about it more, there may be a case this should be
                        //     allowed.
                        //
                        //     If the transition is allowed, stop the timeout.
                        //     If the transition is _not_ allowed... keep the timeout?
                        //
                        //   B. Short -> Short:
                        //     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(
                                    "setForeground(): Changing foreground service type from / to "
                                    + " SHORT_SERVICE is now allowed");
                        }
                    }

                    // 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
@@ -1952,32 +1983,6 @@ public final class ActiveServices {
                        // on the same sarvice after it's created, regardless of whether
                        // stopForeground() has been called or not.

                        // TODO(short-service): Consider transitions:
                        //   A. Short -> other types:
                        //     Apply the BG restriction again. Don't just allow it.
                        //     i.e. unless the app is in a situation where it's allowed to start
                        //     a FGS, this transition shouldn't be allowed.
                        //     ... But think about it more, there may be a case this should be
                        //     allowed.
                        //
                        //     If the transition is allowed, stop the timeout.
                        //     If the transition is _not_ allowed... keep the timeout?
                        //
                        //   B. Short -> Short:
                        //     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(),
@@ -2948,6 +2953,9 @@ public final class ActiveServices {
     * Stop the timeout for a ServiceRecord, if it's of a short-FGS.
     */
    private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) {
        if (!sr.isShortFgs()) {
            return;
        }
        if (DEBUG_SHORT_SERVICE) {
            Slog.i(TAG_SERVICE, "Stop short FGS timeout: " + sr);
        }
@@ -2960,7 +2968,7 @@ public final class ActiveServices {
            if (!sr.shouldTriggerShortFgsTimeout()) {
                return;
            }
            Slog.i(TAG_SERVICE, "Short FGS timed out: " + sr);
            Slog.e(TAG_SERVICE, "Short FGS timed out: " + sr);
            try {
                sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId());
            } catch (RemoteException e) {
@@ -2974,15 +2982,28 @@ public final class ActiveServices {
    }

    void onShortFgsAnrTimeout(ServiceRecord sr) {
        // TODO(short-service): Implement ANR, and change the WTF to e().
        // Also make sure to call waitingOnAMSLockStarted().
        final String reason = "A foreground service of FOREGROUND_SERVICE_TYPE_SHORT_SERVICE"
                + " did not stop within a timeout: " + sr.getComponentName();

        final TimeoutRecord tr = TimeoutRecord.forShortFgsTimeout(reason);

        // TODO(short-service): TODO Add SHORT_FGS_TIMEOUT to AnrLatencyTracker
        tr.mLatencyTracker.waitingOnAMSLockStarted();
        synchronized (mAm) {
            tr.mLatencyTracker.waitingOnAMSLockEnded();

            if (!sr.shouldTriggerShortFgsAnr()) {
                return;
            }
        }

        Slog.wtf(TAG_SERVICE, "Short FGS ANR'ed (NOT IMPLEMENTED YET): " + sr);
            final String message = "Short FGS ANR'ed: " + sr;
            if (DEBUG_SHORT_SERVICE) {
                Slog.wtf(TAG_SERVICE, message);
            } else {
                Slog.e(TAG_SERVICE, message);
            }
            mAm.appNotResponding(sr.app, tr);
        }
    }

    private void updateAllowlistManagerLocked(ProcessServiceRecord psr) {
+22 −19
Original line number Diff line number Diff line
@@ -1821,14 +1821,16 @@ public class OomAdjuster {
            } 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.)

                // TODO(short-service): Proactively run OomAjudster when the grace period finish.
                if (psr.areAllShortForegroundServicesProcstateTimedOut(now)) {
                    // All the short-FGSes within this process are timed out. Don't promote to FGS.
                    // TODO(short-service): Should we set some unique oom-adj to make it detectable,
                    // in a long trace?
                } else {
                    // 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)
                    // We use MEDIUM_APP_ADJ + 1 so we can tell apart EJ
                    // (which uses MEDIUM_APP_ADJ + 1)
                    // from short-FGS.
                    // (We use +1 and +2, not +0 and +1, to be consistent with the following
                    // RECENT_FOREGROUND_APP_ADJ tweak)
@@ -1841,6 +1843,7 @@ public class OomAdjuster {
                    // even when battery saver or data saver is enabled.
                    capabilityFromFGS |= PROCESS_CAPABILITY_NETWORK;
                }
            }

            if (adjType != null) {
                adj = newAdj;
+30 −0
Original line number Diff line number Diff line
@@ -175,6 +175,9 @@ final class ProcessServiceRecord {
        }
    }

    /**
     * @return true if this process has any foreground services (even timed-out short-FGS)
     */
    boolean hasForegroundServices() {
        return mHasForegroundServices;
    }
@@ -224,6 +227,33 @@ final class ProcessServiceRecord {
        return mFgServiceTypes != ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
    }

    /**
     * @return if this process:
     * - has at least one short-FGS
     * - has no other types of FGS
     * - and all the short-FGSes are procstate-timed out.
     */
    boolean areAllShortForegroundServicesProcstateTimedOut(long nowUptime) {
        if (!mHasForegroundServices) { // Process has no FGS?
            return false;
        }
        if (hasNonShortForegroundServices()) {  // Any non-short FGS running?
            return false;
        }
        // Now we need to look at all short-FGS within the process and see if all of them are
        // procstate-timed-out or not.
        for (int i = mServices.size() - 1; i >= 0; i--) {
            final ServiceRecord sr = mServices.valueAt(i);
            if (!sr.isShortFgs() || !sr.hasShortFgsInfo()) {
                continue;
            }
            if (sr.getShortFgsInfo().getProcStateDemoteTime() >= nowUptime) {
                return false;
            }
        }
        return true;
    }

    int getReportedForegroundServiceTypes() {
        return mRepFgServiceTypes;
    }
+26 −0
Original line number Diff line number Diff line
@@ -690,6 +690,32 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
        }
    }

    /** Used only for tests */
    private ServiceRecord(ActivityManagerService ams) {
        this.ams = ams;
        name = null;
        instanceName = null;
        shortInstanceName = null;
        definingPackageName = null;
        definingUid = 0;
        intent = null;
        serviceInfo = null;
        userId = 0;
        packageName = null;
        processName = null;
        permission = null;
        exported = false;
        restarter = null;
        createRealTime = 0;
        isSdkSandbox = false;
        sdkSandboxClientAppUid = 0;
        sdkSandboxClientAppPackage = null;
    }

    public static ServiceRecord newEmptyInstanceForTest(ActivityManagerService ams) {
        return new ServiceRecord(ams);
    }

    ServiceRecord(ActivityManagerService ams, ComponentName name,
            ComponentName instanceName, String definingPackageName, int definingUid,
            Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
Loading