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

Commit 585f2930 authored by Yunfan Chen's avatar Yunfan Chen
Browse files

Add test to ProcessRecord ANR function

The test will cover ProcessRecord#appNotResponding. Status related to
the Process will be tested. It covers the code path related to parent
process to avoid possible NPE in future.

Necessary refactor is there to support the test.

Test: atest ProcessRecordTests
Bug: 117927743
Change-Id: I127020177be0db82158c20102323458f718d3c60
parent 2f040bef
Loading
Loading
Loading
Loading
+57 −25
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
@@ -73,7 +74,7 @@ import java.util.List;
 * Full information about a particular process that
 * is currently running.
 */
final class ProcessRecord implements WindowProcessListener {
class ProcessRecord implements WindowProcessListener {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;

    private final ActivityManagerService mService; // where we came from
@@ -1303,6 +1304,27 @@ final class ProcessRecord implements WindowProcessListener {
            ServerProtoEnums.DATA_APP;
    }

    /**
     * Unless configured otherwise, swallow ANRs in background processes & kill the process.
     * Non-private access is for tests only.
     */
    @VisibleForTesting
    boolean isSilentAnr() {
        return !getShowBackground() && !isInterestingForBackgroundTraces();
    }

    /** Non-private access is for tests only. */
    @VisibleForTesting
    List<ProcessRecord> getLruProcessList() {
        return mService.mProcessList.mLruProcesses;
    }

    /** Non-private access is for tests only. */
    @VisibleForTesting
    boolean isMonitorCpuUsage() {
        return mService.MONITOR_CPU_USAGE;
    }

