diff --git a/firebase-crashlytics/firebase-crashlytics.gradle b/firebase-crashlytics/firebase-crashlytics.gradle index 67631676ebb..84fbe6036fb 100644 --- a/firebase-crashlytics/firebase-crashlytics.gradle +++ b/firebase-crashlytics/firebase-crashlytics.gradle @@ -18,7 +18,10 @@ plugins { } firebaseLibrary { - testLab.enabled = true + testLab { + enabled = true + device 'model=flame,version=30' + } } android { diff --git a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerTest.java b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerTest.java index 6fa6a242136..06da3749b78 100644 --- a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerTest.java +++ b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerTest.java @@ -52,7 +52,6 @@ import org.mockito.ArgumentCaptor; public class CrashlyticsControllerTest extends CrashlyticsTestCase { - private static final String GOOGLE_APP_ID = "google:app:id"; private Context testContext; diff --git a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCaptureTest.java b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCaptureTest.java index c5fbd425b40..669c8359a8d 100644 --- a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCaptureTest.java +++ b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCaptureTest.java @@ -20,6 +20,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -101,6 +103,30 @@ public void testCaptureReport_containsNoDeveloperPlatformFieldsWhenUnityIsMissin assertNull(report.getSession().getApp().getDevelopmentPlatformVersion()); } + @Test + public void testCaptureAnrEvent_foregroundAnr() { + CrashlyticsReport.ApplicationExitInfo testApplicationExitInfo = makeAppExitInfo(false); + final CrashlyticsReport.Session.Event event = + dataCapture.captureAnrEventData(testApplicationExitInfo); + + assertEquals("anr", event.getType()); + assertEquals(testApplicationExitInfo, event.getApp().getExecution().getAppExitInfo()); + assertEquals(testApplicationExitInfo.getTimestamp(), event.getTimestamp()); + assertEquals(false, event.getApp().getBackground()); + } + + @Test + public void testCaptureAnrEvent_backgroundAnr() { + CrashlyticsReport.ApplicationExitInfo testApplicationExitInfo = makeAppExitInfo(true); + final CrashlyticsReport.Session.Event event = + dataCapture.captureAnrEventData(testApplicationExitInfo); + + assertEquals("anr", event.getType()); + assertEquals(testApplicationExitInfo, event.getApp().getExecution().getAppExitInfo()); + assertEquals(testApplicationExitInfo.getTimestamp(), event.getTimestamp()); + assertEquals(true, event.getApp().getBackground()); + } + @Test public void testCaptureReportSessionFields() { final String sessionId = "sessionId"; @@ -369,4 +395,21 @@ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) } }; } + + private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo(boolean isBackground) { + final int anrImportance = + isBackground + ? RunningAppProcessInfo.IMPORTANCE_CACHED + : ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; + return CrashlyticsReport.ApplicationExitInfo.builder() + .setTraceFile("trace") + .setTimestamp(1L) + .setImportance(anrImportance) + .setReasonCode(1) + .setProcessName("test") + .setPid(1) + .setPss(1L) + .setRss(1L) + .build(); + } } diff --git a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransformTest.java b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransformTest.java index 820ba750db0..6c9ec34190e 100644 --- a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransformTest.java +++ b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransformTest.java @@ -59,12 +59,12 @@ public void testReportToJsonAndBack_with_developmentPlatform_equals() throws IOE } @Test - public void testReportToJsonAndBack_with_appExitInfo_equals() throws IOException { - final CrashlyticsReport testReport = makeTestReport(false).withAppExitInfo(makeAppExitInfo()); - final String testReportJson = transform.reportToJson(testReport); - final CrashlyticsReport reifiedReport = transform.reportFromJson(testReportJson); - assertNotSame(reifiedReport, testReport); - assertEquals(reifiedReport, testReport); + public void testAnrEventToJsonAndBack_equals() throws IOException { + final CrashlyticsReport.Session.Event testEvent = makeAnrEvent(); + final String testEventJson = transform.eventToJson(testEvent); + final CrashlyticsReport.Session.Event reifiedEvent = transform.eventFromJson(testEventJson); + assertNotSame(reifiedEvent, testEvent); + assertEquals(reifiedEvent, testEvent); } @Test @@ -180,6 +180,40 @@ private static Event makeTestEvent() { .build(); } + private static Event makeAnrEvent() { + return Event.builder() + .setType("anr") + .setTimestamp(1000) + .setApp( + Session.Event.Application.builder() + .setBackground(false) + .setExecution( + Execution.builder() + .setBinaries( + ImmutableList.from( + Execution.BinaryImage.builder() + .setBaseAddress(0) + .setName("name") + .setSize(100000) + .setUuid("uuid") + .build())) + .setSignal(Signal.builder().setCode("0").setName("0").setAddress(0).build()) + .setAppExitInfo(makeAppExitInfo()) + .build()) + .setUiOrientation(1) + .build()) + .setDevice( + Session.Event.Device.builder() + .setBatteryLevel(0.5) + .setBatteryVelocity(3) + .setDiskUsed(10000000) + .setOrientation(1) + .setProximityOn(true) + .setRamUsed(10000000) + .build()) + .build(); + } + private static ImmutableList makeTestFrames() { return ImmutableList.from( Frame.builder() @@ -219,6 +253,9 @@ private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo() { .setImportance(1) .setReasonCode(1) .setProcessName("test") + .setPid(1) + .setPss(1L) + .setRss(1L) .build(); } } diff --git a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistenceTest.java b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistenceTest.java index 82acf17a4d7..3419d23681b 100644 --- a/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistenceTest.java +++ b/firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistenceTest.java @@ -669,17 +669,15 @@ public void testPersistEvent_whenSettingsChanges_keepsAppropriateNumberOfMostRec } @Test - public void testPersistAppExitInfo() throws IOException { + public void testPersistReportWithAnrEvent() throws IOException { reportPersistence = new CrashlyticsReportPersistence( folder.newFolder(), getSettingsMock(VERY_LARGE_UPPER_LIMIT, 4)); final String sessionId = "testSession"; final CrashlyticsReport testReport = makeTestReport(sessionId); - final CrashlyticsReport.Session.Event testEvent1 = makeTestEvent("anr", "reason1"); - final CrashlyticsReport.ApplicationExitInfo testAppExitInfo = makeAppExitInfo(); - + final Event testEvent = makeTestAnrEvent(); reportPersistence.persistReport(testReport); - reportPersistence.persistAppExitInfoEvent(testEvent1, sessionId, testAppExitInfo); + reportPersistence.persistEvent(testEvent, sessionId, true); final long endedAt = System.currentTimeMillis(); @@ -690,12 +688,6 @@ public void testPersistAppExitInfo() throws IOException { assertEquals(1, finalizedReports.size()); final CrashlyticsReport finalizedReport = finalizedReports.get(0).getReport(); assertEquals(1, finalizedReport.getSession().getEvents().size()); - assertEquals( - testReport - .withSessionEndFields(endedAt, true, null) - .withEvents(ImmutableList.from(testEvent1)) - .withAppExitInfo(testAppExitInfo), - finalizedReport); } private static void persistReportWithEvent( @@ -818,6 +810,40 @@ private static Event makeTestEvent(String type, String reason) { .build(); } + private static Event makeTestAnrEvent() { + return Event.builder() + .setType("anr") + .setTimestamp(1000) + .setApp( + Session.Event.Application.builder() + .setBackground(false) + .setExecution( + Execution.builder() + .setBinaries( + ImmutableList.from( + Execution.BinaryImage.builder() + .setBaseAddress(0) + .setName("name") + .setSize(100000) + .setUuid("uuid") + .build())) + .setSignal(Signal.builder().setCode("0").setName("0").setAddress(0).build()) + .setAppExitInfo(makeAppExitInfo()) + .build()) + .setUiOrientation(1) + .build()) + .setDevice( + Session.Event.Device.builder() + .setBatteryLevel(0.5) + .setBatteryVelocity(3) + .setDiskUsed(10000000) + .setOrientation(1) + .setProximityOn(true) + .setRamUsed(10000000) + .build()) + .build(); + } + private static ImmutableList makeTestFrames() { return ImmutableList.from( Frame.builder() @@ -857,6 +883,9 @@ private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo() { .setImportance(1) .setReasonCode(1) .setProcessName("test") + .setPid(1) + .setPss(1L) + .setRss(1L) .build(); } } diff --git a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCapture.java b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCapture.java index 92c37da541a..4da94dbe4e2 100644 --- a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCapture.java +++ b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCapture.java @@ -110,6 +110,20 @@ public Event captureEventData( .build(); } + public Event captureAnrEventData(CrashlyticsReport.ApplicationExitInfo applicationExitInfo) { + // This is not the orientation of the device at the time of ANR. + // It's filtered out when the backend processes it. + // TODO: Consider setting it to 0 to mark it as unknown. + final int orientation = context.getResources().getConfiguration().orientation; + + return Event.builder() + .setType("anr") + .setTimestamp(applicationExitInfo.getTimestamp()) + .setApp(populateEventApplicationData(orientation, applicationExitInfo)) + .setDevice(populateEventDeviceData(orientation)) + .build(); + } + private CrashlyticsReport.Builder buildReportData() { return CrashlyticsReport.builder() .setSdkVersion(BuildConfig.VERSION_NAME) @@ -212,6 +226,18 @@ private Event.Application populateEventApplicationData( .build(); } + private Event.Application populateEventApplicationData( + int orientation, CrashlyticsReport.ApplicationExitInfo applicationExitInfo) { + boolean isBackground = + applicationExitInfo.getImportance() != RunningAppProcessInfo.IMPORTANCE_FOREGROUND; + + return Event.Application.builder() + .setBackground(isBackground) + .setUiOrientation(orientation) + .setExecution(populateExecutionData(applicationExitInfo)) + .build(); + } + private Event.Device populateEventDeviceData(int orientation) { final BatteryState battery = BatteryState.get(context); final Float batteryLevel = battery.getBatteryLevel(); @@ -250,6 +276,15 @@ private Execution populateExecutionData( .build(); } + private Execution populateExecutionData( + CrashlyticsReport.ApplicationExitInfo applicationExitInfo) { + return Execution.builder() + .setAppExitInfo(applicationExitInfo) + .setSignal(populateSignalData()) + .setBinaries(populateBinaryImagesList()) + .build(); + } + private ImmutableList populateThreadsList( TrimmedThrowableData trimmedEvent, Thread eventThread, diff --git a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinator.java b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinator.java index 3ee08a88f02..abbc354f3a3 100644 --- a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinator.java +++ b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinator.java @@ -55,7 +55,6 @@ public class SessionReportingCoordinator implements CrashlyticsLifecycleEvents { private static final String EVENT_TYPE_CRASH = "crash"; - private static final String EVENT_TYPE_ANR = "anr"; private static final String EVENT_TYPE_LOGGED = "error"; private static final int EVENT_THREAD_IMPORTANCE = 4; private static final int MAX_CHAINED_EXCEPTION_DEPTH = 8; @@ -147,21 +146,11 @@ public void persistAppExitInfoEvent(String sessionId, ApplicationExitInfo applic return; } - // TODO: Refactor Event to only contain relevant information rather than unnecessary data like - // thread, exception etc. final CrashlyticsReport.Session.Event capturedEvent = - dataCapture.captureEventData( - new Exception("ANR"), - Thread.currentThread(), - EVENT_TYPE_ANR, - applicationExitInfo.getTimestamp(), - EVENT_THREAD_IMPORTANCE, - MAX_CHAINED_EXCEPTION_DEPTH, - false); - CrashlyticsReport.ApplicationExitInfo crashlyticsAppExitInfo = - convertApplicationExitInfo(applicationExitInfo); + dataCapture.captureAnrEventData(convertApplicationExitInfo(applicationExitInfo)); + Logger.getLogger().d("Persisting anr for session " + sessionId); - reportPersistence.persistAppExitInfoEvent(capturedEvent, sessionId, crashlyticsAppExitInfo); + reportPersistence.persistEvent(addLogsAndCustomKeysToEvent(capturedEvent), sessionId, true); } public void finalizeSessionWithNativeEvent( @@ -227,28 +216,9 @@ public Task sendReports(@NonNull Executor reportSendCompleteExecutor) { return Tasks.whenAll(sendTasks); } - private void persistEvent( - @NonNull Throwable event, - @NonNull Thread thread, - @NonNull String sessionId, - @NonNull String eventType, - long timestamp, - boolean includeAllThreads) { - - final boolean isHighPriority = eventType.equals(EVENT_TYPE_CRASH); - - final CrashlyticsReport.Session.Event capturedEvent = - dataCapture.captureEventData( - event, - thread, - eventType, - timestamp, - EVENT_THREAD_IMPORTANCE, - MAX_CHAINED_EXCEPTION_DEPTH, - includeAllThreads); - + private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent( + CrashlyticsReport.Session.Event capturedEvent) { final CrashlyticsReport.Session.Event.Builder eventBuilder = capturedEvent.toBuilder(); - final String content = logFileManager.getLogString(); if (content != null) { @@ -274,7 +244,31 @@ private void persistEvent( .build()); } - reportPersistence.persistEvent(eventBuilder.build(), sessionId, isHighPriority); + return eventBuilder.build(); + } + + private void persistEvent( + @NonNull Throwable event, + @NonNull Thread thread, + @NonNull String sessionId, + @NonNull String eventType, + long timestamp, + boolean includeAllThreads) { + + final boolean isHighPriority = eventType.equals(EVENT_TYPE_CRASH); + + final CrashlyticsReport.Session.Event capturedEvent = + dataCapture.captureEventData( + event, + thread, + eventType, + timestamp, + EVENT_THREAD_IMPORTANCE, + MAX_CHAINED_EXCEPTION_DEPTH, + includeAllThreads); + + reportPersistence.persistEvent( + addLogsAndCustomKeysToEvent(capturedEvent), sessionId, isHighPriority); } private boolean onReportSendComplete(@NonNull Task task) { @@ -328,6 +322,9 @@ private static CrashlyticsReport.ApplicationExitInfo convertApplicationExitInfo( .setProcessName(applicationExitInfo.getProcessName()) .setReasonCode(applicationExitInfo.getReason()) .setTimestamp(applicationExitInfo.getTimestamp()) + .setPid(applicationExitInfo.getPid()) + .setPss(applicationExitInfo.getPss()) + .setRss(applicationExitInfo.getRss()) .setTraceFile(traceFile) .build(); } diff --git a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReport.java b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReport.java index 0199e1c0727..9019611def6 100644 --- a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReport.java +++ b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReport.java @@ -105,9 +105,6 @@ public Type getType() { @Nullable public abstract FilesPayload getNdkPayload(); - @Nullable - public abstract ApplicationExitInfo getAppExitInfo(); - @NonNull protected abstract Builder toBuilder(); @@ -158,16 +155,6 @@ public CrashlyticsReport withNdkPayload(@NonNull FilesPayload filesPayload) { return toBuilder().setSession(null).setNdkPayload(filesPayload).build(); } - /** - * Augment an existing {@link CrashlyticsReport} with an ApplicationExitInfo - * - * @return a new {@link CrashlyticsReport} with AppExitInfo inside of it. - */ - @NonNull - public CrashlyticsReport withAppExitInfo(@NonNull ApplicationExitInfo appExitInfo) { - return toBuilder().setAppExitInfo(appExitInfo).build(); - } - /** * Augment an existing {@link CrashlyticsReport} with fields set at session end. * @@ -669,12 +656,15 @@ public static Builder builder() { return new AutoValue_CrashlyticsReport_Session_Event_Application_Execution.Builder(); } - @NonNull + @Nullable public abstract ImmutableList getThreads(); - @NonNull + @Nullable public abstract Exception getException(); + @Nullable + public abstract ApplicationExitInfo getAppExitInfo(); + @NonNull public abstract Signal getSignal(); @@ -908,6 +898,9 @@ public abstract static class Builder { @NonNull public abstract Builder setException(@NonNull Exception value); + @NonNull + public abstract Builder setAppExitInfo(@NonNull ApplicationExitInfo value); + @NonNull public abstract Builder setSignal(@NonNull Signal value); @@ -1048,6 +1041,9 @@ public static ApplicationExitInfo.Builder builder() { return new AutoValue_CrashlyticsReport_ApplicationExitInfo.Builder(); } + @NonNull + public abstract int getPid(); + @NonNull public abstract String getProcessName(); @@ -1057,6 +1053,12 @@ public static ApplicationExitInfo.Builder builder() { @NonNull public abstract int getImportance(); + @NonNull + public abstract long getPss(); + + @NonNull + public abstract long getRss(); + @NonNull public abstract long getTimestamp(); @@ -1067,6 +1069,8 @@ public static ApplicationExitInfo.Builder builder() { /** Builder for {@link ApplicationExitInfo}. */ @AutoValue.Builder public abstract static class Builder { + @NonNull + public abstract ApplicationExitInfo.Builder setPid(@NonNull int value); @NonNull public abstract ApplicationExitInfo.Builder setProcessName(@NonNull String value); @@ -1077,10 +1081,16 @@ public abstract static class Builder { @NonNull public abstract ApplicationExitInfo.Builder setImportance(@NonNull int value); + @NonNull + public abstract ApplicationExitInfo.Builder setPss(@NonNull long value); + + @NonNull + public abstract ApplicationExitInfo.Builder setRss(@NonNull long value); + @NonNull public abstract ApplicationExitInfo.Builder setTimestamp(@NonNull long value); - @Nullable + @NonNull public abstract ApplicationExitInfo.Builder setTraceFile(@Nullable String value); @NonNull @@ -1115,9 +1125,6 @@ public abstract static class Builder { @NonNull public abstract Builder setNdkPayload(FilesPayload value); - @NonNull - public abstract Builder setAppExitInfo(ApplicationExitInfo value); - @NonNull public abstract CrashlyticsReport build(); } diff --git a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransform.java b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransform.java index 0bf56796202..545c5511a69 100644 --- a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransform.java +++ b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/model/serialization/CrashlyticsReportJsonTransform.java @@ -113,9 +113,6 @@ private static CrashlyticsReport parseReport(@NonNull JsonReader jsonReader) thr case "ndkPayload": builder.setNdkPayload(parseNdkPayload(jsonReader)); break; - case "appExitInfo": - builder.setAppExitInfo(parseAppExitInfo(jsonReader)); - break; default: jsonReader.skipValue(); break; @@ -213,6 +210,9 @@ private static CrashlyticsReport.ApplicationExitInfo parseAppExitInfo( while (jsonReader.hasNext()) { String name = jsonReader.nextName(); switch (name) { + case "pid": + builder.setPid(jsonReader.nextInt()); + break; case "processName": builder.setProcessName(jsonReader.nextString()); break; @@ -222,6 +222,12 @@ private static CrashlyticsReport.ApplicationExitInfo parseAppExitInfo( case "importance": builder.setImportance(jsonReader.nextInt()); break; + case "pss": + builder.setPss(jsonReader.nextLong()); + break; + case "rss": + builder.setRss(jsonReader.nextLong()); + break; case "timestamp": builder.setTimestamp(jsonReader.nextLong()); break; @@ -492,6 +498,9 @@ private static Event.Application.Execution parseEventExecution(@NonNull JsonRead builder.setBinaries( parseArray(jsonReader, CrashlyticsReportJsonTransform::parseEventBinaryImage)); break; + case "appExitInfo": + builder.setAppExitInfo(parseAppExitInfo(jsonReader)); + break; default: jsonReader.skipValue(); break; diff --git a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistence.java b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistence.java index 4687c740d18..fad50d5f230 100644 --- a/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistence.java +++ b/firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/persistence/CrashlyticsReportPersistence.java @@ -166,21 +166,6 @@ public void persistEvent( trimEvents(sessionDirectory, maxEventsToKeep); } - /** - * Persist an ANR event and the relevant ApplicationExitInfo to the given session. - * - * @param event - * @param sessionId - * @param applicationExitInfo - */ - public void persistAppExitInfoEvent( - @NonNull CrashlyticsReport.Session.Event event, - @NonNull String sessionId, - @NonNull CrashlyticsReport.ApplicationExitInfo applicationExitInfo) { - persistEvent(event, sessionId, true); - persistApplicationExitInfo(applicationExitInfo, sessionId); - } - public void persistUserIdForSession(@NonNull String userId, @NonNull String sessionId) { final File sessionDirectory = getSessionDirectoryById(sessionId); try { @@ -349,21 +334,12 @@ private void synthesizeReport(@NonNull File sessionDirectory, long sessionEndTim Collections.sort(eventFiles); final List events = new ArrayList<>(); boolean isHighPriorityReport = false; - CrashlyticsReport.ApplicationExitInfo appExitInfo = null; for (File eventFile : eventFiles) { try { Event event = TRANSFORM.eventFromJson(readTextFile(eventFile)); events.add(event); isHighPriorityReport = isHighPriorityReport || isHighPriorityEventFile(eventFile.getName()); - - if (event.getType().equals(EVENT_TYPE_ANR)) { - try { - appExitInfo = getAppExitInfo(sessionDirectory); - } catch (IOException e) { - Logger.getLogger().d("Failed to read AppExitInfo: ", e); - } - } } catch (IOException e) { Logger.getLogger().w("Could not add event to report for " + eventFile, e); } @@ -388,13 +364,7 @@ private void synthesizeReport(@NonNull File sessionDirectory, long sessionEndTim final File reportFile = new File(sessionDirectory, REPORT_FILE_NAME); final File outputDirectory = isHighPriorityReport ? priorityReportsDirectory : reportsDirectory; synthesizeReportFile( - reportFile, - outputDirectory, - events, - sessionEndTime, - isHighPriorityReport, - userId, - appExitInfo); + reportFile, outputDirectory, events, sessionEndTime, isHighPriorityReport, userId); } private static void synthesizeNativeReportFile( @@ -420,18 +390,13 @@ private static void synthesizeReportFile( @NonNull List events, long sessionEndTime, boolean isCrashed, - @Nullable String userId, - @Nullable CrashlyticsReport.ApplicationExitInfo applicationExitInfo) { + @Nullable String userId) { try { CrashlyticsReport report = TRANSFORM .reportFromJson(readTextFile(reportFile)) .withSessionEndFields(sessionEndTime, isCrashed, userId) .withEvents(ImmutableList.from(events)); - if (applicationExitInfo != null) { - report = report.withAppExitInfo(applicationExitInfo); - } - final Session session = report.getSession(); if (session == null) { @@ -602,22 +567,4 @@ private static void recursiveDelete(@Nullable File file) { private static long convertTimestampFromSecondsToMs(long timestampSeconds) { return timestampSeconds * 1000; } - - private void persistApplicationExitInfo( - CrashlyticsReport.ApplicationExitInfo applicationExitInfo, String sessionId) { - File sessionDirectory = getSessionDirectoryById(sessionId); - - try { - String appExitInfo = TRANSFORM.appExitInfoToJson(applicationExitInfo); - writeTextFile(new File(sessionDirectory, APP_EXIT_INFO_FILE_NAME), appExitInfo); - } catch (IOException e) { - Logger.getLogger().w("Unable to write app exit info file: " + e); - } - } - - private CrashlyticsReport.ApplicationExitInfo getAppExitInfo(File sessionDirectory) - throws IOException { - File appExitInfoFile = new File(sessionDirectory, APP_EXIT_INFO_FILE_NAME); - return TRANSFORM.applicationExitInfoFromJson(readTextFile(appExitInfoFile)); - } } diff --git a/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinatorRobolectricTest.java b/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinatorRobolectricTest.java index 4a2396d8fc6..d9de7d64232 100644 --- a/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinatorRobolectricTest.java +++ b/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinatorRobolectricTest.java @@ -16,10 +16,6 @@ import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -84,18 +80,8 @@ public void testAppExitInfoEvent_persistIfAnrWithinSession() { reportingCoordinator.onBeginSession(sessionId, sessionStartTimestamp); reportingCoordinator.persistAppExitInfoEvent(sessionId, testApplicationExitInfo); - verify(dataCapture) - .captureEventData( - any(Throwable.class), - any(Thread.class), - eq("anr"), - anyLong(), - anyInt(), - anyInt(), - anyBoolean()); - verify(reportPersistence) - .persistAppExitInfoEvent( - any(), eq(sessionId), eq(convertApplicationExitInfo(testApplicationExitInfo))); + verify(dataCapture).captureAnrEventData(convertApplicationExitInfo(testApplicationExitInfo)); + verify(reportPersistence).persistEvent(any(), eq(sessionId), eq(true)); } @Test @@ -112,17 +98,8 @@ public void testAppExitInfoEvent_notPersistIfAnrBeforeSession() { reportingCoordinator.persistAppExitInfoEvent(sessionId, testApplicationExitInfo); verify(dataCapture, never()) - .captureEventData( - any(Throwable.class), - any(Thread.class), - eq("anr"), - anyLong(), - anyInt(), - anyInt(), - anyBoolean()); - verify(reportPersistence, never()) - .persistAppExitInfoEvent( - any(), eq(sessionId), eq(convertApplicationExitInfo(testApplicationExitInfo))); + .captureAnrEventData(convertApplicationExitInfo(testApplicationExitInfo)); + verify(reportPersistence, never()).persistEvent(any(), eq(sessionId), eq(true)); } @Test @@ -140,17 +117,8 @@ public void testAppExitInfoEvent_notPersistIfAppExitInfoNotAnrButWithinSession() reportingCoordinator.persistAppExitInfoEvent(sessionId, testApplicationExitInfo); verify(dataCapture, never()) - .captureEventData( - any(Throwable.class), - any(Thread.class), - eq("anr"), - anyLong(), - anyInt(), - anyInt(), - anyBoolean()); - verify(reportPersistence, never()) - .persistAppExitInfoEvent( - any(), eq(sessionId), eq(convertApplicationExitInfo(testApplicationExitInfo))); + .captureAnrEventData(convertApplicationExitInfo(testApplicationExitInfo)); + verify(reportPersistence, never()).persistEvent(any(), eq(sessionId), eq(true)); } @Test @@ -167,14 +135,7 @@ private void mockEventInteractions() { when(mockEventApp.toBuilder()).thenReturn(mockEventAppBuilder); when(mockEventAppBuilder.setCustomAttributes(any())).thenReturn(mockEventAppBuilder); when(mockEventAppBuilder.build()).thenReturn(mockEventApp); - when(dataCapture.captureEventData( - any(Throwable.class), - any(Thread.class), - anyString(), - anyLong(), - anyInt(), - anyInt(), - anyBoolean())) + when(dataCapture.captureAnrEventData(any(CrashlyticsReport.ApplicationExitInfo.class))) .thenReturn(mockEvent); } @@ -202,6 +163,9 @@ private static CrashlyticsReport.ApplicationExitInfo convertApplicationExitInfo( .setProcessName(applicationExitInfo.getProcessName()) .setReasonCode(applicationExitInfo.getReason()) .setTimestamp(applicationExitInfo.getTimestamp()) + .setPid(applicationExitInfo.getPid()) + .setPss(applicationExitInfo.getPss()) + .setRss(applicationExitInfo.getRss()) .setTraceFile(null) .build(); } diff --git a/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReportTest.java b/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReportTest.java index 8a0640f1620..81e2240c31c 100644 --- a/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReportTest.java +++ b/firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/model/CrashlyticsReportTest.java @@ -49,14 +49,19 @@ public void testWithEvents_returnsNewReportWithAnr() { assertNull(testReport.getSession().getEvents()); final CrashlyticsReport withAnrEventsReport = - testReport - .withEvents(ImmutableList.from(makeAnrEvent())) - .withAppExitInfo(makeAppExitInfo()); + testReport.withEvents(ImmutableList.from(makeAnrEvent())); assertNotEquals(testReport, withAnrEventsReport); assertNotNull(withAnrEventsReport.getSession().getEvents()); assertEquals(1, withAnrEventsReport.getSession().getEvents().size()); - assertNotNull(withAnrEventsReport.getAppExitInfo()); + assertNotNull( + withAnrEventsReport + .getSession() + .getEvents() + .get(0) + .getApp() + .getExecution() + .getAppExitInfo()); } @Test @@ -225,16 +230,42 @@ private static ImmutableList makeTestEvents(int numEvents) { } private static Event makeAnrEvent() { - return makeTestEvent("ANR"); + return Event.builder() + .setType("anr") + .setTimestamp(1000) + .setApp( + Session.Event.Application.builder() + .setBackground(false) + .setExecution( + Execution.builder() + .setBinaries( + ImmutableList.from( + Execution.BinaryImage.builder() + .setBaseAddress(0) + .setName("name") + .setSize(100000) + .setUuid("uuid") + .build())) + .setSignal(Signal.builder().setCode("0").setName("0").setAddress(0).build()) + .setAppExitInfo(makeAppExitInfo()) + .build()) + .setUiOrientation(1) + .build()) + .setDevice( + Session.Event.Device.builder() + .setBatteryLevel(0.5) + .setBatteryVelocity(3) + .setDiskUsed(10000000) + .setOrientation(1) + .setProximityOn(true) + .setRamUsed(10000000) + .build()) + .build(); } private static Event makeTestEvent() { - return makeTestEvent("test"); - } - - private static Event makeTestEvent(String eventType) { return Event.builder() - .setType(eventType) + .setType("test") .setTimestamp(1000) .setApp( Session.Event.Application.builder() @@ -286,6 +317,9 @@ private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo() { .setImportance(1) .setReasonCode(1) .setProcessName("test") + .setPid(1) + .setPss(1) + .setRss(1) .build(); }