1 /*
2  * Copyright (C) 2006 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.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.res.Resources;
24 import android.os.Build;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.provider.Settings;
29 import android.text.BidiFormatter;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.WindowManager;
33 import android.widget.FrameLayout;
34 import android.widget.TextView;
35 
36 final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
37 
38     private final ActivityManagerService mService;
39     private final AppErrorResult mResult;
40     private final ProcessRecord mProc;
41     private final boolean mIsRestartable;
42 
43     static int CANT_SHOW = -1;
44     static int BACKGROUND_USER = -2;
45     static int ALREADY_SHOWING = -3;
46 
47     // Event 'what' codes
48     static final int FORCE_QUIT = 1;
49     static final int FORCE_QUIT_AND_REPORT = 2;
50     static final int RESTART = 3;
51     static final int MUTE = 5;
52     static final int TIMEOUT = 6;
53     static final int CANCEL = 7;
54     static final int APP_INFO = 8;
55 
56     // 5-minute timeout, then we automatically dismiss the crash dialog
57     static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
58 
AppErrorDialog(Context context, ActivityManagerService service, Data data)59     public AppErrorDialog(Context context, ActivityManagerService service, Data data) {
60         super(context);
61         Resources res = context.getResources();
62 
63         mService = service;
64         mProc = data.proc;
65         mResult = data.result;
66         mIsRestartable = (data.task != null || data.isRestartableForService)
67                 && Settings.Global.getInt(context.getContentResolver(),
68                 Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, 0) != 0;
69         BidiFormatter bidi = BidiFormatter.getInstance();
70 
71         CharSequence name;
72         if ((mProc.pkgList.size() == 1) &&
73                 (name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
74             setTitle(res.getString(
75                     data.repeating ? com.android.internal.R.string.aerr_application_repeated
76                             : com.android.internal.R.string.aerr_application,
77                     bidi.unicodeWrap(name.toString()),
78                     bidi.unicodeWrap(mProc.info.processName)));
79         } else {
80             name = mProc.processName;
81             setTitle(res.getString(
82                     data.repeating ? com.android.internal.R.string.aerr_process_repeated
83                             : com.android.internal.R.string.aerr_process,
84                     bidi.unicodeWrap(name.toString())));
85         }
86 
87         setCancelable(true);
88         setCancelMessage(mHandler.obtainMessage(CANCEL));
89 
90         WindowManager.LayoutParams attrs = getWindow().getAttributes();
91         attrs.setTitle("Application Error: " + mProc.info.processName);
92         attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
93                 | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
94         getWindow().setAttributes(attrs);
95         if (mProc.persistent) {
96             getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
97         }
98 
99         // After the timeout, pretend the user clicked the quit button
100         mHandler.sendMessageDelayed(
101                 mHandler.obtainMessage(TIMEOUT),
102                 DISMISS_TIMEOUT);
103     }
104 
105     @Override
onCreate(Bundle savedInstanceState)106     protected void onCreate(Bundle savedInstanceState) {
107         super.onCreate(savedInstanceState);
108         final FrameLayout frame = findViewById(android.R.id.custom);
109         final Context context = getContext();
110         LayoutInflater.from(context).inflate(
111                 com.android.internal.R.layout.app_error_dialog, frame, true);
112 
113         final boolean hasReceiver = mProc.errorReportReceiver != null;
114 
115         final TextView restart = findViewById(com.android.internal.R.id.aerr_restart);
116         restart.setOnClickListener(this);
117         restart.setVisibility(mIsRestartable ? View.VISIBLE : View.GONE);
118         final TextView report = findViewById(com.android.internal.R.id.aerr_report);
119         report.setOnClickListener(this);
120         report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
121         final TextView close = findViewById(com.android.internal.R.id.aerr_close);
122         close.setOnClickListener(this);
123         final TextView appInfo = findViewById(com.android.internal.R.id.aerr_app_info);
124         appInfo.setOnClickListener(this);
125 
126         boolean showMute = !Build.IS_USER && Settings.Global.getInt(context.getContentResolver(),
127                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0
128                 && Settings.Global.getInt(context.getContentResolver(),
129                 Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG, 0) != 0;
130         final TextView mute = findViewById(com.android.internal.R.id.aerr_mute);
131         mute.setOnClickListener(this);
132         mute.setVisibility(showMute ? View.VISIBLE : View.GONE);
133 
134         findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
135     }
136 
137     @Override
onStart()138     public void onStart() {
139         super.onStart();
140         getContext().registerReceiver(mReceiver,
141                 new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
142     }
143 
144     @Override
onStop()145     protected void onStop() {
146         super.onStop();
147         getContext().unregisterReceiver(mReceiver);
148     }
149 
150     private final Handler mHandler = new Handler() {
151         public void handleMessage(Message msg) {
152             setResult(msg.what);
153             dismiss();
154         }
155     };
156 
157     @Override
dismiss()158     public void dismiss() {
159         if (!mResult.mHasResult) {
160             // We are dismissing and the result has not been set...go ahead and set.
161             setResult(FORCE_QUIT);
162         }
163         super.dismiss();
164     }
165 
setResult(int result)166     private void setResult(int result) {
167         synchronized (mService) {
168             if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
169                 mProc.crashDialog = null;
170             }
171         }
172         mResult.set(result);
173 
174         // Make sure we don't have time timeout still hanging around.
175         mHandler.removeMessages(TIMEOUT);
176     }
177 
178     @Override
onClick(View v)179     public void onClick(View v) {
180         switch (v.getId()) {
181             case com.android.internal.R.id.aerr_restart:
182                 mHandler.obtainMessage(RESTART).sendToTarget();
183                 break;
184             case com.android.internal.R.id.aerr_report:
185                 mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget();
186                 break;
187             case com.android.internal.R.id.aerr_close:
188                 mHandler.obtainMessage(FORCE_QUIT).sendToTarget();
189                 break;
190             case com.android.internal.R.id.aerr_app_info:
191                 mHandler.obtainMessage(APP_INFO).sendToTarget();
192                 break;
193             case com.android.internal.R.id.aerr_mute:
194                 mHandler.obtainMessage(MUTE).sendToTarget();
195                 break;
196             default:
197                 break;
198         }
199     }
200 
201     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
202         @Override
203         public void onReceive(Context context, Intent intent) {
204             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
205                 cancel();
206             }
207         }
208     };
209 
210     static class Data {
211         AppErrorResult result;
212         TaskRecord task;
213         boolean repeating;
214         ProcessRecord proc;
215         boolean isRestartableForService;
216     }
217 }
218