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