resolved conflicts for merge of f6bb01b0 to master
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,18 +982,13 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
case SIGNAL_PERSISTENT_PROCESSES_TRANSACTION: {
|
||||
data.enforceInterface(IActivityManager.descriptor);
|
||||
int sig = data.readInt();
|
||||
@@ -2342,27 +2337,21 @@ 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 {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user