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