Home | History | Annotate | Download | only in am
      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 
     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
    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
    144     public void onStart() {
    145         super.onStart();
    146         getContext().registerReceiver(mReceiver,
    147                 new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
    148     }
    149 
    150     @Override
    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
    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
    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