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 final BaseErrorDialog dialog = mWaitDialog; 148 mService.mUiHandler.post(dialog::dismiss); 149 mWaitDialog = null; 150 } 151 152 @GuardedBy("mProcLock") scheduleForAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c)153 void scheduleForAllDialogs(List<? extends BaseErrorDialog> dialogs, 154 Consumer<BaseErrorDialog> c) { 155 mService.mUiHandler.post(() -> { 156 if (dialogs != null) { 157 forAllDialogs(dialogs, c); 158 } 159 }); 160 } 161 forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c)162 void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) { 163 for (int i = dialogs.size() - 1; i >= 0; i--) { 164 c.accept(dialogs.get(i)); 165 } 166 } 167 168 @GuardedBy("mProcLock") showCrashDialogs(AppErrorDialog.Data data)169 void showCrashDialogs(AppErrorDialog.Data data) { 170 List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */); 171 mCrashDialogs = new ArrayList<>(); 172 for (int i = contexts.size() - 1; i >= 0; i--) { 173 final Context c = contexts.get(i); 174 mCrashDialogs.add(new AppErrorDialog(c, mService, data)); 175 } 176 mService.mUiHandler.post(() -> { 177 List<AppErrorDialog> dialogs; 178 synchronized (mProcLock) { 179 dialogs = mCrashDialogs; 180 } 181 if (dialogs != null) { 182 forAllDialogs(dialogs, Dialog::show); 183 } 184 }); 185 } 186 187 @GuardedBy("mProcLock") showAnrDialogs(AppNotRespondingDialog.Data data)188 void showAnrDialogs(AppNotRespondingDialog.Data data) { 189 List<Context> contexts = getDisplayContexts( 190 mApp.mErrorState.isSilentAnr() /* lastUsedOnly */); 191 mAnrDialogs = new ArrayList<>(); 192 for (int i = contexts.size() - 1; i >= 0; i--) { 193 final Context c = contexts.get(i); 194 mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data)); 195 } 196 scheduleForAllDialogs(mAnrDialogs, Dialog::show); 197 } 198 199 @GuardedBy("mProcLock") showViolationDialogs(AppErrorResult res)200 void showViolationDialogs(AppErrorResult res) { 201 List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */); 202 mViolationDialogs = new ArrayList<>(); 203 for (int i = contexts.size() - 1; i >= 0; i--) { 204 final Context c = contexts.get(i); 205 mViolationDialogs.add( 206 new StrictModeViolationDialog(c, mService, res, mApp)); 207 } 208 scheduleForAllDialogs(mViolationDialogs, Dialog::show); 209 } 210 211 @GuardedBy("mProcLock") showDebugWaitingDialogs()212 void showDebugWaitingDialogs() { 213 List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */); 214 final Context c = contexts.get(0); 215 mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, mApp); 216 217 mService.mUiHandler.post(() -> { 218 Dialog dialog; 219 synchronized (mProcLock) { 220 dialog = mWaitDialog; 221 } 222 if (dialog != null) { 223 dialog.show(); 224 } 225 }); 226 } 227 228 @GuardedBy("mProcLock") 229 @Nullable getAnrController()230 AnrController getAnrController() { 231 return mAnrController; 232 } 233 234 @GuardedBy("mProcLock") setAnrController(AnrController controller)235 void setAnrController(AnrController controller) { 236 mAnrController = controller; 237 } 238 239 /** 240 * Helper function to collect contexts from crashed app located displays. 241 * 242 * @param lastUsedOnly Sets to {@code true} to indicate to only get last used context. 243 * Sets to {@code false} to collect contexts from crashed app located 244 * displays. 245 * 246 * @return display context list. 247 */ getDisplayContexts(boolean lastUsedOnly)248 private List<Context> getDisplayContexts(boolean lastUsedOnly) { 249 List<Context> displayContexts = new ArrayList<>(); 250 if (!lastUsedOnly) { 251 mApp.getWindowProcessController().getDisplayContextsWithErrorDialogs(displayContexts); 252 } 253 // If there is no foreground window display, fallback to last used display. 254 if (displayContexts.isEmpty() || lastUsedOnly) { 255 displayContexts.add(mService.mWmInternal != null 256 ? mService.mWmInternal.getTopFocusedDisplayUiContext() 257 : mService.mUiContext); 258 } 259 return displayContexts; 260 } 261 ErrorDialogController(ProcessRecord app)262 ErrorDialogController(ProcessRecord app) { 263 mApp = app; 264 mService = app.mService; 265 mProcLock = mService.mProcLock; 266 } 267 } 268