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

Commit d0dc3211 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Trigger an ANR for slow app responses."

parents ed3f2913 f88dcd30
Loading
Loading
Loading
Loading
+52 −9
Original line number Diff line number Diff line
@@ -24,7 +24,9 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.Notification;
import android.app.compat.CompatChanges;
import android.app.job.IJobCallback;
import android.app.job.IJobService;
import android.app.job.JobInfo;
@@ -32,6 +34,9 @@ import android.app.job.JobParameters;
import android.app.job.JobProtoEnums;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -57,6 +62,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -87,6 +93,15 @@ public final class JobServiceContext implements ServiceConnection {
    private static final boolean DEBUG = JobSchedulerService.DEBUG;
    private static final boolean DEBUG_STANDBY = JobSchedulerService.DEBUG_STANDBY;

    /**
     * Whether to trigger an ANR when apps are slow to respond on pre-UDC APIs and functionality.
     */
    @ChangeId
    @Disabled
    // TODO(258236856): Enable after test is fixed
    // @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    private static final long ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES = 258236856L;

    private static final String TAG = "JobServiceContext";
    /** Amount of time the JobScheduler waits for the initial service launch+bind. */
    private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
@@ -119,6 +134,7 @@ public final class JobServiceContext implements ServiceConnection {
    /** Used for service binding, etc. */
    private final Context mContext;
    private final Object mLock;
    private final ActivityManagerInternal mActivityManagerInternal;
    private final IBatteryStats mBatteryStats;
    private final EconomyManagerInternal mEconomyManagerInternal;
    private final JobPackageTracker mJobPackageTracker;
@@ -270,6 +286,7 @@ public final class JobServiceContext implements ServiceConnection {
        mContext = service.getContext();
        mLock = service.getLock();
        mService = service;
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
        mBatteryStats = batteryStats;
        mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class);
        mJobPackageTracker = tracker;
@@ -1121,23 +1138,31 @@ public final class JobServiceContext implements ServiceConnection {
    private void handleOpTimeoutLocked() {
        switch (mVerb) {
            case VERB_BINDING:
                Slog.w(TAG, "Time-out while trying to bind " + getRunningJobNameLocked()
                        + ", dropping.");
                closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
                onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
                        /* debugReason */ "timed out while binding",
                        /* anrMessage */ "Timed out while trying to bind",
                        CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
                            mRunningJob.getUid()));
                break;
            case VERB_STARTING:
                // Client unresponsive - wedged or failed to respond in time. We don't really
                // know what happened so let's log it and notify the JobScheduler
                // FINISHED/NO-RETRY.
                Slog.w(TAG, "No response from client for onStartJob "
                        + getRunningJobNameLocked());
                closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
                onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
                        /* debugReason */ "timed out while starting",
                        /* anrMessage */ "No response to onStartJob",
                        CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
                            mRunningJob.getUid()));
                break;
            case VERB_STOPPING:
                // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
                Slog.w(TAG, "No response from client for onStopJob "
                        + getRunningJobNameLocked());
                closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
                // Don't update the stop reasons since we were already stopping the job for some
                // other reason.
                onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ false,
                        /* debugReason */ "timed out while stopping",
                        /* anrMessage */ "No response to onStopJob",
                        CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
                            mRunningJob.getUid()));
                break;
            case VERB_EXECUTING:
                if (mPendingStopReason != JobParameters.STOP_REASON_UNDEFINED) {
@@ -1218,6 +1243,24 @@ public final class JobServiceContext implements ServiceConnection {
        }
    }

    @GuardedBy("mLock")
    private void onSlowAppResponseLocked(boolean reschedule, boolean updateStopReasons,
            @NonNull String debugReason, @NonNull String anrMessage, boolean triggerAnr) {
        Slog.w(TAG, anrMessage + " for " + getRunningJobNameLocked());
        if (updateStopReasons) {
            mParams.setStopReason(
                    JobParameters.STOP_REASON_UNDEFINED,
                    JobParameters.INTERNAL_STOP_REASON_ANR,
                    debugReason);
        }
        if (triggerAnr) {
            mActivityManagerInternal.appNotResponding(
                    mRunningJob.serviceProcessName, mRunningJob.getUid(),
                    TimeoutRecord.forJobService(anrMessage));
        }
        closeAndCleanupJobLocked(reschedule, debugReason);
    }

    /**
     * The provided job has finished, either by calling
     * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
+6 −0
Original line number Diff line number Diff line
@@ -508,6 +508,12 @@ public abstract class ActivityManagerInternal {
     */
    public abstract void broadcastCloseSystemDialogs(String reason);

    /**
     * Trigger an ANR for the specified process.
     */
    public abstract void appNotResponding(@NonNull String processName, int uid,
            @NonNull TimeoutRecord timeoutRecord);

    /**
     * Kills all background processes, except those matching any of the specified properties.
     *
+10 −1
Original line number Diff line number Diff line
@@ -41,7 +41,9 @@ public class TimeoutRecord {
            TimeoutKind.SERVICE_EXEC,
            TimeoutKind.CONTENT_PROVIDER,
            TimeoutKind.APP_REGISTERED,
            TimeoutKind.SHORT_FGS_TIMEOUT})
            TimeoutKind.SHORT_FGS_TIMEOUT,
            TimeoutKind.JOB_SERVICE,
    })

    @Retention(RetentionPolicy.SOURCE)
    public @interface TimeoutKind {
@@ -53,6 +55,7 @@ public class TimeoutRecord {
        int CONTENT_PROVIDER = 6;
        int APP_REGISTERED = 7;
        int SHORT_FGS_TIMEOUT = 8;
        int JOB_SERVICE = 9;
    }

    /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
@@ -152,4 +155,10 @@ public class TimeoutRecord {
    public static TimeoutRecord forShortFgsTimeout(String reason) {
        return TimeoutRecord.endingNow(TimeoutKind.SHORT_FGS_TIMEOUT, reason);
    }

    /** Record for a job related timeout. */
    @NonNull
    public static TimeoutRecord forJobService(String reason) {
        return TimeoutRecord.endingNow(TimeoutKind.JOB_SERVICE, reason);
    }
}
+21 −0
Original line number Diff line number Diff line
@@ -6811,6 +6811,21 @@ public class ActivityManagerService extends IActivityManager.Stub
        mAnrHelper.appNotResponding(anrProcess, timeoutRecord);
    }
    private void appNotResponding(@NonNull String processName, int uid,
            @NonNull TimeoutRecord timeoutRecord) {
        Objects.requireNonNull(processName);
        Objects.requireNonNull(timeoutRecord);
        synchronized (this) {
            final ProcessRecord app = getProcessRecordLocked(processName, uid);
            if (app == null) {
                Slog.e(TAG, "Unknown process: " + processName);
                return;
            }
            mAnrHelper.appNotResponding(app, timeoutRecord);
        }
    }
    void startPersistentApps(int matchFlags) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
@@ -18015,6 +18030,12 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
        }
        @Override
        public void appNotResponding(@NonNull String processName, int uid,
                @NonNull TimeoutRecord timeoutRecord) {
            ActivityManagerService.this.appNotResponding(processName, uid, timeoutRecord);
        }
        @Override
        public void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
            synchronized (ActivityManagerService.this) {