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 android.annotation.Nullable; 20 import android.app.AnrController; 21 import android.app.Dialog; 22 import android.content.Context; 23 24 import com.android.internal.annotations.GuardedBy; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.function.Consumer; 29 30 /** 31 * A controller to generate error dialogs in {@link ProcessRecord}. 32 */ 33 final class ErrorDialogController { 34 private final ProcessRecord mApp; 35 private final ActivityManagerService mService; 36 private final ActivityManagerGlobalLock mProcLock; 37 38 /** 39 * Dialogs being displayed due to crash. 40 */ 41 @GuardedBy("mProcLock") 42 private List<AppErrorDialog> mCrashDialogs; 43 44 /** 45 * Dialogs being displayed due to app not responding. 46 */ 47 @GuardedBy("mProcLock") 48 private List<AppNotRespondingDialog> mAnrDialogs; 49 50 /** 51 * Dialogs displayed due to strict mode violation. 52 */ 53 @GuardedBy("mProcLock") 54 private List<StrictModeViolationDialog> mViolationDialogs; 55 56 /** 57 * Current wait for debugger dialog. 58 */ 59 @GuardedBy("mProcLock") 60 private AppWaitingForDebuggerDialog mWaitDialog; 61 62 /** 63 * ANR dialog controller 64 */ 65 @GuardedBy("mProcLock") 66 @Nullable 67 private AnrController mAnrController; 68 69 @GuardedBy("mProcLock") hasCrashDialogs()70 boolean hasCrashDialogs() { 71 return mCrashDialogs != null; 72 } 73 74 @GuardedBy("mProcLock") getCrashDialogs()75 List<AppErrorDialog> getCrashDialogs() { 76 return mCrashDialogs; 77 } 78 79 @GuardedBy("mProcLock") hasAnrDialogs()80 boolean hasAnrDialogs() { 81 return mAnrDialogs != null; 82 } 83 84 @GuardedBy("mProcLock") getAnrDialogs()85 List<AppNotRespondingDialog> getAnrDialogs() { 86 return mAnrDialogs; 87 } 88 89 @GuardedBy("mProcLock") hasViolationDialogs()90 boolean hasViolationDialogs() { 91 return mViolationDialogs != null; 92 } 93 94 @GuardedBy("mProcLock") hasDebugWaitingDialog()95 boolean hasDebugWaitingDialog() { 96 return mWaitDialog != null; 97 } 98 99 @GuardedBy("mProcLock") clearAllErrorDialogs()100 void clearAllErrorDialogs() { 101 clearCrashDialogs(); 102 clearAnrDialogs(); 103 clearViolationDialogs(); 104 clearWaitingDialog(); 105 } 106 107 @GuardedBy("mProcLock") clearCrashDialogs()108 void clearCrashDialogs() { 109 clearCrashDialogs(true /* needDismiss */); 110 } 111 112 @GuardedBy("mProcLock") clearCrashDialogs(boolean needDismiss)113 void clearCrashDialogs(boolean needDismiss) { 114 if (mCrashDialogs == null) { 115 return; 116 } 117 if (needDismiss) { 118 scheduleForAllDialogs(mCrashDialogs, Dialog::dismiss); 119 } 120 mCrashDialogs = null; 121 } 122 123 @GuardedBy("mProcLock") clearAnrDialogs()124 void clearAnrDialogs() { 125 if (mAnrDialogs == null) { 126 return; 127 } 128 scheduleForAllDialogs(mAnrDialogs, Dialog::dismiss); 129 mAnrDialogs = null; 130 mAnrController = null; 131 } 132 133 @GuardedBy("mProcLock") clearViolationDialogs()134 void clearViolationDialogs() { 135 if (mViolationDialogs == null) { 136 return; 137 } 138 scheduleForAllDialogs(mViolationDialogs, Dialog::dismiss); 139 mViolationDialogs = null; 140 } 141 142 @GuardedBy("mProcLock") clearWaitingDialog()143 void clearWaitingDialog() { 144 if (mWaitDialog == null) { 145 return; 146 } 147 mWaitDialog.dismiss(); 148 mWaitDialog = null; 149 } 150 151 @GuardedBy("mProcLock") scheduleForAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c)152 void scheduleForAllDialogs(List<? extends BaseErrorDialog> dialogs, 153 Consumer<BaseErrorDialog> c) { 154 mService.mUiHandler.post(() -> { 155 if (dialogs != null) { 156 forAllDialogs(dialogs, c); 157 } 158 }); 159 } 160 forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c)161 void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) { 162 for (int i = dialogs.size() - 1; i >= 0; i--) { 163 c.accept(dialogs.get(i)); 164 } 165 } 166 167 @GuardedBy("mProcLock") showCrashDialogs(AppErrorDialog.Data data)168 void showCrashDialogs(AppErrorDialog.Data data) { 169 List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */); 170 mCrashDialogs = new ArrayList<>(); 171 for (int i = contexts.size() - 1; i >= 0; i--) { 172 final Context c = contexts.get(i); 173 mCrashDialogs.add(new AppErrorDialog(c, mService, data)); 174 } 175 mService.mUiHandler.post(() -> { 176 List<AppErrorDialog> dialogs; 177 synchronized (mProcLock) { 178 dialogs = mCrashDialogs; 179 } 180 if (dialogs != null) { 181 forAllDialogs(dialogs, Dialog::show); 182 } 183 }); 184 } 185 186 @GuardedBy("mProcLock") showAnrDialogs(AppNotRespondingDialog.Data data)187 void showAnrDialogs(AppNotRespondingDialog.Data data) { 188 List<Context> contexts = getDisplayContexts( 189 mApp.mErrorState.isSilentAnr() /* lastUsedOnly */); 190 mAnrDialogs = new ArrayList<>(); 191 for (int i = contexts.size() - 1; i >= 0; i--) { 192 final Context c = contexts.get(i); 193 mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data)); 194 } 195 scheduleForAllDialogs(mAnrDialogs, Dialog::show); 196 } 197 198 @GuardedBy("mProcLock") showViolationDialogs(AppErrorResult res)199 void showViolationDialogs(AppErrorResult res) { 200 List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */); 201 mViolationDialogs = new ArrayList<>(); 202 for (int i = contexts.size() - 1; i >= 0; i--) { 203 final Context c = contexts.get(i); 204 mViolationDialogs.add( 205 new StrictModeViolationDialog(c, mService, res, mApp)); 206 } 207 scheduleForAllDialogs(mViolationDialogs, Dialog::show); 208 } 209 210 @GuardedBy("mProcLock") showDebugWaitingDialogs()211 void showDebugWaitingDialogs() { 212 List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */); 213 final Context c = contexts.get(0); 214 mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, mApp); 215 216 mService.mUiHandler.post(() -> { 217 Dialog dialog; 218 synchronized (mProcLock) { 219 dialog = mWaitDialog; 220 } 221 if (dialog != null) { 222 dialog.show(); 223 } 224 }); 225 } 226 227 @GuardedBy("mProcLock") 228 @Nullable getAnrController()229 AnrController getAnrController() { 230 return mAnrController; 231 } 232 233 @GuardedBy("mProcLock") setAnrController(AnrController controller)234 void setAnrController(AnrController controller) { 235 mAnrController = controller; 236 } 237 238 /** 239 * Helper function to collect contexts from crashed app located displays. 240 * 241 * @param lastUsedOnly Sets to {@code true} to indicate to only get last used context. 242 * Sets to {@code false} to collect contexts from crashed app located 243 * displays. 244 * 245 * @return display context list. 246 */ getDisplayContexts(boolean lastUsedOnly)247 private List<Context> getDisplayContexts(boolean lastUsedOnly) { 248 List<Context> displayContexts = new ArrayList<>(); 249 if (!lastUsedOnly) { 250 mApp.getWindowProcessController().getDisplayContextsWithErrorDialogs(displayContexts); 251 } 252 // If there is no foreground window display, fallback to last used display. 253 if (displayContexts.isEmpty() || lastUsedOnly) { 254 displayContexts.add(mService.mWmInternal != null 255 ? mService.mWmInternal.getTopFocusedDisplayUiContext() 256 : mService.mUiContext); 257 } 258 return displayContexts; 259 } 260 ErrorDialogController(ProcessRecord app)261 ErrorDialogController(ProcessRecord app) { 262 mApp = app; 263 mService = app.mService; 264 mProcLock = mService.mProcLock; 265 } 266 } 267