blob: 6350e225a429afc01ba28690fabbc22279878448 [file] [log] [blame]
/*
* 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.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.FlakyTest;
import com.android.internal.os.TimeoutRecord;
import com.android.server.LocalServices;
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.concurrent.ExecutorService;
/**
* 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;
private ProcessErrorStateRecord mProcessErrorState;
private ExecutorService mExecutorService;
@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();
final AppProfiler profiler = mock(AppProfiler.class);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
setFieldValue(ActivityManagerService.class, sService, "mProcLock",
new ActivityManagerProcLock());
final ProcessList processList = new ProcessList();
setFieldValue(ActivityManagerService.class, sService, "mProcessList", processList);
});
// Avoid NPE when initializing {@link ProcessRecord#mWindowProcessController}.
final PackageManagerInternal packageManagerInternal = mock(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, packageManagerInternal);
final ComponentName sysUiName = new ComponentName(sContext.getPackageName(), "test");
doReturn(sysUiName).when(packageManagerInternal).getSystemUiServiceComponent();
}
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Field mfield = Field.class.getDeclaredField("accessFlags");
mfield.setAccessible(true);
mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
field.set(obj, val);
} catch (NoSuchFieldException | IllegalAccessException e) {
}
}
@AfterClass
public static void tearDownOnce() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@Before
public void setUpProcess() throws Exception {
// Need to run with dexmaker share class loader to mock package private class.
runWithDexmakerShareClassLoader(() -> {
mProcessRecord = new ProcessRecord(sService, sContext.getApplicationInfo(),
"name", 12345);
mExecutorService = mock(ExecutorService.class);
mProcessErrorState = spy(mProcessRecord.mErrorState);
doNothing().when(mProcessErrorState).startAppProblemLSP();
doReturn(false).when(mProcessErrorState).isSilentAnr();
doReturn(false).when(mProcessErrorState).isMonitorCpuUsage();
});
}
/**
* 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(mProcessErrorState.isNotResponding());
assertFalse(mProcessErrorState.isCrashing());
assertFalse(mProcessRecord.isKilledByAm());
assertFalse(mProcessRecord.isKilled());
}
/**
* This test verifies that if the process is crashing, Anr will do nothing.
*/
@Test
public void testAnrWhenCrash() {
mProcessErrorState.setCrashing(true);
assertTrue(mProcessErrorState.isCrashing());
appNotResponding(mProcessErrorState, "Test ANR when crash");
assertFalse(mProcessErrorState.isNotResponding());
assertFalse(mProcessRecord.isKilledByAm());
assertFalse(mProcessRecord.isKilled());
}
/**
* This test verifies that if the process is killed by AM, Anr will do nothing.
*/
@Test
public void testAnrWhenKilledByAm() {
mProcessRecord.setKilledByAm(true);
appNotResponding(mProcessErrorState, "Test ANR when killed by AM");
assertFalse(mProcessErrorState.isNotResponding());
assertFalse(mProcessErrorState.isCrashing());
assertFalse(mProcessRecord.isKilled());
}
/**
* This test verifies that if the process is killed, Anr will do nothing.
*/
@Test
public void testAnrWhenKilled() {
mProcessRecord.setKilled(true);
appNotResponding(mProcessErrorState, "Test ANR when killed");
assertFalse(mProcessErrorState.isNotResponding());
assertFalse(mProcessErrorState.isCrashing());
assertFalse(mProcessRecord.isKilledByAm());
}
/**
* This test verifies that non-silent ANR can run through successfully and the corresponding
* flags can be set correctly.
*/
@Test
public void testNonSilentAnr() {
appNotResponding(mProcessErrorState, "Test non-silent ANR");
assertTrue(mProcessErrorState.isNotResponding());
assertFalse(mProcessErrorState.isCrashing());
assertFalse(mProcessRecord.isKilledByAm());
assertFalse(mProcessRecord.isKilled());
}
/**
* 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(mProcessErrorState).isSilentAnr();
appNotResponding(mProcessErrorState, "Test silent ANR");
assertTrue(mProcessErrorState.isNotResponding());
assertFalse(mProcessErrorState.isCrashing());
assertTrue(mProcessRecord.isKilledByAm());
assertTrue(mProcessRecord.isKilled());
}
private void appNotResponding(ProcessErrorStateRecord processErrorState,
String annotation) {
TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(annotation);
processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
false /* aboveSystem */, timeoutRecord, mExecutorService, false /* onlyDumpSelf */,
false /*isContinuousAnr*/);
}
}