Eliminate CrashData and friends.
(CrashData was a custom-marshalled crash-info class used for a server crash reporting system I am deprecating). Use ApplicationErrorReport.CrashInfo instead to report crash details (mostly the stack trace) from RuntimeInfo to ActivityManagerService, since we're likely to need the crash information in that form anyway. Remove the (long-disabled) flags and support for the "Debug" button in the crash dialog. Further gut the ICheckinService interface by removing the crash-reporting APIs (and everything that calls them), plus the synchronous checkin() method (which has been stubbed out for a while now). A new dropbox-based crash reporting system is in the works, but not part of this change.
This commit is contained in:
@ -621,9 +621,15 @@ public class ActivityManager {
|
||||
public String longMsg;
|
||||
|
||||
/**
|
||||
* Raw data about the crash (typically a stack trace).
|
||||
* The stack trace where the error originated. May be null.
|
||||
* @pending
|
||||
*/
|
||||
public byte[] crashData;
|
||||
public String stackTrace;
|
||||
|
||||
/**
|
||||
* to be deprecated: This value will always be null.
|
||||
*/
|
||||
public byte[] crashData = null;
|
||||
|
||||
public ProcessErrorStateInfo() {
|
||||
}
|
||||
@ -640,8 +646,7 @@ public class ActivityManager {
|
||||
dest.writeString(tag);
|
||||
dest.writeString(shortMsg);
|
||||
dest.writeString(longMsg);
|
||||
dest.writeInt(crashData == null ? -1 : crashData.length);
|
||||
dest.writeByteArray(crashData);
|
||||
dest.writeString(stackTrace);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel source) {
|
||||
@ -652,13 +657,7 @@ public class ActivityManager {
|
||||
tag = source.readString();
|
||||
shortMsg = source.readString();
|
||||
longMsg = source.readString();
|
||||
int cdLen = source.readInt();
|
||||
if (cdLen == -1) {
|
||||
crashData = null;
|
||||
} else {
|
||||
crashData = new byte[cdLen];
|
||||
source.readByteArray(crashData);
|
||||
}
|
||||
stackTrace = source.readString();
|
||||
}
|
||||
|
||||
public static final Creator<ProcessErrorStateInfo> CREATOR =
|
||||
|
@ -982,15 +982,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
|
||||
case HANDLE_APPLICATION_ERROR_TRANSACTION: {
|
||||
data.enforceInterface(IActivityManager.descriptor);
|
||||
IBinder app = data.readStrongBinder();
|
||||
int fl = data.readInt();
|
||||
String tag = data.readString();
|
||||
String shortMsg = data.readString();
|
||||
String longMsg = data.readString();
|
||||
byte[] crashData = data.createByteArray();
|
||||
int res = handleApplicationError(app, fl, tag, shortMsg, longMsg,
|
||||
crashData);
|
||||
ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
|
||||
handleApplicationError(app, tag, ci);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2342,25 +2337,19 @@ class ActivityManagerProxy implements IActivityManager
|
||||
/* this base class version is never called */
|
||||
return true;
|
||||
}
|
||||
public int handleApplicationError(IBinder app, int flags,
|
||||
String tag, String shortMsg, String longMsg,
|
||||
byte[] crashData) throws RemoteException
|
||||
public void handleApplicationError(IBinder app, String tag,
|
||||
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||
data.writeStrongBinder(app);
|
||||
data.writeInt(flags);
|
||||
data.writeString(tag);
|
||||
data.writeString(shortMsg);
|
||||
data.writeString(longMsg);
|
||||
data.writeByteArray(crashData);
|
||||
crashInfo.writeToParcel(data, 0);
|
||||
mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0);
|
||||
reply.readException();
|
||||
int res = reply.readInt();
|
||||
reply.recycle();
|
||||
data.recycle();
|
||||
return res;
|
||||
}
|
||||
|
||||
public void signalPersistentProcesses(int sig) throws RemoteException {
|
||||
|
@ -19,6 +19,8 @@ package android.app;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Printer;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Describes an application error.
|
||||
@ -186,6 +188,31 @@ public class ApplicationErrorReport implements Parcelable {
|
||||
public CrashInfo() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of CrashInfo initialized from an exception.
|
||||
*/
|
||||
public CrashInfo(Throwable tr) {
|
||||
StringWriter sw = new StringWriter();
|
||||
tr.printStackTrace(new PrintWriter(sw));
|
||||
stackTrace = sw.toString();
|
||||
|
||||
// Populate fields with the "root cause" exception
|
||||
while (tr.getCause() != null) {
|
||||
tr = tr.getCause();
|
||||
String msg = tr.getMessage();
|
||||
if (msg != null && msg.length() > 0) {
|
||||
exceptionMessage = msg;
|
||||
}
|
||||
}
|
||||
|
||||
exceptionClassName = tr.getClass().getName();
|
||||
StackTraceElement trace = tr.getStackTrace()[0];
|
||||
throwFileName = trace.getFileName();
|
||||
throwClassName = trace.getClassName();
|
||||
throwMethodName = trace.getMethodName();
|
||||
throwLineNumber = trace.getLineNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of CrashInfo initialized from a Parcel.
|
||||
*/
|
||||
|
@ -43,8 +43,9 @@ interface IActivityController
|
||||
* normal error recovery (app crash dialog) to occur, false to kill
|
||||
* it immediately.
|
||||
*/
|
||||
boolean appCrashed(String processName, int pid, String shortMsg,
|
||||
String longMsg, in byte[] crashData);
|
||||
boolean appCrashed(String processName, int pid,
|
||||
String tag, String shortMsg, String longMsg,
|
||||
long timeMillis, String stackTrace);
|
||||
|
||||
/**
|
||||
* An application process is not responding. Return 0 to show the "app
|
||||
|
@ -242,11 +242,9 @@ public interface IActivityManager extends IInterface {
|
||||
// Special low-level communication with activity manager.
|
||||
public void startRunning(String pkg, String cls, String action,
|
||||
String data) throws RemoteException;
|
||||
// Returns 1 if the user wants to debug.
|
||||
public int handleApplicationError(IBinder app,
|
||||
int flags, /* 1 == can debug */
|
||||
String tag, String shortMsg, String longMsg,
|
||||
byte[] crashData) throws RemoteException;
|
||||
|
||||
public void handleApplicationError(IBinder app, String tag,
|
||||
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
|
||||
|
||||
/*
|
||||
* This will deliver the specified signal to all the persistent processes. Currently only
|
||||
|
@ -26,21 +26,6 @@ import android.os.IParentalControlCallback;
|
||||
* {@hide}
|
||||
*/
|
||||
interface ICheckinService {
|
||||
/** Synchronously attempt a checkin with the server, return true
|
||||
* on success.
|
||||
* @throws IllegalStateException whenever an error occurs. The
|
||||
* cause of the exception will be the real exception:
|
||||
* IOException for network errors, JSONException for invalid
|
||||
* server responses, etc.
|
||||
*/
|
||||
boolean checkin();
|
||||
|
||||
/** Direct submission of crash data; returns after writing the crash. */
|
||||
void reportCrashSync(in byte[] crashData);
|
||||
|
||||
/** Asynchronous "fire and forget" version of crash reporting. */
|
||||
oneway void reportCrashAsync(in byte[] crashData);
|
||||
|
||||
/** Reboot into the recovery system and wipe all user data. */
|
||||
void masterClear();
|
||||
|
||||
|
@ -23,7 +23,6 @@ import android.content.ContentValues;
|
||||
import android.database.SQLException;
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
import android.server.data.CrashData;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -267,59 +266,4 @@ public final class Checkin {
|
||||
|
||||
/** {@link SystemClock#elapsedRealtime} of the last time a crash report failed. */
|
||||
static private volatile long sLastCrashFailureRealtime = -MIN_CRASH_FAILURE_RETRY;
|
||||
|
||||
/**
|
||||
* Helper function to report a crash.
|
||||
*
|
||||
* @param resolver from {@link android.content.Context#getContentResolver}
|
||||
* @param crash data from {@link android.server.data.CrashData}
|
||||
* @return URI of the crash report that was added
|
||||
*/
|
||||
static public Uri reportCrash(ContentResolver resolver, byte[] crash) {
|
||||
try {
|
||||
// If we are in a situation where crash reports fail (such as a full disk),
|
||||
// it's important that we don't get into a loop trying to report failures.
|
||||
// So discard all crash reports for a few seconds after reporting fails.
|
||||
long realtime = SystemClock.elapsedRealtime();
|
||||
if (realtime - sLastCrashFailureRealtime < MIN_CRASH_FAILURE_RETRY) {
|
||||
Log.e(TAG, "Crash logging skipped, too soon after logging failure");
|
||||
return null;
|
||||
}
|
||||
|
||||
// HACK: we don't support BLOB values, so base64 encode it.
|
||||
byte[] encoded = Base64.encodeBase64(crash);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Crashes.DATA, new String(encoded));
|
||||
Uri uri = resolver.insert(Crashes.CONTENT_URI, values);
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Error reporting crash");
|
||||
sLastCrashFailureRealtime = SystemClock.elapsedRealtime();
|
||||
}
|
||||
return uri;
|
||||
} catch (Throwable t) {
|
||||
// To avoid an infinite crash-reporting loop, swallow all errors and exceptions.
|
||||
Log.e(TAG, "Error reporting crash: " + t);
|
||||
sLastCrashFailureRealtime = SystemClock.elapsedRealtime();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a crash in CrashData format.
|
||||
*
|
||||
* @param resolver from {@link android.content.Context#getContentResolver}
|
||||
* @param crash data to report
|
||||
* @return URI of the crash report that was added
|
||||
*/
|
||||
static public Uri reportCrash(ContentResolver resolver, CrashData crash) {
|
||||
try {
|
||||
ByteArrayOutputStream data = new ByteArrayOutputStream();
|
||||
crash.write(new DataOutputStream(data));
|
||||
return reportCrash(resolver, data.toByteArray());
|
||||
} catch (Throwable t) {
|
||||
// Swallow all errors and exceptions when writing crash report
|
||||
Log.e(TAG, "Error writing crash: " + t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.server.data;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.android.internal.util.Objects.nonNull;
|
||||
|
||||
/**
|
||||
* Build data transfer object. Keep in sync. with the server side version.
|
||||
*/
|
||||
public class BuildData {
|
||||
|
||||
/** The version of the data returned by write() and understood by the constructor. */
|
||||
private static final int VERSION = 0;
|
||||
|
||||
private final String fingerprint;
|
||||
private final String incrementalVersion;
|
||||
private final long time; // in *seconds* since the epoch (not msec!)
|
||||
|
||||
public BuildData() {
|
||||
this.fingerprint = "android:" + Build.FINGERPRINT;
|
||||
this.incrementalVersion = Build.VERSION.INCREMENTAL;
|
||||
this.time = Build.TIME / 1000; // msec -> sec
|
||||
}
|
||||
|
||||
public BuildData(String fingerprint, String incrementalVersion, long time) {
|
||||
this.fingerprint = nonNull(fingerprint);
|
||||
this.incrementalVersion = incrementalVersion;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
/*package*/ BuildData(DataInput in) throws IOException {
|
||||
int dataVersion = in.readInt();
|
||||
if (dataVersion != VERSION) {
|
||||
throw new IOException("Expected " + VERSION + ". Got: " + dataVersion);
|
||||
}
|
||||
|
||||
this.fingerprint = in.readUTF();
|
||||
this.incrementalVersion = Long.toString(in.readLong());
|
||||
this.time = in.readLong();
|
||||
}
|
||||
|
||||
/*package*/ void write(DataOutput out) throws IOException {
|
||||
out.writeInt(VERSION);
|
||||
out.writeUTF(fingerprint);
|
||||
|
||||
// TODO: change the format/version to expect a string for this field.
|
||||
// Version 0, still used by the server side, expects a long.
|
||||
long changelist;
|
||||
try {
|
||||
changelist = Long.parseLong(incrementalVersion);
|
||||
} catch (NumberFormatException ex) {
|
||||
changelist = -1;
|
||||
}
|
||||
out.writeLong(changelist);
|
||||
out.writeLong(time);
|
||||
}
|
||||
|
||||
public String getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
public String getIncrementalVersion() {
|
||||
return incrementalVersion;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.server.data;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.android.internal.util.Objects.nonNull;
|
||||
|
||||
/**
|
||||
* Crash data transfer object. Keep in sync. with the server side version.
|
||||
*/
|
||||
public class CrashData {
|
||||
|
||||
final String id;
|
||||
final String activity;
|
||||
final long time;
|
||||
final BuildData buildData;
|
||||
final ThrowableData throwableData;
|
||||
final byte[] state;
|
||||
|
||||
public CrashData(String id, String activity, BuildData buildData,
|
||||
ThrowableData throwableData) {
|
||||
this.id = nonNull(id);
|
||||
this.activity = nonNull(activity);
|
||||
this.buildData = nonNull(buildData);
|
||||
this.throwableData = nonNull(throwableData);
|
||||
this.time = System.currentTimeMillis();
|
||||
this.state = null;
|
||||
}
|
||||
|
||||
public CrashData(String id, String activity, BuildData buildData,
|
||||
ThrowableData throwableData, byte[] state) {
|
||||
this.id = nonNull(id);
|
||||
this.activity = nonNull(activity);
|
||||
this.buildData = nonNull(buildData);
|
||||
this.throwableData = nonNull(throwableData);
|
||||
this.time = System.currentTimeMillis();
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public CrashData(DataInput in) throws IOException {
|
||||
int dataVersion = in.readInt();
|
||||
if (dataVersion != 0 && dataVersion != 1) {
|
||||
throw new IOException("Expected 0 or 1. Got: " + dataVersion);
|
||||
}
|
||||
|
||||
this.id = in.readUTF();
|
||||
this.activity = in.readUTF();
|
||||
this.time = in.readLong();
|
||||
this.buildData = new BuildData(in);
|
||||
this.throwableData = new ThrowableData(in);
|
||||
if (dataVersion == 1) {
|
||||
int len = in.readInt();
|
||||
if (len == 0) {
|
||||
this.state = null;
|
||||
} else {
|
||||
this.state = new byte[len];
|
||||
in.readFully(this.state, 0, len);
|
||||
}
|
||||
} else {
|
||||
this.state = null;
|
||||
}
|
||||
}
|
||||
|
||||
public CrashData(String tag, Throwable throwable) {
|
||||
id = "";
|
||||
activity = tag;
|
||||
buildData = new BuildData();
|
||||
throwableData = new ThrowableData(throwable);
|
||||
time = System.currentTimeMillis();
|
||||
state = null;
|
||||
}
|
||||
|
||||
public void write(DataOutput out) throws IOException {
|
||||
// version
|
||||
if (this.state == null) {
|
||||
out.writeInt(0);
|
||||
} else {
|
||||
out.writeInt(1);
|
||||
}
|
||||
|
||||
out.writeUTF(this.id);
|
||||
out.writeUTF(this.activity);
|
||||
out.writeLong(this.time);
|
||||
buildData.write(out);
|
||||
throwableData.write(out);
|
||||
if (this.state != null) {
|
||||
out.writeInt(this.state.length);
|
||||
out.write(this.state, 0, this.state.length);
|
||||
}
|
||||
}
|
||||
|
||||
public BuildData getBuildData() {
|
||||
return buildData;
|
||||
}
|
||||
|
||||
public ThrowableData getThrowableData() {
|
||||
return throwableData;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public byte[] getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a brief description of this CrashData record. The details of the
|
||||
* representation are subject to change.
|
||||
*
|
||||
* @return Returns a String representing the contents of the object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[CrashData: id=" + id + " activity=" + activity + " time=" + time +
|
||||
" buildData=" + buildData.toString() +
|
||||
" throwableData=" + throwableData.toString() + "]";
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.server.data;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Stack trace element data transfer object. Keep in sync. with the server side
|
||||
* version.
|
||||
*/
|
||||
public class StackTraceElementData {
|
||||
|
||||
final String className;
|
||||
final String fileName;
|
||||
final String methodName;
|
||||
final int lineNumber;
|
||||
|
||||
public StackTraceElementData(StackTraceElement element) {
|
||||
this.className = element.getClassName();
|
||||
|
||||
String fileName = element.getFileName();
|
||||
this.fileName = fileName == null ? "[unknown source]" : fileName;
|
||||
|
||||
this.methodName = element.getMethodName();
|
||||
this.lineNumber = element.getLineNumber();
|
||||
}
|
||||
|
||||
public StackTraceElementData(DataInput in) throws IOException {
|
||||
int dataVersion = in.readInt();
|
||||
if (dataVersion != 0) {
|
||||
throw new IOException("Expected 0. Got: " + dataVersion);
|
||||
}
|
||||
|
||||
this.className = in.readUTF();
|
||||
this.fileName = in.readUTF();
|
||||
this.methodName = in.readUTF();
|
||||
this.lineNumber = in.readInt();
|
||||
}
|
||||
|
||||
void write(DataOutput out) throws IOException {
|
||||
out.writeInt(0); // version
|
||||
|
||||
out.writeUTF(className);
|
||||
out.writeUTF(fileName);
|
||||
out.writeUTF(methodName);
|
||||
out.writeInt(lineNumber);
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.server.data;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Throwable data transfer object. Keep in sync. with the server side version.
|
||||
*/
|
||||
public class ThrowableData {
|
||||
|
||||
final String message;
|
||||
final String type;
|
||||
final StackTraceElementData[] stackTrace;
|
||||
final ThrowableData cause;
|
||||
|
||||
public ThrowableData(Throwable throwable) {
|
||||
this.type = throwable.getClass().getName();
|
||||
String message = throwable.getMessage();
|
||||
this.message = message == null ? "" : message;
|
||||
|
||||
StackTraceElement[] elements = throwable.getStackTrace();
|
||||
this.stackTrace = new StackTraceElementData[elements.length];
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
this.stackTrace[i] = new StackTraceElementData(elements[i]);
|
||||
}
|
||||
|
||||
Throwable cause = throwable.getCause();
|
||||
this.cause = cause == null ? null : new ThrowableData(cause);
|
||||
}
|
||||
|
||||
public ThrowableData(DataInput in) throws IOException {
|
||||
int dataVersion = in.readInt();
|
||||
if (dataVersion != 0) {
|
||||
throw new IOException("Expected 0. Got: " + dataVersion);
|
||||
}
|
||||
|
||||
this.message = in.readUTF();
|
||||
this.type = in.readUTF();
|
||||
|
||||
int count = in.readInt();
|
||||
this.stackTrace = new StackTraceElementData[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
this.stackTrace[i] = new StackTraceElementData(in);
|
||||
}
|
||||
|
||||
this.cause = in.readBoolean() ? new ThrowableData(in) : null;
|
||||
}
|
||||
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeInt(0); // version
|
||||
|
||||
out.writeUTF(message);
|
||||
out.writeUTF(type);
|
||||
|
||||
out.writeInt(stackTrace.length);
|
||||
for (StackTraceElementData elementData : stackTrace) {
|
||||
elementData.write(out);
|
||||
}
|
||||
|
||||
out.writeBoolean(cause != null);
|
||||
if (cause != null) {
|
||||
cause.write(out);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public StackTraceElementData[] getStackTrace() {
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
public ThrowableData getCause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return toString(null);
|
||||
}
|
||||
|
||||
public String toString(String prefix) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
append(prefix, builder, this);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static void append(String prefix, StringBuilder builder,
|
||||
ThrowableData throwableData) {
|
||||
if (prefix != null) builder.append(prefix);
|
||||
builder.append(throwableData.getType())
|
||||
.append(": ")
|
||||
.append(throwableData.getMessage())
|
||||
.append('\n');
|
||||
for (StackTraceElementData element : throwableData.getStackTrace()) {
|
||||
if (prefix != null ) builder.append(prefix);
|
||||
builder.append(" at ")
|
||||
.append(element.getClassName())
|
||||
.append('.')
|
||||
.append(element.getMethodName())
|
||||
.append("(")
|
||||
.append(element.getFileName())
|
||||
.append(':')
|
||||
.append(element.getLineNumber())
|
||||
.append(")\n");
|
||||
|
||||
}
|
||||
|
||||
ThrowableData cause = throwableData.getCause();
|
||||
if (cause != null) {
|
||||
if (prefix != null ) builder.append(prefix);
|
||||
builder.append("Caused by: ");
|
||||
append(prefix, builder, cause);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
{@hide}
|
||||
</body>
|
||||
</html>
|
@ -216,9 +216,7 @@ public final class Log {
|
||||
* @param tr An exception to log
|
||||
*/
|
||||
public static int e(String tag, String msg, Throwable tr) {
|
||||
int r = println(ERROR, tag, msg + '\n' + getStackTraceString(tr));
|
||||
RuntimeInit.reportException(tag, tr, false); // asynchronous
|
||||
return r;
|
||||
return println(ERROR, tag, msg + '\n' + getStackTraceString(tr));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,9 @@
|
||||
package com.android.internal.os;
|
||||
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.ApplicationErrorReport;
|
||||
import android.app.IActivityManager;
|
||||
import android.os.Build;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.ICheckinService;
|
||||
@ -25,8 +27,6 @@ import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Build;
|
||||
import android.server.data.CrashData;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
@ -309,51 +309,18 @@ public class RuntimeInit {
|
||||
*/
|
||||
public static void crash(String tag, Throwable t) {
|
||||
if (mApplicationObject != null) {
|
||||
byte[] crashData = null;
|
||||
try {
|
||||
// Log exception.
|
||||
Log.e(TAG, Log.getStackTraceString(t));
|
||||
crashData = marshallException(tag, t);
|
||||
if (crashData == null) {
|
||||
throw new NullPointerException("Can't marshall crash data");
|
||||
}
|
||||
} catch (Throwable t2) {
|
||||
try {
|
||||
// Log exception as a string so we don't get in an infinite loop.
|
||||
Log.e(TAG, "Error reporting crash: "
|
||||
+ Log.getStackTraceString(t2));
|
||||
} catch (Throwable t3) {
|
||||
// Do nothing, must be OOM so we can't format the message
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Display user-visible error message.
|
||||
String msg = t.getMessage();
|
||||
if (msg == null) {
|
||||
msg = t.toString();
|
||||
}
|
||||
|
||||
// Show a message to the user.
|
||||
IActivityManager am = ActivityManagerNative.getDefault();
|
||||
try {
|
||||
int res = am.handleApplicationError(mApplicationObject,
|
||||
0, tag, msg, t.toString(), crashData);
|
||||
// Is waiting for the debugger the right thing?
|
||||
// For now I have turned off the Debug button, because
|
||||
// I'm not sure what we should do if it is actually
|
||||
// selected.
|
||||
//Log.i(TAG, "Got app error result: " + res);
|
||||
if (res == 1) {
|
||||
Debug.waitForDebugger();
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
am.handleApplicationError(mApplicationObject, tag,
|
||||
new ApplicationErrorReport.CrashInfo(t));
|
||||
} catch (Throwable t2) {
|
||||
try {
|
||||
// Log exception as a string so we don't get in an infinite loop.
|
||||
Log.e(TAG, "Error reporting crash: "
|
||||
+ Log.getStackTraceString(t2));
|
||||
Log.e(TAG, "Error reporting crash: " + Log.getStackTraceString(t2));
|
||||
} catch (Throwable t3) {
|
||||
// Do nothing, must be OOM so we can't format the message
|
||||
}
|
||||
@ -366,7 +333,6 @@ public class RuntimeInit {
|
||||
try {
|
||||
Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS. System will crash.");
|
||||
Log.e(tag, Log.getStackTraceString(t));
|
||||
reportException(tag, t, true); // synchronous
|
||||
} catch (Throwable t2) {
|
||||
// Do nothing, must be OOM so we can't format the message
|
||||
} finally {
|
||||
@ -380,82 +346,6 @@ public class RuntimeInit {
|
||||
/** Counter used to prevent reentrancy in {@link #reportException}. */
|
||||
private static final AtomicInteger sInReportException = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* Report an error in the current process. The exception information will
|
||||
* be handed off to the checkin service and eventually uploaded for analysis.
|
||||
* This is expensive! Only use this when the exception indicates a programming
|
||||
* error ("should not happen").
|
||||
*
|
||||
* @param tag to use when logging the error
|
||||
* @param t exception that was generated by the error
|
||||
* @param sync true to wait for the report, false to "fire and forget"
|
||||
*/
|
||||
public static void reportException(String tag, Throwable t, boolean sync) {
|
||||
if (!initialized) {
|
||||
// Exceptions during, eg, zygote cannot use this mechanism
|
||||
return;
|
||||
}
|
||||
|
||||
// It's important to prevent an infinite crash-reporting loop:
|
||||
// while this function is running, don't let it be called again.
|
||||
int reenter = sInReportException.getAndIncrement();
|
||||
if (reenter != 0) {
|
||||
sInReportException.decrementAndGet();
|
||||
Log.e(TAG, "Crash logging skipped, already logging another crash");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Enable callers to specify a level (i.e. warn or error).
|
||||
try {
|
||||
// Submit crash data to statistics service.
|
||||
byte[] crashData = marshallException(tag, t);
|
||||
ICheckinService checkin = ICheckinService.Stub.asInterface(
|
||||
ServiceManager.getService("checkin"));
|
||||
if (checkin == null) {
|
||||
Log.e(TAG, "Crash logging skipped, no checkin service");
|
||||
} else if (sync) {
|
||||
checkin.reportCrashSync(crashData);
|
||||
} else {
|
||||
checkin.reportCrashAsync(crashData);
|
||||
}
|
||||
} catch (Throwable t2) {
|
||||
// Log exception as a string so we don't get in an infinite loop.
|
||||
Log.e(TAG, "Crash logging failed: " + t2);
|
||||
} finally {
|
||||
sInReportException.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] marshallException(String tag, Throwable t) {
|
||||
// Convert crash data to bytes.
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
DataOutputStream dout = new DataOutputStream(bout);
|
||||
try {
|
||||
new CrashData(tag, t).write(dout);
|
||||
dout.close();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return bout.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replay an encoded CrashData record back into a useable CrashData record. This can be
|
||||
* helpful for providing debugging output after a process error.
|
||||
*
|
||||
* @param crashDataBytes The byte array containing the encoded crash record
|
||||
* @return new CrashData record, or null if could not create one.
|
||||
*/
|
||||
public static CrashData unmarshallException(byte[] crashDataBytes) {
|
||||
try {
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream(crashDataBytes);
|
||||
DataInputStream din = new DataInputStream(bin);
|
||||
return new CrashData(din);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object identifying this application/process, for reporting VM
|
||||
* errors.
|
||||
@ -473,5 +363,4 @@ public class RuntimeInit {
|
||||
}
|
||||
|
||||
private static IBinder mApplicationObject;
|
||||
|
||||
}
|
||||
|
@ -1844,8 +1844,6 @@
|
||||
<string name="report">Report</string>
|
||||
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
|
||||
<string name="wait">Wait</string>
|
||||
<!-- Button allowing a developer to connect a debugger to an application that is not responding. -->
|
||||
<string name="debug">Debug</string>
|
||||
|
||||
<!-- Displayed in the title of the chooser for things to do with text that
|
||||
is to be sent to another application. For example, I can send text through SMS or IM. A dialog with those choices would be shown, and this would be the title. -->
|
||||
|
@ -637,7 +637,6 @@ android.security.Sha1MessageDigest
|
||||
android.server.BluetoothA2dpService
|
||||
android.server.BluetoothEventLoop
|
||||
android.server.BluetoothService
|
||||
android.server.data.CrashData
|
||||
android.speech.IRecognitionListener$Stub
|
||||
android.speech.IRecognitionService$Stub
|
||||
android.speech.RecognitionResult
|
||||
|
@ -39,16 +39,6 @@ public final class FallbackCheckinService extends ICheckinService.Stub {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public boolean checkin() {
|
||||
return false; // failure, because not implemented
|
||||
}
|
||||
|
||||
public void reportCrashSync(byte[] crashData) {
|
||||
}
|
||||
|
||||
public void reportCrashAsync(byte[] crashData) {
|
||||
}
|
||||
|
||||
public void masterClear() {
|
||||
if (mContext.checkCallingOrSelfPermission("android.permission.MASTER_CLEAR") !=
|
||||
PackageManager.PERMISSION_GRANTED) {
|
||||
|
@ -88,9 +88,6 @@ import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Checkin;
|
||||
import android.provider.Settings;
|
||||
import android.server.data.CrashData;
|
||||
import android.server.data.StackTraceElementData;
|
||||
import android.server.data.ThrowableData;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Config;
|
||||
import android.util.EventLog;
|
||||
@ -944,12 +941,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
switch (msg.what) {
|
||||
case SHOW_ERROR_MSG: {
|
||||
HashMap data = (HashMap) msg.obj;
|
||||
byte[] crashData = (byte[])data.get("crashData");
|
||||
if (crashData != null) {
|
||||
// This needs to be *un*synchronized to avoid deadlock.
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
Checkin.reportCrash(resolver, crashData);
|
||||
}
|
||||
synchronized (ActivityManagerService.this) {
|
||||
ProcessRecord proc = (ProcessRecord)data.get("app");
|
||||
if (proc != null && proc.crashDialog != null) {
|
||||
@ -958,11 +949,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
AppErrorResult res = (AppErrorResult) data.get("result");
|
||||
if (!mSleeping && !mShuttingDown) {
|
||||
Dialog d = new AppErrorDialog(
|
||||
mContext, res, proc,
|
||||
(Integer)data.get("flags"),
|
||||
(String)data.get("shortMsg"),
|
||||
(String)data.get("longMsg"));
|
||||
Dialog d = new AppErrorDialog(mContext, res, proc);
|
||||
d.show();
|
||||
proc.crashDialog = d;
|
||||
} else {
|
||||
@ -4706,7 +4693,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
makeAppNotRespondingLocked(app,
|
||||
activity != null ? activity.shortComponentName : null,
|
||||
annotation != null ? "ANR " + annotation : "ANR",
|
||||
info.toString(), null);
|
||||
info.toString());
|
||||
Message msg = Message.obtain();
|
||||
HashMap map = new HashMap();
|
||||
msg.what = SHOW_NOT_RESPONDING_MSG;
|
||||
@ -8544,11 +8531,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
}
|
||||
|
||||
boolean makeAppCrashingLocked(ProcessRecord app,
|
||||
String tag, String shortMsg, String longMsg, byte[] crashData) {
|
||||
private boolean makeAppCrashingLocked(ProcessRecord app,
|
||||
String tag, String shortMsg, String longMsg, String stackTrace) {
|
||||
app.crashing = true;
|
||||
app.crashingReport = generateProcessError(app,
|
||||
ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg, crashData);
|
||||
ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg,
|
||||
stackTrace);
|
||||
startAppProblemLocked(app);
|
||||
app.stopFreezingAllLocked();
|
||||
return handleAppCrashLocked(app);
|
||||
@ -8621,12 +8609,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
return new ComponentName(receiverPackage, info.activityInfo.name);
|
||||
}
|
||||
|
||||
void makeAppNotRespondingLocked(ProcessRecord app,
|
||||
String tag, String shortMsg, String longMsg, byte[] crashData) {
|
||||
private void makeAppNotRespondingLocked(ProcessRecord app,
|
||||
String tag, String shortMsg, String longMsg) {
|
||||
app.notResponding = true;
|
||||
app.notRespondingReport = generateProcessError(app,
|
||||
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg,
|
||||
crashData);
|
||||
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg, null);
|
||||
startAppProblemLocked(app);
|
||||
app.stopFreezingAllLocked();
|
||||
}
|
||||
@ -8640,12 +8627,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
* @param tag The tag that was passed into handleApplicationError(). Typically the classname.
|
||||
* @param shortMsg Short message describing the crash.
|
||||
* @param longMsg Long message describing the crash.
|
||||
* @param crashData Raw data passed into handleApplicationError(). Typically a stack trace.
|
||||
* @param stackTrace Full crash stack trace, may be null.
|
||||
*
|
||||
* @return Returns a fully-formed AppErrorStateInfo record.
|
||||
*/
|
||||
private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
|
||||
int condition, String tag, String shortMsg, String longMsg, byte[] crashData) {
|
||||
int condition, String tag, String shortMsg, String longMsg, String stackTrace) {
|
||||
ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
|
||||
|
||||
report.condition = condition;
|
||||
@ -8655,7 +8642,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
report.tag = tag;
|
||||
report.shortMsg = shortMsg;
|
||||
report.longMsg = longMsg;
|
||||
report.crashData = crashData;
|
||||
report.stackTrace = stackTrace;
|
||||
|
||||
return report;
|
||||
}
|
||||
@ -8686,7 +8673,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
}
|
||||
|
||||
boolean handleAppCrashLocked(ProcessRecord app) {
|
||||
private boolean handleAppCrashLocked(ProcessRecord app) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
|
||||
Long crashTime = mProcessCrashTimes.get(app.info.processName,
|
||||
@ -8769,10 +8756,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
}
|
||||
|
||||
public int handleApplicationError(IBinder app, int flags,
|
||||
String tag, String shortMsg, String longMsg, byte[] crashData) {
|
||||
public void handleApplicationError(IBinder app, String tag,
|
||||
ApplicationErrorReport.CrashInfo crashInfo) {
|
||||
AppErrorResult result = new AppErrorResult();
|
||||
ProcessRecord r = null;
|
||||
long timeMillis = System.currentTimeMillis();
|
||||
String shortMsg = crashInfo.exceptionClassName;
|
||||
String longMsg = crashInfo.exceptionMessage;
|
||||
String stackTrace = crashInfo.stackTrace;
|
||||
if (shortMsg != null && longMsg != null) {
|
||||
longMsg = shortMsg + ": " + longMsg;
|
||||
} else if (shortMsg != null) {
|
||||
longMsg = shortMsg;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (app != null) {
|
||||
for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
|
||||
@ -8799,12 +8796,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
try {
|
||||
String name = r != null ? r.processName : null;
|
||||
int pid = r != null ? r.pid : Binder.getCallingPid();
|
||||
if (!mController.appCrashed(name, pid,
|
||||
shortMsg, longMsg, crashData)) {
|
||||
if (!mController.appCrashed(name, pid, tag,
|
||||
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
|
||||
Log.w(TAG, "Force-killing crashed app " + name
|
||||
+ " at watcher's request");
|
||||
Process.killProcess(pid);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
mController = null;
|
||||
@ -8824,12 +8821,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
info.putString("longMsg", longMsg);
|
||||
finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (r != null) {
|
||||
if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, crashData)) {
|
||||
return 0;
|
||||
if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, stackTrace)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Some application object " + app + " tag " + tag
|
||||
@ -8837,7 +8834,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
Log.w(TAG, "ShortMsg:" + shortMsg);
|
||||
Log.w(TAG, "LongMsg:" + longMsg);
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Message msg = Message.obtain();
|
||||
@ -8845,13 +8842,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
HashMap data = new HashMap();
|
||||
data.put("result", result);
|
||||
data.put("app", r);
|
||||
data.put("flags", flags);
|
||||
data.put("shortMsg", shortMsg);
|
||||
data.put("longMsg", longMsg);
|
||||
if (r != null && (r.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
// For system processes, submit crash data to the server.
|
||||
data.put("crashData", crashData);
|
||||
}
|
||||
msg.obj = data;
|
||||
mHandler.sendMessage(msg);
|
||||
|
||||
@ -8867,8 +8857,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
SystemClock.uptimeMillis());
|
||||
}
|
||||
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
|
||||
appErrorIntent = createAppErrorIntentLocked(r);
|
||||
res = AppErrorDialog.FORCE_QUIT;
|
||||
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8879,12 +8868,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
Log.w(TAG, "bug report receiver dissappeared", e);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Intent createAppErrorIntentLocked(ProcessRecord r) {
|
||||
ApplicationErrorReport report = createAppErrorReportLocked(r);
|
||||
Intent createAppErrorIntentLocked(ProcessRecord r,
|
||||
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
|
||||
ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
|
||||
if (report == null) {
|
||||
return null;
|
||||
}
|
||||
@ -8895,7 +8883,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
return result;
|
||||
}
|
||||
|
||||
ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) {
|
||||
private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
|
||||
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
|
||||
if (r.errorReportReceiver == null) {
|
||||
return null;
|
||||
}
|
||||
@ -8904,43 +8893,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ApplicationErrorReport report = new ApplicationErrorReport();
|
||||
report.packageName = r.info.packageName;
|
||||
report.installerPackageName = r.errorReportReceiver.getPackageName();
|
||||
report.processName = r.processName;
|
||||
report.time = timeMillis;
|
||||
|
||||
if (r.crashing) {
|
||||
report.type = ApplicationErrorReport.TYPE_CRASH;
|
||||
report.crashInfo = new ApplicationErrorReport.CrashInfo();
|
||||
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(
|
||||
r.crashingReport.crashData);
|
||||
DataInputStream dataStream = new DataInputStream(byteStream);
|
||||
CrashData crashData = new CrashData(dataStream);
|
||||
ThrowableData throwData = crashData.getThrowableData();
|
||||
|
||||
report.time = crashData.getTime();
|
||||
report.crashInfo.stackTrace = throwData.toString();
|
||||
|
||||
// Extract the source of the exception, useful for report
|
||||
// clustering. Also extract the "deepest" non-null exception
|
||||
// message.
|
||||
String exceptionMessage = throwData.getMessage();
|
||||
while (throwData.getCause() != null) {
|
||||
throwData = throwData.getCause();
|
||||
String msg = throwData.getMessage();
|
||||
if (msg != null && msg.length() > 0) {
|
||||
exceptionMessage = msg;
|
||||
}
|
||||
}
|
||||
StackTraceElementData trace = throwData.getStackTrace()[0];
|
||||
report.crashInfo.exceptionMessage = exceptionMessage;
|
||||
report.crashInfo.exceptionClassName = throwData.getType();
|
||||
report.crashInfo.throwFileName = trace.getFileName();
|
||||
report.crashInfo.throwClassName = trace.getClassName();
|
||||
report.crashInfo.throwMethodName = trace.getMethodName();
|
||||
report.crashInfo.throwLineNumber = trace.getLineNumber();
|
||||
report.crashInfo = crashInfo;
|
||||
} else if (r.notResponding) {
|
||||
report.type = ApplicationErrorReport.TYPE_ANR;
|
||||
report.anrInfo = new ApplicationErrorReport.AnrInfo();
|
||||
@ -8951,11 +8912,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
|
||||
return report;
|
||||
} catch (IOException e) {
|
||||
// we don't send it
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
|
||||
@ -11432,8 +11388,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
if (timeout != null && mLRUProcesses.contains(proc)) {
|
||||
Log.w(TAG, "Timeout executing service: " + timeout);
|
||||
appNotRespondingLocked(proc, null, null, "Executing service "
|
||||
+ timeout.name);
|
||||
appNotRespondingLocked(proc, null, null, "Executing service " + timeout.name);
|
||||
} else {
|
||||
Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
|
||||
msg.obj = proc;
|
||||
@ -12233,8 +12188,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
|
||||
if (app != null) {
|
||||
appNotRespondingLocked(app, null, null,
|
||||
"Broadcast of " + r.intent.toString());
|
||||
appNotRespondingLocked(app, null, null, "Broadcast of " + r.intent.toString());
|
||||
}
|
||||
|
||||
if (mPendingBroadcast == r) {
|
||||
|
@ -33,15 +33,12 @@ class AppErrorDialog extends BaseErrorDialog {
|
||||
|
||||
// Event 'what' codes
|
||||
static final int FORCE_QUIT = 0;
|
||||
static final int DEBUG = 1;
|
||||
static final int FORCE_QUIT_AND_REPORT = 2;
|
||||
static final int FORCE_QUIT_AND_REPORT = 1;
|
||||
|
||||
// 5-minute timeout, then we automatically dismiss the crash dialog
|
||||
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
|
||||
|
||||
public AppErrorDialog(Context context, AppErrorResult result,
|
||||
ProcessRecord app, int flags,
|
||||
String shortMsg, String longMsg) {
|
||||
public AppErrorDialog(Context context, AppErrorResult result, ProcessRecord app) {
|
||||
super(context);
|
||||
|
||||
Resources res = context.getResources();
|
||||
@ -67,12 +64,6 @@ class AppErrorDialog extends BaseErrorDialog {
|
||||
res.getText(com.android.internal.R.string.force_close),
|
||||
mHandler.obtainMessage(FORCE_QUIT));
|
||||
|
||||
if ((flags&1) != 0) {
|
||||
setButton(DialogInterface.BUTTON_NEUTRAL,
|
||||
res.getText(com.android.internal.R.string.debug),
|
||||
mHandler.obtainMessage(DEBUG));
|
||||
}
|
||||
|
||||
if (app.errorReportReceiver != null) {
|
||||
setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||
res.getText(com.android.internal.R.string.report),
|
||||
|
@ -114,7 +114,7 @@ class AppNotRespondingDialog extends BaseErrorDialog {
|
||||
ProcessRecord app = mProc;
|
||||
|
||||
if (msg.what == WAIT_AND_REPORT) {
|
||||
appErrorIntent = mService.createAppErrorIntentLocked(app);
|
||||
appErrorIntent = mService.createAppErrorIntentLocked(app, 0, null);
|
||||
}
|
||||
|
||||
app.notResponding = false;
|
||||
|
@ -61,6 +61,10 @@ class BaseErrorDialog extends AlertDialog {
|
||||
if (b != null) {
|
||||
b.setEnabled(enabled);
|
||||
}
|
||||
b = (Button)findViewById(R.id.button3);
|
||||
if (b != null) {
|
||||
b.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private Handler mHandler = new Handler() {
|
||||
|
@ -17,15 +17,14 @@
|
||||
package com.android.unit_tests;
|
||||
|
||||
import android.os.Build;
|
||||
import android.server.data.BuildData;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Provides test cases for android.os.Build and android.server.data.BuildData,
|
||||
* and, in turn, many of the system properties set by the build system.
|
||||
* Provides test cases for android.os.Build and, in turn, many of the
|
||||
* system properties set by the build system.
|
||||
*/
|
||||
public class BuildTest extends TestCase {
|
||||
|
||||
@ -74,58 +73,4 @@ public class BuildTest extends TestCase {
|
||||
// (e.g., must be a C identifier, must be a valid filename, must not contain any spaces)
|
||||
// add tests for them.
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that android.server.data.BuildData behaves as expected.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testBuildData() throws Exception {
|
||||
BuildData bd;
|
||||
|
||||
/*
|
||||
* Default constructor
|
||||
*/
|
||||
bd = new BuildData();
|
||||
assertNotEmpty(bd.getFingerprint());
|
||||
assertNotEmpty(bd.getIncrementalVersion());
|
||||
Assert.assertTrue(bd.getTime() > 0);
|
||||
|
||||
/*
|
||||
* Explicit constructor
|
||||
*/
|
||||
final String FINGERPRINT = "fingerprint";
|
||||
final String INCREMENTAL_VERSION = "74321"; // a valid long, for the serialization test
|
||||
final long TIME = 12345;
|
||||
bd = new BuildData(FINGERPRINT, INCREMENTAL_VERSION, TIME);
|
||||
Assert.assertEquals(FINGERPRINT, bd.getFingerprint());
|
||||
Assert.assertEquals(INCREMENTAL_VERSION, bd.getIncrementalVersion());
|
||||
Assert.assertTrue(bd.getTime() == TIME);
|
||||
|
||||
// The serialization methods are package-private.
|
||||
//
|
||||
// import java.io.ByteArrayInputStream;
|
||||
// import java.io.ByteArrayOutputStream;
|
||||
// import java.io.DataInputStream;
|
||||
// import java.io.DataOutputStream;
|
||||
//
|
||||
// /*
|
||||
// * Serialization
|
||||
// */
|
||||
// ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// bd.write(new DataOutputStream(out));
|
||||
// Assert.assertTrue(out.size() > 0);
|
||||
//
|
||||
// /*
|
||||
// * Deserialization
|
||||
// *
|
||||
// * The current version of BuildData converts the incremental version to
|
||||
// * and from a long when serializing/deserializing. Future versions should
|
||||
// * treat it as a string.
|
||||
// */
|
||||
// BuildData bd2 =
|
||||
// new BuildData(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
|
||||
// Assert.assertEquals(bd.getFingerprint(), bd2.getFingerprint());
|
||||
// Assert.assertEquals(bd.getIncrementalVersion(), bd2.getIncrementalVersion());
|
||||
// Assert.assertTrue(bd.getTime() == bd2.getTime());
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import com.android.internal.os.RuntimeInit;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.server.data.CrashData;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
@ -94,22 +93,9 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
||||
break;
|
||||
}
|
||||
|
||||
String stackTrace = null;
|
||||
try {
|
||||
if (entry.crashData != null) {
|
||||
CrashData cd = RuntimeInit.unmarshallException(entry.crashData);
|
||||
stackTrace = cd.toString();
|
||||
}
|
||||
} catch (RuntimeException e) { }
|
||||
if (stackTrace == null) {
|
||||
stackTrace = "<no stack trace>";
|
||||
}
|
||||
|
||||
final String entryReport = "Process error " + condition + " " + entry.shortMsg +
|
||||
" detected in " + entry.processName + " " + entry.tag +
|
||||
". \n" + stackTrace;
|
||||
|
||||
builder.append(entryReport).append(" ");
|
||||
builder.append("Process error ").append(condition).append(" ");
|
||||
builder.append(" ").append(entry.shortMsg);
|
||||
builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
Reference in New Issue
Block a user