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