1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.am; 18 19 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; 20 21 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 22 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyBoolean; 26 import static org.mockito.ArgumentMatchers.anyString; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.Mockito.doAnswer; 29 import static org.mockito.Mockito.doReturn; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.timeout; 32 import static org.mockito.Mockito.verify; 33 34 import android.content.Context; 35 import android.content.pm.ApplicationInfo; 36 import android.os.Handler; 37 import android.platform.test.annotations.Presubmit; 38 39 import androidx.test.filters.SmallTest; 40 41 import com.android.internal.os.TimeoutRecord; 42 import com.android.server.appop.AppOpsService; 43 import com.android.server.wm.WindowProcessController; 44 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 49 import java.io.File; 50 import java.lang.reflect.Field; 51 import java.lang.reflect.Modifier; 52 import java.util.concurrent.Callable; 53 import java.util.concurrent.CountDownLatch; 54 import java.util.concurrent.ExecutorService; 55 import java.util.concurrent.Future; 56 import java.util.concurrent.TimeUnit; 57 58 /** 59 * Build/Install/Run: 60 * atest FrameworksServicesTests:AnrHelperTest 61 */ 62 @SmallTest 63 @Presubmit 64 public class AnrHelperTest { 65 private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5); 66 private AnrHelper mAnrHelper; 67 68 private ProcessRecord mAnrApp; 69 private ExecutorService mAuxExecutorService; 70 71 private Future<File> mEarlyDumpFuture; 72 @Rule 73 public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); 74 75 @Before setUp()76 public void setUp() { 77 final Context context = getInstrumentation().getTargetContext(); 78 runWithDexmakerShareClassLoader(() -> { 79 mAnrApp = mock(ProcessRecord.class); 80 mAnrApp.mPid = 12345; 81 final ProcessErrorStateRecord errorState = mock(ProcessErrorStateRecord.class); 82 setFieldValue(ProcessErrorStateRecord.class, errorState, "mProcLock", 83 new ActivityManagerProcLock()); 84 setFieldValue(ProcessRecord.class, mAnrApp, "mErrorState", errorState); 85 final ActivityManagerService service = new ActivityManagerService( 86 new ActivityManagerService.Injector(context) { 87 @Override 88 public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile, 89 Handler handler) { 90 return null; 91 } 92 93 @Override 94 public Handler getUiHandler(ActivityManagerService service) { 95 return mServiceThreadRule.getThread().getThreadHandler(); 96 } 97 }, mServiceThreadRule.getThread()); 98 mAuxExecutorService = mock(ExecutorService.class); 99 final ExecutorService earlyDumpExecutorService = mock(ExecutorService.class); 100 mEarlyDumpFuture = mock(Future.class); 101 doReturn(mEarlyDumpFuture).when(earlyDumpExecutorService).submit(any(Callable.class)); 102 103 mAnrHelper = new AnrHelper(service, mAuxExecutorService, earlyDumpExecutorService); 104 }); 105 } 106 setFieldValue(Class clazz, Object obj, String fieldName, T val)107 private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { 108 try { 109 Field field = clazz.getDeclaredField(fieldName); 110 field.setAccessible(true); 111 Field mfield = Field.class.getDeclaredField("accessFlags"); 112 mfield.setAccessible(true); 113 mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE)); 114 field.set(obj, val); 115 } catch (NoSuchFieldException | IllegalAccessException e) { 116 } 117 } 118 119 @Test testHandleAppNotResponding()120 public void testHandleAppNotResponding() { 121 final String activityShortComponentName = "pkg.test/.A"; 122 final String parentShortComponentName = "pkg.test/.P"; 123 final ApplicationInfo appInfo = new ApplicationInfo(); 124 final WindowProcessController parentProcess = mock(WindowProcessController.class); 125 final boolean aboveSystem = false; 126 final String annotation = "test"; 127 final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive( 128 annotation); 129 mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo, 130 parentShortComponentName, parentProcess, aboveSystem, timeoutRecord, 131 /*isContinuousAnr*/ false); 132 133 verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding( 134 eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName), 135 eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService), 136 eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture)); 137 } 138 139 @Test testSkipDuplicatedAnr()140 public void testSkipDuplicatedAnr() { 141 final CountDownLatch consumerLatch = new CountDownLatch(1); 142 final CountDownLatch processingLatch = new CountDownLatch(1); 143 doAnswer(invocation -> { 144 consumerLatch.countDown(); 145 // Simulate that it is dumping to block the consumer thread. 146 processingLatch.await(); 147 return null; 148 }).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(), 149 anyBoolean(), any(), any(), anyBoolean(), anyBoolean(), any()); 150 final ApplicationInfo appInfo = new ApplicationInfo(); 151 final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive( 152 "annotation"); 153 final Runnable reportAnr = () -> mAnrHelper.appNotResponding(mAnrApp, 154 "activityShortComponentName", appInfo, "parentShortComponentName", 155 null /* parentProcess */, false /* aboveSystem */, timeoutRecord, 156 false /*isContinuousAnr*/); 157 reportAnr.run(); 158 // This should be skipped because the pid is pending in queue. 159 reportAnr.run(); 160 // The first reported ANR must be processed. 161 try { 162 assertTrue(consumerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 163 } catch (InterruptedException ignored) { 164 } 165 // This should be skipped because the pid is under processing. 166 reportAnr.run(); 167 168 // Assume that the first one finishes after all incoming ANRs. 169 processingLatch.countDown(); 170 // There is only one ANR reported. 171 verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding( 172 anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService), 173 anyBoolean(), anyBoolean(), any()); 174 } 175 } 176