diff options
| author | 2019-03-07 00:51:39 +0000 | |
|---|---|---|
| committer | 2019-03-07 00:51:39 +0000 | |
| commit | 021dccc37ff6290791b5c3373845b51f97d05c97 (patch) | |
| tree | 880548b9828c10f0b533a045049994792c5b3b3f | |
| parent | 756bbab5d0503486848e7870eed52d9ec53a088f (diff) | |
| parent | 585f293075c8f7f0f9fc9132c13a2f77ee84b45d (diff) | |
Merge "Add test to ProcessRecord ANR function"
3 files changed, 223 insertions, 25 deletions
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index fcc857b7a75d..51b1ae197821 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -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) { - mService.mBatteryStatsService.noteProcessAnr(processName, uid); + // 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,20 +1507,28 @@ final class ProcessRecord implements WindowProcessListener { makeAppNotRespondingLocked(activityShortComponentName, annotation != null ? "ANR " + annotation : "ANR", info.toString()); - // 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(this, aInfo, aboveSystem); + // 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; + msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem); - mService.mUiHandler.sendMessage(msg); + mService.mUiHandler.sendMessage(msg); + } } } private void makeAppNotRespondingLocked(String activity, String shortMsg, String longMsg) { setNotResponding(true); - notRespondingReport = mService.mAppErrors.generateProcessError(this, - ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, - activity, shortMsg, longMsg, null); + // 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; + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 75f299c54e79..f100efc9bb89 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -776,6 +776,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); diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java new file mode 100644 index 000000000000..5b85980d3f1c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java @@ -0,0 +1,160 @@ +/* + * 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); + } +} |