/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.am;
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto;
import com.android.internal.os.ProcessCpuTracker;
import com.android.server.Watchdog;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Binder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOWMEM;
import android.os.Handler;
/**
* Controls error conditions in applications.
*/
class AppErrors {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM;
private final ActivityManagerService mService;
private final Context mContext;
private ArraySet<String> mAppsNotReportingCrashes;
/**
* The last time that various processes have crashed since they were last explicitly started.
*/
private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>();
/**
* The last time that various processes have crashed (not reset even when explicitly started).
*/
private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
/**
* Set of applications that we consider to be bad, and will reject
* incoming broadcasts from (which the user has no control over).
* Processes are added to this set when they have crashed twice within
* a minimum amount of time; they are removed from it when they are
* later restarted (hopefully due to some user action). The value is the
* time it was added to the list.
*/
private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
/**
* execute close dialog runnable,add by zwj
*/
private Handler mHandler = new Handler();
AppErrors(Context context, ActivityManagerService service) {
mService = service;
mContext = context;
}
boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep,
String dumpPackage) {
if (!mProcessCrashTimes.getMap().isEmpty()) {
boolean printed = false;
final long now = SystemClock.uptimeMillis();
final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
final int processCount = pmap.size();
for (int ip = 0; ip < processCount; ip++) {
final String pname = pmap.keyAt(ip);
final SparseArray<Long> uids = pmap.valueAt(ip);
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
final ProcessRecord r = mService.mProcessNames.get(pname, puid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
if (!printed) {
if (needSep) pw.println();
needSep = true;
pw.println(" Time since processes crashed:");
printed = true;
}
pw.print(" Process "); pw.print(pname);
pw.print(" uid "); pw.print(puid);
pw.print(": last crashed ");
TimeUtils.formatDuration(now-uids.valueAt(i), pw);
pw.println(" ago");
}
}
}
if (!mBadProcesses.getMap().isEmpty()) {
boolean printed = false;
final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
final int processCount = pmap.size();
for (int ip = 0; ip < processCount; ip++) {
final String pname = pmap.keyAt(ip);
final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
final ProcessRecord r = mService.mProcessNames.get(pname, puid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
if (!printed) {
if (needSep) pw.println();
needSep = true;
pw.println(" Bad processes:");
printed = true;
}
final BadProcessInfo info = uids.valueAt(i);
pw.print(" Bad process "); pw.print(pname);
pw.print(" uid "); pw.print(puid);
pw.print(": crashed at time "); pw.println(info.time);
if (info.shortMsg != null) {
pw.print(" Short msg: "); pw.println(info.shortMsg);
}
if (info.longMsg != null) {
pw.print(" Long msg: "); pw.println(info.longMsg);
}
if (info.stack != null) {
pw.println(" Stack:");
int lastPos = 0;
for (int pos = 0; pos < info.stack.length(); pos++) {
if (info.stack.charAt(pos) == '\n') {
pw.print(" ");
pw.write(info.stack, lastPos, pos-lastPos);
pw.println();
lastPos = pos+1;
}
}
if (lastPos < info.stack.length()) {
pw.print(" ");
pw.write(info.stack, lastPos, info.stack.length()-lastPos);
pw.println();
}
}
}
}