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 package com.android.server.wm;
17 
18 
19 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
20 
21 import static com.android.server.wm.ActivityRecord.INVALID_PID;
22 import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24 
25 import android.annotation.NonNull;
26 import android.os.Build;
27 import android.os.IBinder;
28 import android.os.Process;
29 import android.os.SystemClock;
30 import android.os.Trace;
31 import android.util.ArrayMap;
32 import android.util.Slog;
33 import android.util.SparseArray;
34 import android.view.InputApplicationHandle;
35 
36 import com.android.internal.os.TimeoutRecord;
37 import com.android.server.FgThread;
38 import com.android.server.am.StackTracesDumpHelper;
39 import com.android.server.criticalevents.CriticalEventLog;
40 
41 import java.io.File;
42 import java.util.ArrayList;
43 import java.util.OptionalInt;
44 import java.util.concurrent.CompletableFuture;
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.TimeUnit;
47 
48 /**
49  * Translates input channel tokens and app tokens to ProcessRecords and PIDs that AMS can use to
50  * blame unresponsive apps. This class also handles dumping WMS state when an app becomes
51  * unresponsive.
52  */
53 class AnrController {
54     /** Prevent spamming the traces because pre-dump cannot aware duplicated ANR. */
55     private static final long PRE_DUMP_MIN_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
56     /** The timeout to detect if a monitor is held for a while. */
57     private static final long PRE_DUMP_MONITOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
58     /** The last time pre-dump was executed. */
59     private volatile long mLastPreDumpTimeMs;
60 
61     private final SparseArray<ActivityRecord> mUnresponsiveAppByDisplay = new SparseArray<>();
62 
63     private final WindowManagerService mService;
AnrController(WindowManagerService service)64     AnrController(WindowManagerService service) {
65         mService = service;
66     }
67 
notifyAppUnresponsive(InputApplicationHandle applicationHandle, TimeoutRecord timeoutRecord)68     void notifyAppUnresponsive(InputApplicationHandle applicationHandle,
69             TimeoutRecord timeoutRecord) {
70         try {
71             timeoutRecord.mLatencyTracker.notifyAppUnresponsiveStarted();
72             timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowStarted();
73             preDumpIfLockTooSlow();
74             timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowEnded();
75             final ActivityRecord activity;
76             timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
77             boolean blamePendingFocusRequest = false;
78             IBinder focusToken = null;
79             WindowState targetWindowState = null;
80             synchronized (mService.mGlobalLock) {
81                 timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
82                 activity = ActivityRecord.forTokenLocked(applicationHandle.token);
83                 if (activity == null) {
84                     Slog.e(TAG_WM, "Unknown app appToken:" + applicationHandle.name
85                             + ". Dropping notifyNoFocusedWindowAnr request");
86                     return;
87                 } else if (activity.mAppStopped) {
88                     Slog.d(TAG_WM, "App is in stopped state:" + applicationHandle.name
89                             + ". Dropping notifyNoFocusedWindowAnr request");
90                     return;
91                 }
92 
93                 // App is unresponsive, but we are actively trying to give focus to a window.
94                 // Blame the window if possible since the window may not belong to the app.
95                 DisplayContent display = mService.mRoot.getDisplayContent(activity.getDisplayId());
96                 if (display != null) {
97                     focusToken = display.getInputMonitor().mInputFocus;
98                 }
99                 InputTarget focusTarget = mService.getInputTargetFromToken(focusToken);
100 
101                 if (focusTarget != null) {
102                     // Check if we have a recent focus request, newer than the dispatch timeout,
103                     // then ignore the focus request.
104                     targetWindowState = focusTarget.getWindowState();
105                     blamePendingFocusRequest = SystemClock.uptimeMillis()
106                             - display.getInputMonitor().mInputFocusRequestTimeMillis
107                             >= getInputDispatchingTimeoutMillisLocked(
108                                     targetWindowState.getActivityRecord());
109                 }
110 
111                 if (!blamePendingFocusRequest) {
112                     Slog.i(TAG_WM, "ANR in " + activity.getName() + ".  Reason: "
113                             + timeoutRecord.mReason);
114                     mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity);
115                 }
116             }
117 
118             if (blamePendingFocusRequest && notifyWindowUnresponsive(focusToken, timeoutRecord)) {
119                 Slog.i(TAG_WM, "Blamed " + targetWindowState.getName()
120                         + " using pending focus request. Focused activity: "
121                         + activity.getName());
122             } else {
123                 activity.inputDispatchingTimedOut(timeoutRecord, INVALID_PID);
124             }
125 
126             if (!blamePendingFocusRequest) {
127                 dumpAnrStateAsync(activity, null /* windowState */, timeoutRecord.mReason);
128             }
129 
130         } finally {
131             timeoutRecord.mLatencyTracker.notifyAppUnresponsiveEnded();
132         }
133     }
134 
135 
136     /**
137      * Notify a window was unresponsive.
138      *
139      * @param token         - the input token of the window
140      * @param pid           - the pid of the window, if known
141      * @param timeoutRecord - details for the timeout
142      */
notifyWindowUnresponsive(@onNull IBinder token, @NonNull OptionalInt pid, @NonNull TimeoutRecord timeoutRecord)143     void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
144             @NonNull TimeoutRecord timeoutRecord) {
145         try {
146             timeoutRecord.mLatencyTracker.notifyWindowUnresponsiveStarted();
147             if (notifyWindowUnresponsive(token, timeoutRecord)) {
148                 return;
149             }
150             if (!pid.isPresent()) {
151                 Slog.w(TAG_WM, "Failed to notify that window token=" + token
152                         + " was unresponsive.");
153                 return;
154             }
155             notifyWindowUnresponsive(pid.getAsInt(), timeoutRecord);
156         } finally {
157             timeoutRecord.mLatencyTracker.notifyWindowUnresponsiveEnded();
158         }
159     }
160 
161     /**
162      * Notify a window identified by its input token was unresponsive.
163      *
164      * @return true if the window was identified by the given input token and the request was
165      *         handled, false otherwise.
166      */
notifyWindowUnresponsive(@onNull IBinder inputToken, TimeoutRecord timeoutRecord)167     private boolean notifyWindowUnresponsive(@NonNull IBinder inputToken,
168             TimeoutRecord timeoutRecord) {
169         timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowStarted();
170         preDumpIfLockTooSlow();
171         timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowEnded();
172         final int pid;
173         final boolean aboveSystem;
174         final ActivityRecord activity;
175         final WindowState windowState;
176         timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
177         synchronized (mService.mGlobalLock) {
178             timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
179             InputTarget target = mService.getInputTargetFromToken(inputToken);
180             if (target == null) {
181                 return false;
182             }
183             windowState = target.getWindowState();
184             pid = target.getPid();
185             // Blame the activity if the input token belongs to the window. If the target is
186             // embedded, then we will blame the pid instead.
187             activity = (windowState.mInputChannelToken == inputToken)
188                     ? windowState.mActivityRecord : null;
189             Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + timeoutRecord.mReason);
190             aboveSystem = isWindowAboveSystem(windowState);
191         }
192         if (activity != null) {
193             activity.inputDispatchingTimedOut(timeoutRecord, pid);
194         } else {
195             mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, timeoutRecord);
196         }
197         dumpAnrStateAsync(activity, windowState, timeoutRecord.mReason);
198         return true;
199     }
200 
201     /**
202      * Notify a window owned by the provided pid was unresponsive.
203      */
notifyWindowUnresponsive(int pid, TimeoutRecord timeoutRecord)204     private void notifyWindowUnresponsive(int pid, TimeoutRecord timeoutRecord) {
205         Slog.i(TAG_WM,
206                 "ANR in input window owned by pid=" + pid + ". Reason: " + timeoutRecord.mReason);
207         // We cannot determine the z-order of the window, so place the anr dialog as high
208         // as possible.
209         mService.mAmInternal.inputDispatchingTimedOut(pid, true /*aboveSystem*/, timeoutRecord);
210         dumpAnrStateAsync(null /* activity */, null /* windowState */, timeoutRecord.mReason);
211     }
212 
213     /**
214      * Notify a window was responsive after previously being unresponsive.
215      *
216      * @param token - the input token of the window
217      * @param pid - the pid of the window, if known
218      */
notifyWindowResponsive(@onNull IBinder token, @NonNull OptionalInt pid)219     void notifyWindowResponsive(@NonNull IBinder token, @NonNull OptionalInt pid) {
220         if (notifyWindowResponsive(token)) {
221             return;
222         }
223         if (!pid.isPresent()) {
224             Slog.w(TAG_WM, "Failed to notify that window token=" + token + " was responsive.");
225             return;
226         }
227         notifyWindowResponsive(pid.getAsInt());
228     }
229 
230     /**
231      * Notify a window identified by its input token was responsive after previously being
232      * unresponsive.
233      *
234      * @return true if the window was identified by the given input token and the request was
235      *         handled, false otherwise.
236      */
notifyWindowResponsive(@onNull IBinder inputToken)237     private boolean notifyWindowResponsive(@NonNull IBinder inputToken) {
238         final int pid;
239         synchronized (mService.mGlobalLock) {
240             InputTarget target = mService.getInputTargetFromToken(inputToken);
241             if (target == null) {
242                 return false;
243             }
244             pid = target.getPid();
245         }
246         mService.mAmInternal.inputDispatchingResumed(pid);
247         return true;
248     }
249 
250     /**
251      * Notify a window owned by the provided pid was responsive after previously being unresponsive.
252      */
notifyWindowResponsive(int pid)253     private void notifyWindowResponsive(int pid) {
254         mService.mAmInternal.inputDispatchingResumed(pid);
255     }
256 
257     /**
258      * If we reported an unresponsive apps to AMS, notify AMS that the app is now responsive if a
259      * window belonging to the app gets focused.
260      * <p>
261      * @param newFocus new focused window
262      */
onFocusChanged(WindowState newFocus)263     void onFocusChanged(WindowState newFocus) {
264         ActivityRecord unresponsiveApp;
265         synchronized (mService.mGlobalLock) {
266             unresponsiveApp = mUnresponsiveAppByDisplay.get(newFocus.getDisplayId());
267             if (unresponsiveApp == null || unresponsiveApp != newFocus.mActivityRecord) {
268                 return;
269             }
270         }
271         mService.mAmInternal.inputDispatchingResumed(unresponsiveApp.getPid());
272         mUnresponsiveAppByDisplay.remove(newFocus.getDisplayId());
273     }
274 
275     /**
276      * Pre-dump stack trace if the locks of activity manager or window manager (they may be locked
277      * in the path of reporting ANR) cannot be acquired in time. That provides the stack traces
278      * before the real blocking symptom has gone.
279      * <p>
280      * Do not hold the {@link WindowManagerGlobalLock} while calling this method.
281      */
preDumpIfLockTooSlow()282     private void preDumpIfLockTooSlow() {
283         if (!Build.IS_DEBUGGABLE)  {
284             return;
285         }
286         final long now = SystemClock.uptimeMillis();
287         if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) {
288             return;
289         }
290         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "preDumpIfLockTooSlow()");
291         try {
292             final boolean[] shouldDumpSf = { true };
293             final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
294             monitors.put(TAG_WM, mService::monitor);
295             monitors.put("ActivityManager", mService.mAmInternal::monitor);
296             final CountDownLatch latch = new CountDownLatch(monitors.size());
297             // The pre-dump will execute if one of the monitors doesn't complete within
298             // the timeout.
299             for (int i = 0; i < monitors.size(); i++) {
300                 final String name = monitors.keyAt(i);
301                 final Runnable monitor = monitors.valueAt(i);
302                 // Always create new thread to avoid noise of existing threads. Suppose here won't
303                 // create too many threads because it means that watchdog will be triggered first.
304                 new Thread() {
305                     @Override
306                     public void run() {
307                         monitor.run();
308                         latch.countDown();
309                         final long elapsed = SystemClock.uptimeMillis() - now;
310                         if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
311                             Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
312                         } else if (TAG_WM.equals(name)) {
313                             // Window manager is the main client of SurfaceFlinger.
314                             // If window manager is responsive, the stack traces
315                             // of SurfaceFlinger may not be important.
316                             shouldDumpSf[0] = false;
317                         }
318                     };
319                 }.start();
320             }
321             try {
322                 if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
323                     return;
324                 }
325             } catch (InterruptedException ignored) { }
326             mLastPreDumpTimeMs = now;
327             Slog.i(TAG_WM, "Pre-dump for unresponsive");
328 
329             final ArrayList<Integer> firstPids = new ArrayList<>(1);
330             firstPids.add(WindowManagerService.MY_PID);
331             ArrayList<Integer> nativePids = null;
332             final int[] pids = shouldDumpSf[0]
333                     ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
334                     : null;
335             if (pids != null) {
336                 nativePids = new ArrayList<>(1);
337                 for (int pid : pids) {
338                     nativePids.add(pid);
339                 }
340             }
341 
342             String criticalEvents =
343                     CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
344             final File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
345                     null /* processCpuTracker */, null /* lastPids */,
346                     CompletableFuture.completedFuture(nativePids),
347                     null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents,
348                     Runnable::run, null/* AnrLatencyTracker */);
349             if (tracesFile != null) {
350                 tracesFile.renameTo(
351                         new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
352             }
353         } finally {
354             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
355         }
356 
357     }
358 
359     /**
360      * Executes asynchronously on the fg thread not to block the stack dump for
361      * the ANRing processes.
362      */
dumpAnrStateAsync(ActivityRecord activity, WindowState windowState, String reason)363     private void dumpAnrStateAsync(ActivityRecord activity, WindowState windowState,
364             String reason) {
365         FgThread.getExecutor().execute(() -> {
366             try {
367                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "dumpAnrStateLocked()");
368                 synchronized (mService.mGlobalLock) {
369                     mService.saveANRStateLocked(activity, windowState, reason);
370                     mService.mAtmService.saveANRState(reason);
371                 }
372             } finally {
373                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
374             }
375         });
376     }
377 
isWindowAboveSystem(@onNull WindowState windowState)378     private boolean isWindowAboveSystem(@NonNull WindowState windowState) {
379         int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
380                 TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
381         return windowState.mBaseLayer > systemAlertLayer;
382     }
383 }
384