Skip to content

Commit d06d7c5

Browse files
authored
Do not attempt to parse empty json files in MetaDataStore (#3735)
* Do not attempt to parse empty json files in MetaDataStore * Safely delete corrupt files in MetaDataStore
1 parent b60eaf7 commit d06d7c5

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/metadata/MetaDataStoreTest.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
import com.google.firebase.crashlytics.internal.CrashlyticsTestCase;
1818
import com.google.firebase.crashlytics.internal.common.CrashlyticsBackgroundWorker;
1919
import com.google.firebase.crashlytics.internal.persistence.FileStore;
20+
import java.io.File;
21+
import java.io.IOException;
22+
import java.io.PrintWriter;
2023
import java.util.Collections;
2124
import java.util.HashMap;
2225
import java.util.Map;
2326

27+
@SuppressWarnings("ResultOfMethodCallIgnored") // Convenient use of files.
2428
public class MetaDataStoreTest extends CrashlyticsTestCase {
2529

2630
private static final String SESSION_ID_1 = "session1";
@@ -40,7 +44,7 @@ public class MetaDataStoreTest extends CrashlyticsTestCase {
4044
private static final String ESCAPED = "\ttest\nvalue";
4145

4246
private FileStore fileStore;
43-
private CrashlyticsBackgroundWorker worker = new CrashlyticsBackgroundWorker(Runnable::run);
47+
private final CrashlyticsBackgroundWorker worker = new CrashlyticsBackgroundWorker(Runnable::run);
4448

4549
private MetaDataStore storeUnderTest;
4650

@@ -83,7 +87,7 @@ public void testWriteUserData_singleField() {
8387
public void testWriteUserData_null() {
8488
storeUnderTest.writeUserData(SESSION_ID_1, metadataWithUserId(SESSION_ID_1, null).getUserId());
8589
UserMetadata userData = UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, worker);
86-
assertEquals(null, userData.getUserId());
90+
assertNull(userData.getUserId());
8791
}
8892

8993
public void testWriteUserData_emptyString() {
@@ -112,6 +116,24 @@ public void testWriteUserData_readDifferentSession() {
112116
assertNull(userData.getUserId());
113117
}
114118

119+
public void testReadUserData_corruptData() throws IOException {
120+
File file = storeUnderTest.getUserDataFileForSession(SESSION_ID_1);
121+
try (PrintWriter printWriter = new PrintWriter(file)) {
122+
printWriter.println("Matt says hi!");
123+
}
124+
UserMetadata userData = UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, worker);
125+
assertNull(userData.getUserId());
126+
assertFalse(file.exists());
127+
}
128+
129+
public void testReadUserData_emptyData() throws IOException {
130+
File file = storeUnderTest.getUserDataFileForSession(SESSION_ID_1);
131+
file.createNewFile();
132+
UserMetadata userData = UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, worker);
133+
assertNull(userData.getUserId());
134+
assertFalse(file.exists());
135+
}
136+
115137
public void testReadUserData_noStoredData() {
116138
UserMetadata userData = UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, worker);
117139
assertNull(userData.getUserId());
@@ -237,6 +259,24 @@ public void testWriteKeys_readSeparateFromUser() {
237259
assertEqualMaps(internalKeys, readInternalKeys);
238260
}
239261

262+
public void testReadKeys_corruptData() throws IOException {
263+
File file = storeUnderTest.getKeysFileForSession(SESSION_ID_1);
264+
try (PrintWriter printWriter = new PrintWriter(file)) {
265+
printWriter.println("This is not json.");
266+
}
267+
final Map<String, String> readKeys = storeUnderTest.readKeyData(SESSION_ID_1);
268+
assertEquals(0, readKeys.size());
269+
assertFalse(file.exists());
270+
}
271+
272+
public void testReadKeys_emptyStoredData() throws IOException {
273+
File file = storeUnderTest.getKeysFileForSession(SESSION_ID_1);
274+
file.createNewFile();
275+
final Map<String, String> readKeys = storeUnderTest.readKeyData(SESSION_ID_1);
276+
assertEquals(0, readKeys.size());
277+
assertFalse(file.exists());
278+
}
279+
240280
public void testReadKeys_noStoredData() {
241281
final Map<String, String> readKeys = storeUnderTest.readKeyData(SESSION_ID_1);
242282
assertEquals(0, readKeys.size());

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/metadata/MetaDataStore.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void writeUserData(String sessionId, String userId) {
5959
writer.write(userIdJson);
6060
writer.flush();
6161
} catch (Exception e) {
62-
Logger.getLogger().e("Error serializing user metadata.", e);
62+
Logger.getLogger().w("Error serializing user metadata.", e);
6363
} finally {
6464
CommonUtils.closeOrLog(writer, "Failed to close user metadata file.");
6565
}
@@ -68,8 +68,9 @@ public void writeUserData(String sessionId, String userId) {
6868
@Nullable
6969
public String readUserId(String sessionId) {
7070
final File f = getUserDataFileForSession(sessionId);
71-
if (!f.exists()) {
71+
if (!f.exists() || f.length() == 0) {
7272
Logger.getLogger().d("No userId set for session " + sessionId);
73+
safeDeleteCorruptFile(f);
7374
return null;
7475
}
7576

@@ -80,7 +81,8 @@ public String readUserId(String sessionId) {
8081
Logger.getLogger().d("Loaded userId " + userId + " for session " + sessionId);
8182
return userId;
8283
} catch (Exception e) {
83-
Logger.getLogger().e("Error deserializing user metadata.", e);
84+
Logger.getLogger().w("Error deserializing user metadata.", e);
85+
safeDeleteCorruptFile(f);
8486
} finally {
8587
CommonUtils.closeOrLog(is, "Failed to close user metadata file.");
8688
}
@@ -101,7 +103,8 @@ public void writeKeyData(String sessionId, Map<String, String> keyData, boolean
101103
writer.write(keyDataString);
102104
writer.flush();
103105
} catch (Exception e) {
104-
Logger.getLogger().e("Error serializing key/value metadata.", e);
106+
Logger.getLogger().w("Error serializing key/value metadata.", e);
107+
safeDeleteCorruptFile(f);
105108
} finally {
106109
CommonUtils.closeOrLog(writer, "Failed to close key/value metadata file.");
107110
}
@@ -114,7 +117,8 @@ public Map<String, String> readKeyData(String sessionId) {
114117
Map<String, String> readKeyData(String sessionId, boolean isInternal) {
115118
final File f =
116119
isInternal ? getInternalKeysFileForSession(sessionId) : getKeysFileForSession(sessionId);
117-
if (!f.exists()) {
120+
if (!f.exists() || f.length() == 0) {
121+
safeDeleteCorruptFile(f);
118122
return Collections.emptyMap();
119123
}
120124

@@ -123,7 +127,8 @@ Map<String, String> readKeyData(String sessionId, boolean isInternal) {
123127
is = new FileInputStream(f);
124128
return jsonToKeysData(CommonUtils.streamToString(is));
125129
} catch (Exception e) {
126-
Logger.getLogger().e("Error deserializing user metadata.", e);
130+
Logger.getLogger().w("Error deserializing user metadata.", e);
131+
safeDeleteCorruptFile(f);
127132
} finally {
128133
CommonUtils.closeOrLog(is, "Failed to close user metadata file.");
129134
}
@@ -177,4 +182,10 @@ private static String keysDataToJson(final Map<String, String> keyData) {
177182
private static String valueOrNull(JSONObject json, String key) {
178183
return !json.isNull(key) ? json.optString(key, null) : null;
179184
}
185+
186+
private static void safeDeleteCorruptFile(File file) {
187+
if (file.exists() && file.delete()) {
188+
Logger.getLogger().i("Deleted corrupt file: " + file.getAbsolutePath());
189+
}
190+
}
180191
}

0 commit comments

Comments
 (0)