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:
Dan Egnor
2009-12-09 16:22:32 -08:00
parent af1255dab8
commit b7f0367cec
23 changed files with 132 additions and 892 deletions

View File

@ -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 =

View File

@ -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 {

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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() + "]";
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -1,5 +0,0 @@
<html>
<body>
{@hide}
</body>
</html>

View File

@ -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));
}
/**

View File

@ -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;
}

View File

@ -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. -->

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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),

View File

@ -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;

View File

@ -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() {

View File

@ -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());
}
}

View File

@ -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();
}