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