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