    void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
            String parentShortComponentName, WindowProcessController parentProcess,
            boolean aboveSystem, String annotation) {
@@ -1312,16 +1334,10 @@ final class ProcessRecord implements WindowProcessListener {
        mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr", true));

        long anrTime = SystemClock.uptimeMillis();
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
        if (isMonitorCpuUsage()) {
            mService.updateCpuStatsNow();
        }

        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
        boolean showBackground = Settings.Secure.getInt(mService.mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;

        boolean isSilentANR;

        synchronized (mService) {
            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
            if (mService.mAtmInternal.isShuttingDown()) {
@@ -1353,8 +1369,7 @@ final class ProcessRecord implements WindowProcessListener {
            firstPids.add(pid);

            // Don't dump other PIDs if it's a background ANR
            isSilentANR = !showBackground && !isInterestingForBackgroundTraces();
            if (!isSilentANR) {
            if (!isSilentAnr()) {
                int parentPid = pid;
                if (parentProcess != null && parentProcess.getPid() > 0) {
                    parentPid = parentProcess.getPid();
@@ -1363,8 +1378,8 @@ final class ProcessRecord implements WindowProcessListener {

                if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);

                for (int i = mService.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
                    ProcessRecord r = mService.mProcessList.mLruProcesses.get(i);
                for (int i = getLruProcessList().size() - 1; i >= 0; i--) {
                    ProcessRecord r = getLruProcessList().get(i);
                    if (r != null && r.thread != null) {
                        int myPid = r.pid;
                        if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
@@ -1405,7 +1420,7 @@ final class ProcessRecord implements WindowProcessListener {

        // don't dump native PIDs for background ANRs unless it is the process of interest
        String[] nativeProcs = null;
        if (isSilentANR) {
        if (isSilentAnr()) {
            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
                if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) {
                    nativeProcs = new String[] { processName };
@@ -1429,11 +1444,11 @@ final class ProcessRecord implements WindowProcessListener {
        // For background ANRs, don't pass the ProcessCpuTracker to
        // avoid spending 1/2 second collecting stats to rank lastPids.
        File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
                (isSilentANR) ? null : processCpuTracker, (isSilentANR) ? null : lastPids,
                (isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids,
                nativePids);

        String cpuInfo = null;
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
        if (isMonitorCpuUsage()) {
            mService.updateCpuStatsNow();
            synchronized (mService.mProcessCpuTracker) {
                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
@@ -1477,9 +1492,13 @@ final class ProcessRecord implements WindowProcessListener {
        }

        synchronized (mService) {
            // mBatteryStatsService can be null if the AMS is constructed with injector only. This
            // will only happen in tests.
            if (mService.mBatteryStatsService != null) {
                mService.mBatteryStatsService.noteProcessAnr(processName, uid);
            }

            if (isSilentANR) {
            if (isSilentAnr()) {
                kill("bg anr", true);
                return;
            }
@@ -1488,6 +1507,9 @@ final class ProcessRecord implements WindowProcessListener {
            makeAppNotRespondingLocked(activityShortComponentName,
                    annotation != null ? "ANR " + annotation : "ANR", info.toString());

            // mUiHandler can be null if the AMS is constructed with injector only. This will only
            // happen in tests.
            if (mService.mUiHandler != null) {
                // Bring up the infamous App Not Responding dialog
                Message msg = Message.obtain();
                msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
@@ -1496,12 +1518,17 @@ final class ProcessRecord implements WindowProcessListener {
                mService.mUiHandler.sendMessage(msg);
            }
        }
    }

    private void makeAppNotRespondingLocked(String activity, String shortMsg, String longMsg) {
        setNotResponding(true);
        // mAppErrors can be null if the AMS is constructed with injector only. This will only
        // happen in tests.
        if (mService.mAppErrors != null) {
            notRespondingReport = mService.mAppErrors.generateProcessError(this,
                    ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
                    activity, shortMsg, longMsg, null);
        }
        startAppProblemLocked();
        getWindowProcessController().stopFreezingActivities();
    }
@@ -1539,4 +1566,9 @@ final class ProcessRecord implements WindowProcessListener {
                (info != null && "com.android.systemui".equals(info.packageName))
                || (hasTopUi() || hasOverlayUi());
    }

    private boolean getShowBackground() {
        return Settings.Secure.getInt(mService.mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -762,6 +762,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        return mGlobalLock;
    }

    /** For test purpose only. */
    @VisibleForTesting
    public ActivityTaskManagerInternal getAtmInternal() {
        return mInternal;
    }

    public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController,
            Looper looper) {
        mH = new H(looper);
+160 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.am;

import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;

import android.content.Context;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.FlakyTest;

import com.android.server.wm.ActivityTaskManagerService;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.Collections;

/**
 * Build/Install/Run:
 *  atest FrameworksServicesTests:ProcessRecordTests
 */
@Presubmit
@FlakyTest(detail = "Promote to presubmit when shown to be stable.")
public class ProcessRecordTests {
    private static Context sContext;
    private static ActivityManagerService sService;

    private ProcessRecord mProcessRecord;

    @BeforeClass
    public static void setUpOnce() throws Exception {
        sContext = getInstrumentation().getTargetContext();

        // We need to run with dexmaker share class loader to make use of ActivityTaskManagerService
        // from wm package.
        runWithDexmakerShareClassLoader(() -> {
            sService = mock(ActivityManagerService.class);
            sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
            sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
            sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
        });
    }

    @Before
    public void setUpProcess() throws Exception {
        // Need to run with dexmaker share class loader to mock package private class.
        runWithDexmakerShareClassLoader(() -> {
            mProcessRecord = spy(new ProcessRecord(sService, sContext.getApplicationInfo(),
                    "name", 12345));
            doNothing().when(mProcessRecord).startAppProblemLocked();
            doReturn(false).when(mProcessRecord).isSilentAnr();
            doReturn(false).when(mProcessRecord).isMonitorCpuUsage();
            doReturn(Collections.emptyList()).when(mProcessRecord).getLruProcessList();
        });
    }


    /**
     * This test verifies the process default status. If this doesn't pass, none of the other tests
     * should be able to pass.
     */
    @Test
    public void testProcessDefaultAnrRelatedStatus() {
        assertFalse(mProcessRecord.isNotResponding());
        assertFalse(mProcessRecord.isCrashing());
        assertFalse(mProcessRecord.killedByAm);
        assertFalse(mProcessRecord.killed);
    }

    /**
     * This test verifies that if the process is crashing, Anr will do nothing.
     */
    @Test
    public void testAnrWhenCrash() {
        mProcessRecord.setCrashing(true);
        assertTrue(mProcessRecord.isCrashing());
        mProcessRecord.appNotResponding(null, null, null, null, false, "Test ANR when crash");
        assertFalse(mProcessRecord.isNotResponding());
        assertFalse(mProcessRecord.killedByAm);
        assertFalse(mProcessRecord.killed);
    }

    /**
     * This test verifies that if the process is killed by AM, Anr will do nothing.
     */
    @Test
    public void testAnrWhenKilledByAm() {
        mProcessRecord.killedByAm = true;
        mProcessRecord.appNotResponding(null, null, null, null, false,
                "Test ANR when killed by AM");
        assertFalse(mProcessRecord.isNotResponding());
        assertFalse(mProcessRecord.isCrashing());
        assertFalse(mProcessRecord.killed);
    }

    /**
     * This test verifies that if the process is killed, Anr will do nothing.
     */
    @Test
    public void testAnrWhenKilled() {
        mProcessRecord.killed = true;
        mProcessRecord.appNotResponding(null, null, null, null, false, "Test ANR when killed");
        assertFalse(mProcessRecord.isNotResponding());
        assertFalse(mProcessRecord.isCrashing());
        assertFalse(mProcessRecord.killedByAm);
    }

    /**
     * This test verifies that non-silent ANR can run through successfully and the corresponding
     * flags can be set correctly.
     */
    @Test
    public void testNonSilentAnr() {
        mProcessRecord.appNotResponding(null, null, null, null, false, "Test non-silent ANR");
        assertTrue(mProcessRecord.isNotResponding());
        assertFalse(mProcessRecord.isCrashing());
        assertFalse(mProcessRecord.killedByAm);
        assertFalse(mProcessRecord.killed);
    }

    /**
     * This test verifies that silent ANR can run through successfully and the corresponding flags
     * can be set correctly.
     */
    @Test
    public void testSilentAnr() {
        // Silent Anr will run through even without a parent process, and directly killed by AM.
        doReturn(true).when(mProcessRecord).isSilentAnr();
        mProcessRecord.appNotResponding(null, null, null, null, false, "Test silent ANR");
        assertTrue(mProcessRecord.isNotResponding());
        assertFalse(mProcessRecord.isCrashing());
        assertTrue(mProcessRecord.killedByAm);
        assertTrue(mProcessRecord.killed);
    }
}