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