• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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