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.WindowManagerDebugConfig.TAG_WM; 23 24 import android.os.Build; 25 import android.os.IBinder; 26 import android.os.Process; 27 import android.os.SystemClock; 28 import android.util.ArrayMap; 29 import android.util.Slog; 30 import android.util.SparseArray; 31 import android.view.InputApplicationHandle; 32 33 import com.android.server.am.ActivityManagerService; 34 35 import java.io.File; 36 import java.util.ArrayList; 37 import java.util.concurrent.CountDownLatch; 38 import java.util.concurrent.TimeUnit; 39 40 /** 41 * Translates input channel tokens and app tokens to ProcessRecords and PIDs that AMS can use to 42 * blame unresponsive apps. This class also handles dumping WMS state when an app becomes 43 * unresponsive. 44 */ 45 class AnrController { 46 /** Prevent spamming the traces because pre-dump cannot aware duplicated ANR. */ 47 private static final long PRE_DUMP_MIN_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20); 48 /** The timeout to detect if a monitor is held for a while. */ 49 private static final long PRE_DUMP_MONITOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1); 50 /** The last time pre-dump was executed. */ 51 private volatile long mLastPreDumpTimeMs; 52 53 private final SparseArray<ActivityRecord> mUnresponsiveAppByDisplay = new SparseArray<>(); 54 55 private final WindowManagerService mService; AnrController(WindowManagerService service)56 AnrController(WindowManagerService service) { 57 mService = service; 58 } 59 notifyAppUnresponsive(InputApplicationHandle applicationHandle, String reason)60 void notifyAppUnresponsive(InputApplicationHandle applicationHandle, String reason) { 61 preDumpIfLockTooSlow(); 62 final ActivityRecord activity; 63 synchronized (mService.mGlobalLock) { 64 activity = ActivityRecord.forTokenLocked(applicationHandle.token); 65 if (activity == null) { 66 Slog.e(TAG_WM, "Unknown app appToken:" + applicationHandle.name 67 + ". Dropping notifyNoFocusedWindowAnr request"); 68 return; 69 } 70 Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: " + reason); 71 dumpAnrStateLocked(activity, null /* windowState */, reason); 72 mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity); 73 } 74 activity.inputDispatchingTimedOut(reason, INVALID_PID); 75 } 76 notifyWindowUnresponsive(IBinder inputToken, String reason)77 void notifyWindowUnresponsive(IBinder inputToken, String reason) { 78 preDumpIfLockTooSlow(); 79 final int pid; 80 final boolean aboveSystem; 81 final ActivityRecord activity; 82 synchronized (mService.mGlobalLock) { 83 InputTarget target = mService.getInputTargetFromToken(inputToken); 84 if (target == null) { 85 Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request"); 86 return; 87 } 88 89 WindowState windowState = target.getWindowState(); 90 pid = target.getPid(); 91 // Blame the activity if the input token belongs to the window. If the target is 92 // embedded, then we will blame the pid instead. 93 activity = (windowState.mInputChannelToken == inputToken) 94 ? windowState.mActivityRecord : null; 95 Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + reason); 96 aboveSystem = isWindowAboveSystem(windowState); 97 dumpAnrStateLocked(activity, windowState, reason); 98 } 99 if (activity != null) { 100 activity.inputDispatchingTimedOut(reason, pid); 101 } else { 102 mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, reason); 103 } 104 } 105 notifyWindowResponsive(IBinder inputToken)106 void notifyWindowResponsive(IBinder inputToken) { 107 final int pid; 108 synchronized (mService.mGlobalLock) { 109 InputTarget target = mService.getInputTargetFromToken(inputToken); 110 if (target == null) { 111 Slog.e(TAG_WM, "Unknown token, dropping notifyWindowConnectionResponsive request"); 112 return; 113 } 114 pid = target.getPid(); 115 } 116 mService.mAmInternal.inputDispatchingResumed(pid); 117 } 118 notifyGestureMonitorUnresponsive(int gestureMonitorPid, String reason)119 void notifyGestureMonitorUnresponsive(int gestureMonitorPid, String reason) { 120 preDumpIfLockTooSlow(); 121 synchronized (mService.mGlobalLock) { 122 Slog.i(TAG_WM, "ANR in gesture monitor owned by pid:" + gestureMonitorPid 123 + ". Reason: " + reason); 124 dumpAnrStateLocked(null /* activity */, null /* windowState */, reason); 125 } 126 mService.mAmInternal.inputDispatchingTimedOut(gestureMonitorPid, /* aboveSystem */ true, 127 reason); 128 } 129 notifyGestureMonitorResponsive(int gestureMonitorPid)130 void notifyGestureMonitorResponsive(int gestureMonitorPid) { 131 mService.mAmInternal.inputDispatchingResumed(gestureMonitorPid); 132 } 133 134 /** 135 * If we reported an unresponsive apps to AMS, notify AMS that the app is now responsive if a 136 * window belonging to the app gets focused. 137 * <p> 138 * @param newFocus new focused window 139 */ onFocusChanged(WindowState newFocus)140 void onFocusChanged(WindowState newFocus) { 141 ActivityRecord unresponsiveApp; 142 synchronized (mService.mGlobalLock) { 143 unresponsiveApp = mUnresponsiveAppByDisplay.get(newFocus.getDisplayId()); 144 if (unresponsiveApp == null || unresponsiveApp != newFocus.mActivityRecord) { 145 return; 146 } 147 } 148 mService.mAmInternal.inputDispatchingResumed(unresponsiveApp.getPid()); 149 } 150 151 /** 152 * Pre-dump stack trace if the locks of activity manager or window manager (they may be locked 153 * in the path of reporting ANR) cannot be acquired in time. That provides the stack traces 154 * before the real blocking symptom has gone. 155 * <p> 156 * Do not hold the {@link WindowManagerGlobalLock} while calling this method. 157 */ preDumpIfLockTooSlow()158 private void preDumpIfLockTooSlow() { 159 if (!Build.IS_DEBUGGABLE) { 160 return; 161 } 162 final long now = SystemClock.uptimeMillis(); 163 if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) { 164 return; 165 } 166 167 final boolean[] shouldDumpSf = { true }; 168 final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2); 169 monitors.put(TAG_WM, mService::monitor); 170 monitors.put("ActivityManager", mService.mAmInternal::monitor); 171 final CountDownLatch latch = new CountDownLatch(monitors.size()); 172 // The pre-dump will execute if one of the monitors doesn't complete within the timeout. 173 for (int i = 0; i < monitors.size(); i++) { 174 final String name = monitors.keyAt(i); 175 final Runnable monitor = monitors.valueAt(i); 176 // Always create new thread to avoid noise of existing threads. Suppose here won't 177 // create too many threads because it means that watchdog will be triggered first. 178 new Thread() { 179 @Override 180 public void run() { 181 monitor.run(); 182 latch.countDown(); 183 final long elapsed = SystemClock.uptimeMillis() - now; 184 if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) { 185 Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms"); 186 } else if (TAG_WM.equals(name)) { 187 // Window manager is the main client of SurfaceFlinger. If window manager 188 // is responsive, the stack traces of SurfaceFlinger may not be important. 189 shouldDumpSf[0] = false; 190 } 191 }; 192 }.start(); 193 } 194 try { 195 if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 196 return; 197 } 198 } catch (InterruptedException ignored) { } 199 mLastPreDumpTimeMs = now; 200 Slog.i(TAG_WM, "Pre-dump for unresponsive"); 201 202 final ArrayList<Integer> firstPids = new ArrayList<>(1); 203 firstPids.add(ActivityManagerService.MY_PID); 204 ArrayList<Integer> nativePids = null; 205 final int[] pids = shouldDumpSf[0] 206 ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" }) 207 : null; 208 if (pids != null) { 209 nativePids = new ArrayList<>(1); 210 for (int pid : pids) { 211 nativePids.add(pid); 212 } 213 } 214 215 final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids, 216 null /* processCpuTracker */, null /* lastPids */, nativePids, 217 null /* logExceptionCreatingFile */); 218 if (tracesFile != null) { 219 tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre")); 220 } 221 } 222 dumpAnrStateLocked(ActivityRecord activity, WindowState windowState, String reason)223 private void dumpAnrStateLocked(ActivityRecord activity, WindowState windowState, 224 String reason) { 225 mService.saveANRStateLocked(activity, windowState, reason); 226 mService.mAtmInternal.saveANRState(reason); 227 } 228 isWindowAboveSystem(WindowState windowState)229 private boolean isWindowAboveSystem(WindowState windowState) { 230 if (windowState == null) { 231 // If the window state is not available we cannot easily determine its z order. Try to 232 // place the anr dialog as high as possible. 233 return true; 234 } 235 int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw( 236 TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow); 237 return windowState.mBaseLayer > systemAlertLayer; 238 } 239 } 240