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

Commit cb7ba3cf authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Do not repeat ANR dialogs, except for "input" ANRs

The ANR dialog's behavior changed in
I4ca34bb8cc60bcde3ff571566553f44246d78ddc such that even if the user
presses "wait" on the ANR dialog, the dialog will re-appear unless the
app starts responding again.

However, the "stop the repeat" was only implemented for input ANRs,
so for other kinds of ANRs (e.g. the broadcast ANR), the dialog would
keep showing up even after the app starts behaving normally.

This change will change the behavior so that only input ANRs will
repeatedly show up.

Fix: 263182141
Test: Manual test: Make shore "short service" ANR dialog will not repeat
Test: Manual test: Make shore input ANR dialog will actually repeat
Test: atest FrameworksServicesTests:AnrHelperTest
Test: atest FrameworksServicesTests:ProcessRecordTests
Change-Id: Id9b34cef605a65ff29b5569e72945790695d7abf
parent b72d65fd
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -6684,6 +6684,10 @@ public class ActivityManagerService extends IActivityManager.Stub
    @Override
    public void appNotResponding(final String reason) {
        appNotResponding(reason, /*isContinuousAnr*/ false);
    }
    public void appNotResponding(final String reason, boolean isContinuousAnr) {
        TimeoutRecord timeoutRecord = TimeoutRecord.forApp("App requested: " + reason);
        final int callingPid = Binder.getCallingPid();
@@ -6696,7 +6700,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            mAnrHelper.appNotResponding(app, null, app.info, null, null, false,
                    timeoutRecord);
                    timeoutRecord, isContinuousAnr);
        }
    }
@@ -18325,7 +18329,8 @@ public class ActivityManagerService extends IActivityManager.Stub
                    }
                }
                mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,
                        parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
                        parentShortComponentName, parentProcess, aboveSystem, timeoutRecord,
                        /*isContinuousAnr*/ true);
            }
            return true;
+9 −5
Original line number Diff line number Diff line
@@ -96,13 +96,13 @@ class AnrHelper {
    void appNotResponding(ProcessRecord anrProcess, TimeoutRecord timeoutRecord) {
        appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
                null /* parentShortComponentName */, null /* parentProcess */,
                false /* aboveSystem */, timeoutRecord);
                false /* aboveSystem */, timeoutRecord, /*isContinuousAnr*/ false);
    }

    void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
            ApplicationInfo aInfo, String parentShortComponentName,
            WindowProcessController parentProcess, boolean aboveSystem,
            TimeoutRecord timeoutRecord) {
            TimeoutRecord timeoutRecord, boolean isContinuousAnr) {
        try {
            timeoutRecord.mLatencyTracker.appNotRespondingStarted();
            final int incomingPid = anrProcess.mPid;
@@ -132,7 +132,7 @@ class AnrHelper {
                timeoutRecord.mLatencyTracker.anrRecordPlacingOnQueueWithSize(mAnrRecords.size());
                mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
                        parentShortComponentName, parentProcess, aboveSystem,
                        mAuxiliaryTaskExecutor, timeoutRecord));
                        mAuxiliaryTaskExecutor, timeoutRecord, isContinuousAnr));
            }
            startAnrConsumerIfNeeded();
        } finally {
@@ -230,10 +230,12 @@ class AnrHelper {
        final boolean mAboveSystem;
        final ExecutorService mAuxiliaryTaskExecutor;
        final long mTimestamp = SystemClock.uptimeMillis();
        final boolean mIsContinuousAnr;
        AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
                ApplicationInfo aInfo, String parentShortComponentName,
                WindowProcessController parentProcess, boolean aboveSystem,
                ExecutorService auxiliaryTaskExecutor, TimeoutRecord timeoutRecord) {
                ExecutorService auxiliaryTaskExecutor, TimeoutRecord timeoutRecord,
                boolean isContinuousAnr) {
            mApp = anrProcess;
            mPid = anrProcess.mPid;
            mActivityShortComponentName = activityShortComponentName;
@@ -243,6 +245,7 @@ class AnrHelper {
            mParentProcess = parentProcess;
            mAboveSystem = aboveSystem;
            mAuxiliaryTaskExecutor = auxiliaryTaskExecutor;
            mIsContinuousAnr = isContinuousAnr;
        }

        void appNotResponding(boolean onlyDumpSelf) {
@@ -250,7 +253,8 @@ class AnrHelper {
                mTimeoutRecord.mLatencyTracker.anrProcessingStarted();
                mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
                        mParentShortComponentName, mParentProcess, mAboveSystem,
                        mTimeoutRecord, mAuxiliaryTaskExecutor, onlyDumpSelf);
                        mTimeoutRecord, mAuxiliaryTaskExecutor, onlyDumpSelf,
                        mIsContinuousAnr);
            } finally {
                mTimeoutRecord.mLatencyTracker.anrProcessingEnded();
            }
+12 −3
Original line number Diff line number Diff line
@@ -169,9 +169,11 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli
                            errState.getDialogController().clearAnrDialogs();
                        }
                        mService.mServices.scheduleServiceTimeoutLocked(app);
                        if (mData.isContinuousAnr) {
                            // If the app remains unresponsive, show the dialog again after a delay.
                            mService.mInternal.rescheduleAnrDialog(mData);
                        }
                    }
                    break;
            }

