Skip to content

Commit fb2f8b2

Browse files
authored
Make the session id not depend on fetching the fid (#6152)
Make generating a session id not depend on fetching the fid. This is needed so we can allow the common worker to initialize, and start accepting use actions, before any data collection, settings, or fid validation happens. The hash of the crashlytics installation id was used to pad the length and increase entropy. Instead of that, we use a random uuid now. Verified nothing depends on the source of that padding by manually setting it to all 0s and doing an end-to-end test. The way the session id is built is a little bit weird, using bytes. In the future, we could rewrite this to use proper string formatting.
1 parent 9535f94 commit fb2f8b2

File tree

5 files changed

+48
-66
lines changed

5 files changed

+48
-66
lines changed

firebase-crashlytics/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* [feature] Added the `isCrashlyticsCollectionEnabled` API to check if Crashlytics collection is enabled.
33
([Github #5919](//github.com/firebase/firebase-android-sdk/issues/5919))
44
* [fixed] Ensure that on-demand fatal events are never processed on the main thread.
5+
* [changed] Internal changes to the way session IDs are generated.
56

67

78
# 19.0.3

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CLSUUIDTest.java

Lines changed: 25 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,63 +14,41 @@
1414

1515
package com.google.firebase.crashlytics.internal.common;
1616

17-
import static org.mockito.Mockito.mock;
18-
import static org.mockito.Mockito.when;
17+
import static com.google.common.truth.Truth.assertThat;
1918

20-
import com.google.android.gms.tasks.Tasks;
21-
import com.google.firebase.crashlytics.internal.CrashlyticsTestCase;
22-
import com.google.firebase.installations.FirebaseInstallationsApi;
19+
import java.util.ArrayList;
20+
import org.junit.Test;
2321

24-
public class CLSUUIDTest extends CrashlyticsTestCase {
25-
26-
private IdManager idManager;
27-
private CLSUUID uuid;
28-
29-
protected void setUp() throws Exception {
30-
super.setUp();
31-
FirebaseInstallationsApi installationsApiMock = mock(FirebaseInstallationsApi.class);
32-
when(installationsApiMock.getId()).thenReturn(Tasks.forResult("instanceId"));
33-
idManager =
34-
new IdManager(
35-
getContext(),
36-
getContext().getPackageName(),
37-
installationsApiMock,
38-
DataCollectionArbiterTest.MOCK_ARBITER_ENABLED);
39-
uuid = new CLSUUID(idManager);
40-
}
41-
42-
protected void tearDown() throws Exception {
43-
super.tearDown();
44-
uuid = null;
45-
}
22+
public class CLSUUIDTest {
4623

4724
/** Basic test of the CLSUUID string value. */
48-
public void testToString() {
49-
final String s = uuid.toString();
50-
assertNotNull("The uuid string value should not be null", s);
51-
assertEquals("The uuid string value should be 32 chars long", 32, s.length());
25+
@Test
26+
public void getSessionId() {
27+
String sessionId = new CLSUUID().getSessionId();
28+
assertThat(sessionId).isNotNull(); // The session id should not be null
29+
assertThat(sessionId).hasLength(32); // The session id should be 32 chars long
5230
}
5331

5432
/** Test that we don't get duplicate CLSUUID string values in a set of 100 uuid generated. */
55-
public void testOrder() {
56-
final String[] uuids = new String[100];
57-
CLSUUID uuid = null;
33+
@Test
34+
public void sessionIdsInOrder() {
35+
ArrayList<String> sessionIds = new ArrayList<>();
5836

59-
// Put 100 CLSUUID string values into an array.
37+
// Put 100 CLSUUID string values into a list.
6038
for (int i = 0; i < 100; i++) {
61-
uuid = new CLSUUID(idManager);
62-
uuids[i] = uuid.toString();
39+
sessionIds.add(new CLSUUID().getSessionId());
6340
}
6441

65-
// Assert that the other 99 CLSUUIDs don't match the current index in the loop.
66-
for (int i = 0; i < uuids.length; i++) {
67-
final String uuidAtIndex = uuids[i];
68-
for (int j = 0; j < uuids.length; j++) {
69-
if (i != j) {
70-
assertFalse(
71-
"We shouldn't have the same uuid at another index.", uuidAtIndex.equals(uuids[j]));
72-
}
73-
}
74-
}
42+
assertThat(sessionIds).isInOrder();
43+
assertThat(sessionIds).containsNoDuplicates();
44+
}
45+
46+
/** Test that we pad session ids properly. */
47+
@Test
48+
public void sessionIdsArePadded() {
49+
String sessionId1 = new CLSUUID().getSessionId();
50+
String sessionId2 = new CLSUUID().getSessionId();
51+
52+
assertThat(sessionId1.substring(20)).isEqualTo(sessionId2.substring(20));
7553
}
7654
}

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,46 @@
1818
import java.nio.ByteOrder;
1919
import java.util.Date;
2020
import java.util.Locale;
21+
import java.util.UUID;
2122
import java.util.concurrent.atomic.AtomicLong;
2223

2324
/**
2425
* A UUID whose lexicographical ordering is equivalent to a temporal ordering. Uniqueness is based
25-
* on timestamp, sequence identifier, processId, and a hash of the application installation
26-
* identifier.
27-
*
28-
* <p>Applications which generate colliding app installation identifiers are at risk of creating
29-
* colliding CLSUUIDs.
26+
* on timestamp, sequence identifier, processId, and a random hash.
3027
*/
3128
class CLSUUID {
32-
private static final AtomicLong _sequenceNumber = new AtomicLong(0);
3329

34-
private static String _clsId;
30+
// Padding to ensure the id is long enough, has only valid hex characters, and increase entropy.
31+
private static final String ID_SHA =
32+
CommonUtils.sha1(UUID.randomUUID().toString() + System.currentTimeMillis());
3533

36-
CLSUUID(IdManager idManager) {
37-
final byte[] bytes = new byte[10];
34+
private static final AtomicLong sequenceNumber = new AtomicLong(0);
35+
private final String sessionId;
36+
37+
CLSUUID() {
38+
byte[] bytes = new byte[10];
3839

3940
this.populateTime(bytes);
4041
this.populateSequenceNumber(bytes);
4142
this.populatePID(bytes);
4243

43-
// sha1 it to ensure the string is long enough, has only valid hex characters, and to
44-
// increase entropy.
45-
final String idSha = CommonUtils.sha1(idManager.getInstallIds().getCrashlyticsInstallId());
46-
final String timeSeqPid = CommonUtils.hexify(bytes);
44+
String timeSeqPid = CommonUtils.hexify(bytes);
4745

48-
_clsId =
46+
sessionId =
4947
String.format(
5048
Locale.US,
5149
"%s%s%s%s",
5250
timeSeqPid.substring(0, 12),
5351
timeSeqPid.substring(12, 16),
5452
timeSeqPid.subSequence(16, 20),
55-
idSha.substring(0, 12))
53+
ID_SHA.substring(0, 12))
5654
.toUpperCase(Locale.US);
5755
}
5856

57+
public String getSessionId() {
58+
return sessionId;
59+
}
60+
5961
private void populateTime(byte[] bytes) {
6062
final Date date = new Date();
6163
final long time = date.getTime();
@@ -73,7 +75,7 @@ private void populateTime(byte[] bytes) {
7375
}
7476

7577
private void populateSequenceNumber(byte[] bytes) {
76-
final byte[] sequenceBytes = convertLongToTwoByteBuffer(_sequenceNumber.incrementAndGet());
78+
byte[] sequenceBytes = convertLongToTwoByteBuffer(sequenceNumber.incrementAndGet());
7779
bytes[6] = sequenceBytes[0];
7880
bytes[7] = sequenceBytes[1];
7981
}
@@ -103,7 +105,8 @@ private static byte[] convertLongToTwoByteBuffer(long value) {
103105
return buf.array();
104106
}
105107

108+
@Override
106109
public String toString() {
107-
return _clsId;
110+
return sessionId;
108111
}
109112
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public Task<Void> call() throws Exception {
211211

212212
doWriteAppExceptionMarker(timestampMillis);
213213
doCloseSessions(settingsProvider);
214-
doOpenSession(new CLSUUID(idManager).toString(), isOnDemand);
214+
doOpenSession(new CLSUUID().getSessionId(), isOnDemand);
215215

216216
// If automatic data collection is disabled, we'll need to wait until the next run
217217
// of the app.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public boolean onPreExecute(AppData appData, SettingsProvider settingsProvider)
146146
throw new IllegalStateException(MISSING_BUILD_ID_MSG);
147147
}
148148

149-
final String sessionIdentifier = new CLSUUID(idManager).toString();
149+
String sessionIdentifier = new CLSUUID().getSessionId();
150150
try {
151151
crashMarker = new CrashlyticsFileMarker(CRASH_MARKER_FILE_NAME, fileStore);
152152
initializationMarker = new CrashlyticsFileMarker(INITIALIZATION_MARKER_FILE_NAME, fileStore);

0 commit comments

Comments
 (0)