Skip to content

Commit fed4082

Browse files
committed
Refactor datatransport abstractions to support polymorphic handling of session parts
1 parent fa31e55 commit fed4082

11 files changed

+374
-268
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.crashlytics.internal.common;
16+
17+
import androidx.annotation.NonNull;
18+
import androidx.annotation.Nullable;
19+
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
20+
import java.io.ByteArrayInputStream;
21+
import java.io.InputStream;
22+
23+
/** A {@link NativeSessionFile} backed by a byte array. */
24+
class BytesBackedNativeSessionFile implements NativeSessionFile {
25+
private final byte[] bytes;
26+
private final String name;
27+
28+
BytesBackedNativeSessionFile(@NonNull String name, @Nullable byte[] bytes) {
29+
this.name = name;
30+
this.bytes = bytes;
31+
}
32+
33+
public String getName() {
34+
return this.name;
35+
}
36+
37+
@Override
38+
public InputStream getStream() {
39+
return isEmpty() ? null : new ByteArrayInputStream(bytes);
40+
}
41+
42+
@Override
43+
public CrashlyticsReport.FilesPayload.File asFilePayload() {
44+
return isEmpty()
45+
? null
46+
: CrashlyticsReport.FilesPayload.File.builder()
47+
.setContents(bytes)
48+
.setFilename(name)
49+
.build();
50+
}
51+
52+
private boolean isEmpty() {
53+
return bytes == null || bytes.length == 0;
54+
}
55+
}

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsController.java

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@
3232
import com.google.firebase.analytics.connector.AnalyticsConnector;
3333
import com.google.firebase.crashlytics.internal.CrashlyticsNativeComponent;
3434
import com.google.firebase.crashlytics.internal.Logger;
35+
import com.google.firebase.crashlytics.internal.NativeSessionFileProvider;
3536
import com.google.firebase.crashlytics.internal.analytics.AnalyticsConnectorReceiver;
3637
import com.google.firebase.crashlytics.internal.analytics.AnalyticsReceiver;
3738
import com.google.firebase.crashlytics.internal.log.LogFileManager;
39+
import com.google.firebase.crashlytics.internal.ndk.NativeFileUtils;
3840
import com.google.firebase.crashlytics.internal.network.HttpRequestFactory;
3941
import com.google.firebase.crashlytics.internal.persistence.FileStore;
4042
import com.google.firebase.crashlytics.internal.proto.ClsFileOutputStream;
@@ -56,14 +58,13 @@
5658
import com.google.firebase.crashlytics.internal.stacktrace.StackTraceTrimmingStrategy;
5759
import com.google.firebase.crashlytics.internal.stacktrace.TrimmedThrowableData;
5860
import com.google.firebase.crashlytics.internal.unity.UnityVersionProvider;
59-
import java.io.ByteArrayInputStream;
6061
import java.io.File;
6162
import java.io.FileInputStream;
62-
import java.io.FileNotFoundException;
6363
import java.io.FileOutputStream;
6464
import java.io.FilenameFilter;
6565
import java.io.IOException;
6666
import java.io.InputStream;
67+
import java.util.ArrayList;
6768
import java.util.Arrays;
6869
import java.util.Collections;
6970
import java.util.Comparator;
@@ -1104,47 +1105,34 @@ public boolean accept(File dir, String filename) {
11041105

11051106
private void finalizePreviousNativeSession(String previousSessionId) throws IOException {
11061107
Logger.getLogger().d("Finalizing native report for session " + previousSessionId);
1107-
final GzipFileNativeSessionProcessingStrategy strategy =
1108-
new GzipFileNativeSessionProcessingStrategy(
1109-
context, (sessionId) -> new File(getNativeSessionFilesDir(), sessionId));
1110-
1111-
final File filesDir = getFilesDir();
1112-
final MetaDataStore metaDataStore = new MetaDataStore(filesDir);
1113-
final File userFile = metaDataStore.getUserDataFileForSession(previousSessionId);
1114-
final File keysFile = metaDataStore.getKeysFileForSession(previousSessionId);
1115-
1108+
NativeSessionFileProvider sessionFileProvider =
1109+
nativeComponent.getSessionFileProvider(previousSessionId);
1110+
File minidumpFile = sessionFileProvider.getMinidumpFile();
1111+
if (minidumpFile == null || !minidumpFile.exists()) {
1112+
Logger.getLogger().w("No minidump data found for session " + previousSessionId);
1113+
return;
1114+
}
11161115
final LogFileManager previousSessionLogManager =
1117-
new LogFileManager(getContext(), logFileDirectoryProvider, previousSessionId);
1118-
byte[] logBytes = previousSessionLogManager.getBytesForLog();
1116+
new LogFileManager(context, logFileDirectoryProvider, previousSessionId);
1117+
final File nativeSessionDirectory = new File(getNativeSessionFilesDir(), previousSessionId);
11191118

1120-
InputStream keysInput = null;
1121-
InputStream logsInput = null;
1122-
InputStream userInput = null;
1123-
try {
1124-
userInput = openFileStream(userFile);
1125-
keysInput = openFileStream(keysFile);
1126-
if (logBytes != null && logBytes.length > 0) {
1127-
logsInput = new ByteArrayInputStream(logBytes);
1128-
}
1129-
strategy.processNativeSession(
1130-
nativeComponent, previousSessionId, keysInput, logsInput, userInput);
1131-
} finally {
1132-
CommonUtils.closeQuietly(keysInput);
1133-
CommonUtils.closeQuietly(logsInput);
1134-
CommonUtils.closeQuietly(userInput);
1119+
if (!nativeSessionDirectory.mkdirs()) {
1120+
Logger.getLogger().d("Couldn't create native sessions directory");
1121+
return;
11351122
}
11361123

1137-
previousSessionLogManager.clearLog();
1138-
}
1124+
List<NativeSessionFile> nativeSessionFiles =
1125+
getNativeSessionFiles(
1126+
sessionFileProvider,
1127+
previousSessionId,
1128+
getContext(),
1129+
getFilesDir(),
1130+
previousSessionLogManager.getBytesForLog());
11391131

1140-
private static FileInputStream openFileStream(File f) {
1141-
FileInputStream stream;
1142-
try {
1143-
stream = new FileInputStream(f);
1144-
} catch (FileNotFoundException fnf) {
1145-
stream = null;
1146-
}
1147-
return stream;
1132+
NativeSessionFileGzipper.processNativeSessions(nativeSessionDirectory, nativeSessionFiles);
1133+
reportingCoordinator.finalizeNativeEvent(nativeSessionFiles, previousSessionId);
1134+
1135+
previousSessionLogManager.clearLog();
11481136
}
11491137

11501138
/** Removes dashes in the Crashlytics session identifier to conform to Firebase constraints. */
@@ -1860,6 +1848,42 @@ public void run() {
18601848
}
18611849
}
18621850

1851+
static List<NativeSessionFile> getNativeSessionFiles(
1852+
NativeSessionFileProvider fileProvider,
1853+
String previousSessionId,
1854+
Context context,
1855+
File filesDir,
1856+
byte[] logBytes) {
1857+
1858+
final MetaDataStore metaDataStore = new MetaDataStore(filesDir);
1859+
final File userFile = metaDataStore.getUserDataFileForSession(previousSessionId);
1860+
final File keysFile = metaDataStore.getKeysFileForSession(previousSessionId);
1861+
1862+
byte[] binaryImageBytes = null;
1863+
try {
1864+
binaryImageBytes =
1865+
NativeFileUtils.binaryImagesJsonFromMapsFile(fileProvider.getBinaryImagesFile(), context);
1866+
} catch (IOException e) {
1867+
// Keep processing, we'll add an empty binaryImages object.
1868+
}
1869+
1870+
List<NativeSessionFile> nativeSessionFiles = new ArrayList<>();
1871+
nativeSessionFiles.add(new BytesBackedNativeSessionFile("logs", logBytes));
1872+
nativeSessionFiles.add(new BytesBackedNativeSessionFile("binaryImages", binaryImageBytes));
1873+
nativeSessionFiles.add(
1874+
new FileBackedNativeSessionFile("metadata", fileProvider.getMetadataFile()));
1875+
nativeSessionFiles.add(
1876+
new FileBackedNativeSessionFile("session", fileProvider.getSessionFile()));
1877+
nativeSessionFiles.add(new FileBackedNativeSessionFile("app", fileProvider.getAppFile()));
1878+
nativeSessionFiles.add(new FileBackedNativeSessionFile("device", fileProvider.getDeviceFile()));
1879+
nativeSessionFiles.add(new FileBackedNativeSessionFile("os", fileProvider.getOsFile()));
1880+
nativeSessionFiles.add(
1881+
new FileBackedNativeSessionFile("minidump", fileProvider.getMinidumpFile()));
1882+
nativeSessionFiles.add(new FileBackedNativeSessionFile("user", userFile));
1883+
nativeSessionFiles.add(new FileBackedNativeSessionFile("keys", keysFile));
1884+
return nativeSessionFiles;
1885+
}
1886+
18631887
private static final class LogFileDirectoryProvider implements LogFileManager.DirectoryProvider {
18641888

18651889
private static final String LOG_FILES_DIR = "log-files";

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsReportDataCapture.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,11 @@ public CrashlyticsReportDataCapture(
7777
}
7878

7979
public CrashlyticsReport captureReportData(String identifier, long timestamp) {
80-
return CrashlyticsReport.builder()
81-
.setSdkVersion(BuildConfig.VERSION_NAME)
82-
.setGmpAppId(appData.googleAppId)
83-
.setInstallationUuid(idManager.getCrashlyticsInstallId())
84-
.setBuildVersion(appData.versionCode)
85-
.setDisplayVersion(appData.versionName)
86-
.setPlatform(REPORT_ANDROID_PLATFORM)
87-
.setSession(populateSessionData(identifier, timestamp))
88-
.build();
80+
return buildReportData().setSession(populateSessionData(identifier, timestamp)).build();
81+
}
82+
83+
public CrashlyticsReport captureReportData() {
84+
return buildReportData().build();
8985
}
9086

9187
public Event captureEventData(
@@ -115,6 +111,16 @@ public Event captureEventData(
115111
.build();
116112
}
117113

114+
private CrashlyticsReport.Builder buildReportData() {
115+
return CrashlyticsReport.builder()
116+
.setSdkVersion(BuildConfig.VERSION_NAME)
117+
.setGmpAppId(appData.googleAppId)
118+
.setInstallationUuid(idManager.getCrashlyticsInstallId())
119+
.setBuildVersion(appData.versionCode)
120+
.setDisplayVersion(appData.versionName)
121+
.setPlatform(REPORT_ANDROID_PLATFORM);
122+
}
123+
118124
private CrashlyticsReport.Session populateSessionData(String identifier, long timestamp) {
119125
return CrashlyticsReport.Session.builder()
120126
.setStartedAt(timestamp)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.crashlytics.internal.common;
16+
17+
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.File;
20+
import java.io.FileInputStream;
21+
import java.io.FileNotFoundException;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.nio.charset.Charset;
25+
26+
/** A {@link NativeSessionFile} backed by a {@link File} currently on disk. */
27+
class FileBackedNativeSessionFile implements NativeSessionFile {
28+
private static final Charset UTF_8 = Charset.forName("UTF-8");
29+
30+
private final File f;
31+
private final String name;
32+
33+
FileBackedNativeSessionFile(String name, File f) {
34+
this.name = name;
35+
this.f = f;
36+
}
37+
38+
public String getName() {
39+
return this.name;
40+
}
41+
42+
@Override
43+
public InputStream getStream() {
44+
try {
45+
return new FileInputStream(f);
46+
} catch (FileNotFoundException f) {
47+
return null;
48+
}
49+
}
50+
51+
@Override
52+
public CrashlyticsReport.FilesPayload.File asFilePayload() {
53+
byte[] bytes = asBytes();
54+
return bytes != null
55+
? CrashlyticsReport.FilesPayload.File.builder()
56+
.setContents(asBytes())
57+
.setFilename(name)
58+
.build()
59+
: null;
60+
}
61+
62+
private byte[] asBytes() {
63+
final byte[] readBuffer = new byte[8192];
64+
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
65+
try (InputStream stream = new FileInputStream(f)) {
66+
int read;
67+
while ((read = stream.read(readBuffer)) > 0) {
68+
bos.write(readBuffer, 0, read);
69+
}
70+
return new String(bos.toByteArray(), UTF_8).getBytes();
71+
} catch (FileNotFoundException e) {
72+
return null;
73+
} catch (IOException e) {
74+
return null;
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)