@@ -197,10 +199,17 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli
        final ApplicationInfo aInfo;
        final boolean aboveSystem;

        Data(ProcessRecord proc, ApplicationInfo aInfo, boolean aboveSystem) {
        // If true, then even if the user presses "WAIT" on the ANR dialog,
        // we'll show it again until the app start responding again.
        // (we only use it for input dispatch ANRs)
        final boolean isContinuousAnr;

        Data(ProcessRecord proc, ApplicationInfo aInfo, boolean aboveSystem,
                boolean isContinuousAnr) {
            this.proc = proc;
            this.aInfo = aInfo;
            this.aboveSystem = aboveSystem;
            this.isContinuousAnr = isContinuousAnr;
        }
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -263,7 +263,8 @@ class ProcessErrorStateRecord {
    void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
            String parentShortComponentName, WindowProcessController parentProcess,
            boolean aboveSystem, TimeoutRecord timeoutRecord,
            ExecutorService auxiliaryTaskExecutor, boolean onlyDumpSelf) {
            ExecutorService auxiliaryTaskExecutor, boolean onlyDumpSelf,
            boolean isContinuousAnr) {
        String annotation = timeoutRecord.mReason;
        AnrLatencyTracker latencyTracker = timeoutRecord.mLatencyTracker;
        Future<?> updateCpuStatsNowFirstCall = null;
@@ -630,7 +631,8 @@ class ProcessErrorStateRecord {
                // Bring up the infamous App Not Responding dialog
                Message msg = Message.obtain();
                msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
                msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
                msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem,
                        isContinuousAnr);

                mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
            }
+7 −5
Original line number Diff line number Diff line
@@ -119,12 +119,13 @@ public class AnrHelperTest {
        final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
                annotation);
        mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo,
                parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
                parentShortComponentName, parentProcess, aboveSystem, timeoutRecord,
                /*isContinuousAnr*/ false);

        verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
                eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
                eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mExecutorService),
                eq(false) /* onlyDumpSelf */);
                eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/);
    }

    @Test
@@ -137,13 +138,14 @@ public class AnrHelperTest {
            processingLatch.await();
            return null;
        }).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(),
                anyBoolean(), any(), any(), anyBoolean());
                anyBoolean(), any(), any(), anyBoolean(), anyBoolean());
        final ApplicationInfo appInfo = new ApplicationInfo();
        final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
                "annotation");
        final Runnable reportAnr = () -> mAnrHelper.appNotResponding(mAnrApp,
                "activityShortComponentName", appInfo, "parentShortComponentName",
                null /* parentProcess */, false /* aboveSystem */, timeoutRecord);
                null /* parentProcess */, false /* aboveSystem */, timeoutRecord,
                false /*isContinuousAnr*/);
        reportAnr.run();
        // This should be skipped because the pid is pending in queue.
        reportAnr.run();
@@ -160,6 +162,6 @@ public class AnrHelperTest {
        // There is only one ANR reported.
        verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding(
                anyString(), any(), any(), any(), anyBoolean(), any(), eq(mExecutorService),
                anyBoolean());
                anyBoolean(), anyBoolean());
    }
}
Loading