am b7f0367c: Eliminate CrashData and friends.
Merge commit 'b7f0367cec1c744aa66ef397b0244e25d507491c' into eclair-mr2-plus-aosp * commit 'b7f0367cec1c744aa66ef397b0244e25d507491c': Eliminate CrashData and friends.
This commit is contained in:
@@ -621,9 +621,15 @@ public class ActivityManager {
|
|||||||
public String longMsg;
|
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() {
|
public ProcessErrorStateInfo() {
|
||||||
}
|
}
|
||||||
@@ -640,8 +646,7 @@ public class ActivityManager {
|
|||||||
dest.writeString(tag);
|
dest.writeString(tag);
|
||||||
dest.writeString(shortMsg);
|
dest.writeString(shortMsg);
|
||||||
dest.writeString(longMsg);
|
dest.writeString(longMsg);
|
||||||
dest.writeInt(crashData == null ? -1 : crashData.length);
|
dest.writeString(stackTrace);
|
||||||
dest.writeByteArray(crashData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readFromParcel(Parcel source) {
|
public void readFromParcel(Parcel source) {
|
||||||
@@ -652,13 +657,7 @@ public class ActivityManager {
|
|||||||
tag = source.readString();
|
tag = source.readString();
|
||||||
shortMsg = source.readString();
|
shortMsg = source.readString();
|
||||||
longMsg = source.readString();
|
longMsg = source.readString();
|
||||||
int cdLen = source.readInt();
|
stackTrace = source.readString();
|
||||||
if (cdLen == -1) {
|
|
||||||
crashData = null;
|
|
||||||
} else {
|
|
||||||
crashData = new byte[cdLen];
|
|
||||||
source.readByteArray(crashData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<ProcessErrorStateInfo> CREATOR =
|
public static final Creator<ProcessErrorStateInfo> CREATOR =
|
||||||
|
|||||||
@@ -982,15 +982,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
|
|||||||
case HANDLE_APPLICATION_ERROR_TRANSACTION: {
|
case HANDLE_APPLICATION_ERROR_TRANSACTION: {
|
||||||
data.enforceInterface(IActivityManager.descriptor);
|
data.enforceInterface(IActivityManager.descriptor);
|
||||||
IBinder app = data.readStrongBinder();
|
IBinder app = data.readStrongBinder();
|
||||||
int fl = data.readInt();
|
|
||||||
String tag = data.readString();
|
String tag = data.readString();
|
||||||
String shortMsg = data.readString();
|
ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
|
||||||
String longMsg = data.readString();
|
handleApplicationError(app, tag, ci);
|
||||||
byte[] crashData = data.createByteArray();
|
|
||||||
int res = handleApplicationError(app, fl, tag, shortMsg, longMsg,
|
|
||||||
crashData);
|
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
reply.writeInt(res);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2342,25 +2337,19 @@ class ActivityManagerProxy implements IActivityManager
|
|||||||
/* this base class version is never called */
|
/* this base class version is never called */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public int handleApplicationError(IBinder app, int flags,
|
public void handleApplicationError(IBinder app, String tag,
|
||||||
String tag, String shortMsg, String longMsg,
|
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
|
||||||
byte[] crashData) throws RemoteException
|
|
||||||
{
|
{
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
Parcel reply = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
||||||
data.writeInterfaceToken(IActivityManager.descriptor);
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
data.writeStrongBinder(app);
|
data.writeStrongBinder(app);
|
||||||
data.writeInt(flags);
|
|
||||||
data.writeString(tag);
|
data.writeString(tag);
|
||||||
data.writeString(shortMsg);
|
crashInfo.writeToParcel(data, 0);
|
||||||
data.writeString(longMsg);
|
|
||||||
data.writeByteArray(crashData);
|
|
||||||
mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0);
|
mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0);
|
||||||
reply.readException();
|
reply.readException();
|
||||||
int res = reply.readInt();
|
|
||||||
reply.recycle();
|
reply.recycle();
|
||||||
data.recycle();
|
data.recycle();
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void signalPersistentProcesses(int sig) throws RemoteException {
|
public void signalPersistentProcesses(int sig) throws RemoteException {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package android.app;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.Printer;
|
import android.util.Printer;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes an application error.
|
* Describes an application error.
|
||||||
@@ -186,6 +188,31 @@ public class ApplicationErrorReport implements Parcelable {
|
|||||||
public CrashInfo() {
|
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.
|
* 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
|
* normal error recovery (app crash dialog) to occur, false to kill
|
||||||
* it immediately.
|
* it immediately.
|
||||||
*/
|
*/
|
||||||
boolean appCrashed(String processName, int pid, String shortMsg,
|
boolean appCrashed(String processName, int pid,
|
||||||
String longMsg, in byte[] crashData);
|
String tag, String shortMsg, String longMsg,
|
||||||
|
long timeMillis, String stackTrace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An application process is not responding. Return 0 to show the "app
|
* 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.
|
// Special low-level communication with activity manager.
|
||||||
public void startRunning(String pkg, String cls, String action,
|
public void startRunning(String pkg, String cls, String action,
|
||||||
String data) throws RemoteException;
|
String data) throws RemoteException;
|
||||||
// Returns 1 if the user wants to debug.
|
|
||||||
public int handleApplicationError(IBinder app,
|
public void handleApplicationError(IBinder app, String tag,
|
||||||
int flags, /* 1 == can debug */
|
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
|
||||||
String tag, String shortMsg, String longMsg,
|
|
||||||
byte[] crashData) throws RemoteException;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This will deliver the specified signal to all the persistent processes. Currently only
|
* This will deliver the specified signal to all the persistent processes. Currently only
|
||||||
|
|||||||
@@ -26,21 +26,6 @@ import android.os.IParentalControlCallback;
|
|||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
interface ICheckinService {
|
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. */
|
/** Reboot into the recovery system and wipe all user data. */
|
||||||
void masterClear();
|
void masterClear();
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import android.content.ContentValues;
|
|||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.server.data.CrashData;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -267,59 +266,4 @@ public final class Checkin {
|
|||||||
|
|
||||||
/** {@link SystemClock#elapsedRealtime} of the last time a crash report failed. */
|
/** {@link SystemClock#elapsedRealtime} of the last time a crash report failed. */
|
||||||
static private volatile long sLastCrashFailureRealtime = -MIN_CRASH_FAILURE_RETRY;
|
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
|
* @param tr An exception to log
|
||||||
*/
|
*/
|
||||||
public static int e(String tag, String msg, Throwable tr) {
|
public static int e(String tag, String msg, Throwable tr) {
|
||||||
int r = println(ERROR, tag, msg + '\n' + getStackTraceString(tr));
|
return println(ERROR, tag, msg + '\n' + getStackTraceString(tr));
|
||||||
RuntimeInit.reportException(tag, tr, false); // asynchronous
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
package com.android.internal.os;
|
package com.android.internal.os;
|
||||||
|
|
||||||
import android.app.ActivityManagerNative;
|
import android.app.ActivityManagerNative;
|
||||||
|
import android.app.ApplicationErrorReport;
|
||||||
import android.app.IActivityManager;
|
import android.app.IActivityManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Debug;
|
import android.os.Debug;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ICheckinService;
|
import android.os.ICheckinService;
|
||||||
@@ -25,8 +27,6 @@ import android.os.Process;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.Build;
|
|
||||||
import android.server.data.CrashData;
|
|
||||||
import android.util.Config;
|
import android.util.Config;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -309,51 +309,18 @@ public class RuntimeInit {
|
|||||||
*/
|
*/
|
||||||
public static void crash(String tag, Throwable t) {
|
public static void crash(String tag, Throwable t) {
|
||||||
if (mApplicationObject != null) {
|
if (mApplicationObject != null) {
|
||||||
byte[] crashData = null;
|
|
||||||
try {
|
try {
|
||||||
// Log exception.
|
// Log exception.
|
||||||
Log.e(TAG, Log.getStackTraceString(t));
|
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();
|
IActivityManager am = ActivityManagerNative.getDefault();
|
||||||
try {
|
am.handleApplicationError(mApplicationObject, tag,
|
||||||
int res = am.handleApplicationError(mApplicationObject,
|
new ApplicationErrorReport.CrashInfo(t));
|
||||||
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) {
|
|
||||||
}
|
|
||||||
} catch (Throwable t2) {
|
} catch (Throwable t2) {
|
||||||
try {
|
try {
|
||||||
// Log exception as a string so we don't get in an infinite loop.
|
// Log exception as a string so we don't get in an infinite loop.
|
||||||
Log.e(TAG, "Error reporting crash: "
|
Log.e(TAG, "Error reporting crash: " + Log.getStackTraceString(t2));
|
||||||
+ Log.getStackTraceString(t2));
|
|
||||||
} catch (Throwable t3) {
|
} catch (Throwable t3) {
|
||||||
// Do nothing, must be OOM so we can't format the message
|
// Do nothing, must be OOM so we can't format the message
|
||||||
}
|
}
|
||||||
@@ -366,7 +333,6 @@ public class RuntimeInit {
|
|||||||
try {
|
try {
|
||||||
Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS. System will crash.");
|
Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS. System will crash.");
|
||||||
Log.e(tag, Log.getStackTraceString(t));
|
Log.e(tag, Log.getStackTraceString(t));
|
||||||
reportException(tag, t, true); // synchronous
|
|
||||||
} catch (Throwable t2) {
|
} catch (Throwable t2) {
|
||||||
// Do nothing, must be OOM so we can't format the message
|
// Do nothing, must be OOM so we can't format the message
|
||||||
} finally {
|
} finally {
|
||||||
@@ -380,82 +346,6 @@ public class RuntimeInit {
|
|||||||
/** Counter used to prevent reentrancy in {@link #reportException}. */
|
/** Counter used to prevent reentrancy in {@link #reportException}. */
|
||||||
private static final AtomicInteger sInReportException = new AtomicInteger();
|
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
|
* Set the object identifying this application/process, for reporting VM
|
||||||
* errors.
|
* errors.
|
||||||
@@ -473,5 +363,4 @@ public class RuntimeInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static IBinder mApplicationObject;
|
private static IBinder mApplicationObject;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1844,8 +1844,6 @@
|
|||||||
<string name="report">Report</string>
|
<string name="report">Report</string>
|
||||||
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
|
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
|
||||||
<string name="wait">Wait</string>
|
<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
|
<!-- 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. -->
|
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.BluetoothA2dpService
|
||||||
android.server.BluetoothEventLoop
|
android.server.BluetoothEventLoop
|
||||||
android.server.BluetoothService
|
android.server.BluetoothService
|
||||||
android.server.data.CrashData
|
|
||||||
android.speech.IRecognitionListener$Stub
|
android.speech.IRecognitionListener$Stub
|
||||||
android.speech.IRecognitionService$Stub
|
android.speech.IRecognitionService$Stub
|
||||||
android.speech.RecognitionResult
|
android.speech.RecognitionResult
|
||||||
|
|||||||
@@ -39,16 +39,6 @@ public final class FallbackCheckinService extends ICheckinService.Stub {
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkin() {
|
|
||||||
return false; // failure, because not implemented
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reportCrashSync(byte[] crashData) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reportCrashAsync(byte[] crashData) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void masterClear() {
|
public void masterClear() {
|
||||||
if (mContext.checkCallingOrSelfPermission("android.permission.MASTER_CLEAR") !=
|
if (mContext.checkCallingOrSelfPermission("android.permission.MASTER_CLEAR") !=
|
||||||
PackageManager.PERMISSION_GRANTED) {
|
PackageManager.PERMISSION_GRANTED) {
|
||||||
|
|||||||
@@ -88,9 +88,6 @@ import android.os.SystemClock;
|
|||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.provider.Checkin;
|
import android.provider.Checkin;
|
||||||
import android.provider.Settings;
|
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.text.TextUtils;
|
||||||
import android.util.Config;
|
import android.util.Config;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
@@ -944,12 +941,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case SHOW_ERROR_MSG: {
|
case SHOW_ERROR_MSG: {
|
||||||
HashMap data = (HashMap) msg.obj;
|
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) {
|
synchronized (ActivityManagerService.this) {
|
||||||
ProcessRecord proc = (ProcessRecord)data.get("app");
|
ProcessRecord proc = (ProcessRecord)data.get("app");
|
||||||
if (proc != null && proc.crashDialog != null) {
|
if (proc != null && proc.crashDialog != null) {
|
||||||
@@ -958,11 +949,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
AppErrorResult res = (AppErrorResult) data.get("result");
|
AppErrorResult res = (AppErrorResult) data.get("result");
|
||||||
if (!mSleeping && !mShuttingDown) {
|
if (!mSleeping && !mShuttingDown) {
|
||||||
Dialog d = new AppErrorDialog(
|
Dialog d = new AppErrorDialog(mContext, res, proc);
|
||||||
mContext, res, proc,
|
|
||||||
(Integer)data.get("flags"),
|
|
||||||
(String)data.get("shortMsg"),
|
|
||||||
(String)data.get("longMsg"));
|
|
||||||
d.show();
|
d.show();
|
||||||
proc.crashDialog = d;
|
proc.crashDialog = d;
|
||||||
} else {
|
} else {
|
||||||
@@ -4706,7 +4693,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
makeAppNotRespondingLocked(app,
|
makeAppNotRespondingLocked(app,
|
||||||
activity != null ? activity.shortComponentName : null,
|
activity != null ? activity.shortComponentName : null,
|
||||||
annotation != null ? "ANR " + annotation : "ANR",
|
annotation != null ? "ANR " + annotation : "ANR",
|
||||||
info.toString(), null);
|
info.toString());
|
||||||
Message msg = Message.obtain();
|
Message msg = Message.obtain();
|
||||||
HashMap map = new HashMap();
|
HashMap map = new HashMap();
|
||||||
msg.what = SHOW_NOT_RESPONDING_MSG;
|
msg.what = SHOW_NOT_RESPONDING_MSG;
|
||||||
@@ -8545,11 +8532,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean makeAppCrashingLocked(ProcessRecord app,
|
private boolean makeAppCrashingLocked(ProcessRecord app,
|
||||||
String tag, String shortMsg, String longMsg, byte[] crashData) {
|
String tag, String shortMsg, String longMsg, String stackTrace) {
|
||||||
app.crashing = true;
|
app.crashing = true;
|
||||||
app.crashingReport = generateProcessError(app,
|
app.crashingReport = generateProcessError(app,
|
||||||
ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg, crashData);
|
ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg,
|
||||||
|
stackTrace);
|
||||||
startAppProblemLocked(app);
|
startAppProblemLocked(app);
|
||||||
app.stopFreezingAllLocked();
|
app.stopFreezingAllLocked();
|
||||||
return handleAppCrashLocked(app);
|
return handleAppCrashLocked(app);
|
||||||
@@ -8622,12 +8610,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
return new ComponentName(receiverPackage, info.activityInfo.name);
|
return new ComponentName(receiverPackage, info.activityInfo.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeAppNotRespondingLocked(ProcessRecord app,
|
private void makeAppNotRespondingLocked(ProcessRecord app,
|
||||||
String tag, String shortMsg, String longMsg, byte[] crashData) {
|
String tag, String shortMsg, String longMsg) {
|
||||||
app.notResponding = true;
|
app.notResponding = true;
|
||||||
app.notRespondingReport = generateProcessError(app,
|
app.notRespondingReport = generateProcessError(app,
|
||||||
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg,
|
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg, null);
|
||||||
crashData);
|
|
||||||
startAppProblemLocked(app);
|
startAppProblemLocked(app);
|
||||||
app.stopFreezingAllLocked();
|
app.stopFreezingAllLocked();
|
||||||
}
|
}
|
||||||
@@ -8641,12 +8628,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
* @param tag The tag that was passed into handleApplicationError(). Typically the classname.
|
* @param tag The tag that was passed into handleApplicationError(). Typically the classname.
|
||||||
* @param shortMsg Short message describing the crash.
|
* @param shortMsg Short message describing the crash.
|
||||||
* @param longMsg Long 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.
|
* @return Returns a fully-formed AppErrorStateInfo record.
|
||||||
*/
|
*/
|
||||||
private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
|
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();
|
ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
|
||||||
|
|
||||||
report.condition = condition;
|
report.condition = condition;
|
||||||
@@ -8656,7 +8643,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
report.tag = tag;
|
report.tag = tag;
|
||||||
report.shortMsg = shortMsg;
|
report.shortMsg = shortMsg;
|
||||||
report.longMsg = longMsg;
|
report.longMsg = longMsg;
|
||||||
report.crashData = crashData;
|
report.stackTrace = stackTrace;
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
}
|
}
|
||||||
@@ -8687,7 +8674,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean handleAppCrashLocked(ProcessRecord app) {
|
private boolean handleAppCrashLocked(ProcessRecord app) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
Long crashTime = mProcessCrashTimes.get(app.info.processName,
|
Long crashTime = mProcessCrashTimes.get(app.info.processName,
|
||||||
@@ -8770,10 +8757,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int handleApplicationError(IBinder app, int flags,
|
public void handleApplicationError(IBinder app, String tag,
|
||||||
String tag, String shortMsg, String longMsg, byte[] crashData) {
|
ApplicationErrorReport.CrashInfo crashInfo) {
|
||||||
AppErrorResult result = new AppErrorResult();
|
AppErrorResult result = new AppErrorResult();
|
||||||
ProcessRecord r = null;
|
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) {
|
synchronized (this) {
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
|
for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
|
||||||
@@ -8800,12 +8797,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
try {
|
try {
|
||||||
String name = r != null ? r.processName : null;
|
String name = r != null ? r.processName : null;
|
||||||
int pid = r != null ? r.pid : Binder.getCallingPid();
|
int pid = r != null ? r.pid : Binder.getCallingPid();
|
||||||
if (!mController.appCrashed(name, pid,
|
if (!mController.appCrashed(name, pid, tag,
|
||||||
shortMsg, longMsg, crashData)) {
|
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
|
||||||
Log.w(TAG, "Force-killing crashed app " + name
|
Log.w(TAG, "Force-killing crashed app " + name
|
||||||
+ " at watcher's request");
|
+ " at watcher's request");
|
||||||
Process.killProcess(pid);
|
Process.killProcess(pid);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
mController = null;
|
mController = null;
|
||||||
@@ -8825,12 +8822,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
info.putString("longMsg", longMsg);
|
info.putString("longMsg", longMsg);
|
||||||
finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
|
finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
|
||||||
Binder.restoreCallingIdentity(origId);
|
Binder.restoreCallingIdentity(origId);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, crashData)) {
|
if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, stackTrace)) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Some application object " + app + " tag " + tag
|
Log.w(TAG, "Some application object " + app + " tag " + tag
|
||||||
@@ -8838,7 +8835,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
Log.w(TAG, "ShortMsg:" + shortMsg);
|
Log.w(TAG, "ShortMsg:" + shortMsg);
|
||||||
Log.w(TAG, "LongMsg:" + longMsg);
|
Log.w(TAG, "LongMsg:" + longMsg);
|
||||||
Binder.restoreCallingIdentity(origId);
|
Binder.restoreCallingIdentity(origId);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message msg = Message.obtain();
|
Message msg = Message.obtain();
|
||||||
@@ -8846,13 +8843,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
HashMap data = new HashMap();
|
HashMap data = new HashMap();
|
||||||
data.put("result", result);
|
data.put("result", result);
|
||||||
data.put("app", r);
|
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;
|
msg.obj = data;
|
||||||
mHandler.sendMessage(msg);
|
mHandler.sendMessage(msg);
|
||||||
|
|
||||||
@@ -8868,8 +8858,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
SystemClock.uptimeMillis());
|
SystemClock.uptimeMillis());
|
||||||
}
|
}
|
||||||
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
|
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
|
||||||
appErrorIntent = createAppErrorIntentLocked(r);
|
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
|
||||||
res = AppErrorDialog.FORCE_QUIT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8880,12 +8869,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
Log.w(TAG, "bug report receiver dissappeared", e);
|
Log.w(TAG, "bug report receiver dissappeared", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent createAppErrorIntentLocked(ProcessRecord r) {
|
Intent createAppErrorIntentLocked(ProcessRecord r,
|
||||||
ApplicationErrorReport report = createAppErrorReportLocked(r);
|
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
|
||||||
|
ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
|
||||||
if (report == null) {
|
if (report == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -8896,7 +8884,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) {
|
private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
|
||||||
|
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
|
||||||
if (r.errorReportReceiver == null) {
|
if (r.errorReportReceiver == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -8905,58 +8894,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
ApplicationErrorReport report = new ApplicationErrorReport();
|
||||||
ApplicationErrorReport report = new ApplicationErrorReport();
|
report.packageName = r.info.packageName;
|
||||||
report.packageName = r.info.packageName;
|
report.installerPackageName = r.errorReportReceiver.getPackageName();
|
||||||
report.installerPackageName = r.errorReportReceiver.getPackageName();
|
report.processName = r.processName;
|
||||||
report.processName = r.processName;
|
report.time = timeMillis;
|
||||||
|
|
||||||
if (r.crashing) {
|
if (r.crashing) {
|
||||||
report.type = ApplicationErrorReport.TYPE_CRASH;
|
report.type = ApplicationErrorReport.TYPE_CRASH;
|
||||||
report.crashInfo = new ApplicationErrorReport.CrashInfo();
|
report.crashInfo = crashInfo;
|
||||||
|
} else if (r.notResponding) {
|
||||||
|
report.type = ApplicationErrorReport.TYPE_ANR;
|
||||||
|
report.anrInfo = new ApplicationErrorReport.AnrInfo();
|
||||||
|
|
||||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(
|
report.anrInfo.activity = r.notRespondingReport.tag;
|
||||||
r.crashingReport.crashData);
|
report.anrInfo.cause = r.notRespondingReport.shortMsg;
|
||||||
DataInputStream dataStream = new DataInputStream(byteStream);
|
report.anrInfo.info = r.notRespondingReport.longMsg;
|
||||||
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();
|
|
||||||
} else if (r.notResponding) {
|
|
||||||
report.type = ApplicationErrorReport.TYPE_ANR;
|
|
||||||
report.anrInfo = new ApplicationErrorReport.AnrInfo();
|
|
||||||
|
|
||||||
report.anrInfo.activity = r.notRespondingReport.tag;
|
|
||||||
report.anrInfo.cause = r.notRespondingReport.shortMsg;
|
|
||||||
report.anrInfo.info = r.notRespondingReport.longMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
return report;
|
|
||||||
} catch (IOException e) {
|
|
||||||
// we don't send it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return report;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
|
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
|
||||||
@@ -11433,8 +11389,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
if (timeout != null && mLRUProcesses.contains(proc)) {
|
if (timeout != null && mLRUProcesses.contains(proc)) {
|
||||||
Log.w(TAG, "Timeout executing service: " + timeout);
|
Log.w(TAG, "Timeout executing service: " + timeout);
|
||||||
appNotRespondingLocked(proc, null, null, "Executing service "
|
appNotRespondingLocked(proc, null, null, "Executing service " + timeout.name);
|
||||||
+ timeout.name);
|
|
||||||
} else {
|
} else {
|
||||||
Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
|
Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
|
||||||
msg.obj = proc;
|
msg.obj = proc;
|
||||||
@@ -12234,8 +12189,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
appNotRespondingLocked(app, null, null,
|
appNotRespondingLocked(app, null, null, "Broadcast of " + r.intent.toString());
|
||||||
"Broadcast of " + r.intent.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPendingBroadcast == r) {
|
if (mPendingBroadcast == r) {
|
||||||
|
|||||||
@@ -33,15 +33,12 @@ class AppErrorDialog extends BaseErrorDialog {
|
|||||||
|
|
||||||
// Event 'what' codes
|
// Event 'what' codes
|
||||||
static final int FORCE_QUIT = 0;
|
static final int FORCE_QUIT = 0;
|
||||||
static final int DEBUG = 1;
|
static final int FORCE_QUIT_AND_REPORT = 1;
|
||||||
static final int FORCE_QUIT_AND_REPORT = 2;
|
|
||||||
|
|
||||||
// 5-minute timeout, then we automatically dismiss the crash dialog
|
// 5-minute timeout, then we automatically dismiss the crash dialog
|
||||||
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
|
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
|
||||||
|
|
||||||
public AppErrorDialog(Context context, AppErrorResult result,
|
public AppErrorDialog(Context context, AppErrorResult result, ProcessRecord app) {
|
||||||
ProcessRecord app, int flags,
|
|
||||||
String shortMsg, String longMsg) {
|
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
@@ -67,12 +64,6 @@ class AppErrorDialog extends BaseErrorDialog {
|
|||||||
res.getText(com.android.internal.R.string.force_close),
|
res.getText(com.android.internal.R.string.force_close),
|
||||||
mHandler.obtainMessage(FORCE_QUIT));
|
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) {
|
if (app.errorReportReceiver != null) {
|
||||||
setButton(DialogInterface.BUTTON_NEGATIVE,
|
setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||||
res.getText(com.android.internal.R.string.report),
|
res.getText(com.android.internal.R.string.report),
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class AppNotRespondingDialog extends BaseErrorDialog {
|
|||||||
ProcessRecord app = mProc;
|
ProcessRecord app = mProc;
|
||||||
|
|
||||||
if (msg.what == WAIT_AND_REPORT) {
|
if (msg.what == WAIT_AND_REPORT) {
|
||||||
appErrorIntent = mService.createAppErrorIntentLocked(app);
|
appErrorIntent = mService.createAppErrorIntentLocked(app, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.notResponding = false;
|
app.notResponding = false;
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ class BaseErrorDialog extends AlertDialog {
|
|||||||
if (b != null) {
|
if (b != null) {
|
||||||
b.setEnabled(enabled);
|
b.setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
b = (Button)findViewById(R.id.button3);
|
||||||
|
if (b != null) {
|
||||||
|
b.setEnabled(enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Handler mHandler = new Handler() {
|
private Handler mHandler = new Handler() {
|
||||||
|
|||||||
@@ -17,15 +17,14 @@
|
|||||||
package com.android.unit_tests;
|
package com.android.unit_tests;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.server.data.BuildData;
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides test cases for android.os.Build and android.server.data.BuildData,
|
* Provides test cases for android.os.Build and, in turn, many of the
|
||||||
* and, in turn, many of the system properties set by the build system.
|
* system properties set by the build system.
|
||||||
*/
|
*/
|
||||||
public class BuildTest extends TestCase {
|
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)
|
// (e.g., must be a C identifier, must be a valid filename, must not contain any spaces)
|
||||||
// add tests for them.
|
// 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.app.ActivityManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.server.data.CrashData;
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -91,25 +90,12 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
condition = "<unknown>";
|
condition = "<unknown>";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
String stackTrace = null;
|
builder.append("Process error ").append(condition).append(" ");
|
||||||
try {
|
builder.append(" ").append(entry.shortMsg);
|
||||||
if (entry.crashData != null) {
|
builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag);
|
||||||
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(" ");
|
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user