Merge "Add test to ProcessRecord ANR function"
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index fcc857b..51b1ae1 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.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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
}
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 @@
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 @@
(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 75f299c..f100efc 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 @@
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 0000000..5b85980
--- /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);
+ }
+}