Skip to content

Commit 59e9c3a

Browse files
authored
Impose a size limit on payloads sent to DataTransport (#1406)
1 parent ad62fcc commit 59e9c3a

File tree

2 files changed

+58
-7
lines changed

2 files changed

+58
-7
lines changed

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/send/DataTransportCrashlyticsReportSenderTest.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
import static org.mockito.ArgumentMatchers.any;
2121
import static org.mockito.Mockito.doAnswer;
2222
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.never;
24+
import static org.mockito.Mockito.verify;
25+
import static org.mockito.Mockito.when;
2326

2427
import androidx.test.runner.AndroidJUnit4;
28+
import com.google.android.datatransport.Transformer;
2529
import com.google.android.datatransport.Transport;
2630
import com.google.android.datatransport.TransportScheduleCallback;
2731
import com.google.android.gms.tasks.Task;
@@ -40,13 +44,15 @@
4044
public class DataTransportCrashlyticsReportSenderTest {
4145

4246
@Mock private Transport<CrashlyticsReport> mockTransport;
47+
@Mock private Transformer<CrashlyticsReport, byte[]> mockTransform;
4348

4449
private DataTransportCrashlyticsReportSender reportSender;
4550

4651
@Before
4752
public void setUp() throws Exception {
4853
MockitoAnnotations.initMocks(this);
49-
reportSender = new DataTransportCrashlyticsReportSender(mockTransport);
54+
when(mockTransform.apply(any())).thenReturn(new byte[0]);
55+
reportSender = new DataTransportCrashlyticsReportSender(mockTransport, mockTransform);
5056
}
5157

5258
@Test
@@ -123,6 +129,26 @@ public void testSendReports_oneSuccessOneFail() throws Exception {
123129
assertEquals(ex, send2.getException());
124130
}
125131

132+
@Test
133+
public void testSendLargeReport_successfulWithoutSchedulingToDataTransport() throws Exception {
134+
doAnswer(callbackAnswer(null)).when(mockTransport).schedule(any(), any());
135+
136+
final CrashlyticsReportWithSessionId report = mockReportWithSessionId();
137+
138+
when(mockTransform.apply(report.getReport())).thenReturn(new byte[1024 * 1024]);
139+
140+
final Task<CrashlyticsReportWithSessionId> send = reportSender.sendReport(report);
141+
142+
try {
143+
Tasks.await(send);
144+
} catch (ExecutionException e) {
145+
// Allow this to fall through
146+
}
147+
148+
assertTrue(send.isSuccessful());
149+
verify(mockTransport, never()).schedule(any(), any());
150+
}
151+
126152
private static Answer<Void> callbackAnswer(Exception failure) {
127153
return (i) -> {
128154
final TransportScheduleCallback callback = i.getArgument(1);

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/send/DataTransportCrashlyticsReportSender.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
import androidx.annotation.NonNull;
1919
import com.google.android.datatransport.Encoding;
2020
import com.google.android.datatransport.Event;
21+
import com.google.android.datatransport.Transformer;
2122
import com.google.android.datatransport.Transport;
2223
import com.google.android.datatransport.cct.CCTDestination;
2324
import com.google.android.datatransport.runtime.TransportRuntime;
2425
import com.google.android.gms.tasks.Task;
2526
import com.google.android.gms.tasks.TaskCompletionSource;
27+
import com.google.android.gms.tasks.Tasks;
28+
import com.google.firebase.crashlytics.internal.Logger;
2629
import com.google.firebase.crashlytics.internal.common.CrashlyticsReportWithSessionId;
2730
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
2831
import com.google.firebase.crashlytics.internal.model.serialization.CrashlyticsReportJsonTransform;
@@ -41,8 +44,14 @@ public class DataTransportCrashlyticsReportSender {
4144
private static final String CRASHLYTICS_API_KEY =
4245
mergeStrings("AzSBpY4F0rHiHFdinTvM", "IayrSTFL9eJ69YeSUO2");
4346
private static final String CRASHLYTICS_TRANSPORT_NAME = "FIREBASE_CRASHLYTICS_REPORT";
47+
private static final Transformer<CrashlyticsReport, byte[]> DEFAULT_TRANSFORM =
48+
(r) -> TRANSFORM.reportToJson(r).getBytes(Charset.forName("UTF-8"));
49+
50+
// Assumed limit of 1MB, with a little extra headroom
51+
private static final int MAX_DATATRANSPORT_BYTES = 1024 * 832;
4452

4553
private final Transport<CrashlyticsReport> transport;
54+
private final Transformer<CrashlyticsReport, byte[]> transportTransform;
4655

4756
public static DataTransportCrashlyticsReportSender create(Context context) {
4857
TransportRuntime.initialize(context);
@@ -53,26 +62,42 @@ public static DataTransportCrashlyticsReportSender create(Context context) {
5362
CRASHLYTICS_TRANSPORT_NAME,
5463
CrashlyticsReport.class,
5564
Encoding.of("json"),
56-
r -> TRANSFORM.reportToJson(r).getBytes(Charset.forName("UTF-8")));
57-
return new DataTransportCrashlyticsReportSender(transport);
65+
DEFAULT_TRANSFORM);
66+
return new DataTransportCrashlyticsReportSender(transport, DEFAULT_TRANSFORM);
5867
}
5968

60-
DataTransportCrashlyticsReportSender(Transport<CrashlyticsReport> transport) {
69+
DataTransportCrashlyticsReportSender(
70+
Transport<CrashlyticsReport> transport,
71+
Transformer<CrashlyticsReport, byte[]> transportTransform) {
6172
this.transport = transport;
73+
this.transportTransform = transportTransform;
6274
}
6375

6476
@NonNull
6577
public Task<CrashlyticsReportWithSessionId> sendReport(
66-
@NonNull CrashlyticsReportWithSessionId report) {
78+
@NonNull CrashlyticsReportWithSessionId reportWithSessionId) {
79+
final CrashlyticsReport report = reportWithSessionId.getReport();
80+
81+
// Workaround for b/152905875, impose a maximum size on reports.
82+
final int reportSize = transportTransform.apply(report).length;
83+
if (reportSize > MAX_DATATRANSPORT_BYTES) {
84+
Logger.getLogger()
85+
.d(
86+
String.format(
87+
"Report is too large to be sent via DataTransport. Maximum size is %d bytes. Report size is %d bytes. Removing report.",
88+
MAX_DATATRANSPORT_BYTES, reportSize));
89+
return Tasks.forResult(reportWithSessionId);
90+
}
91+
6792
TaskCompletionSource<CrashlyticsReportWithSessionId> tcs = new TaskCompletionSource<>();
6893
transport.schedule(
69-
Event.ofUrgent(report.getReport()),
94+
Event.ofUrgent(report),
7095
error -> {
7196
if (error != null) {
7297
tcs.trySetException(error);
7398
return;
7499
}
75-
tcs.trySetResult(report);
100+
tcs.trySetResult(reportWithSessionId);
76101
});
77102
return tcs.getTask();
78103
}

0 commit comments

Comments
 (0)