Skip to content

Commit 1935e8c

Browse files
committed
Fix set user id for on-demand fatal
1 parent dff55b6 commit 1935e8c

File tree

3 files changed

+70
-6
lines changed

3 files changed

+70
-6
lines changed

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

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.google.firebase.crashlytics.internal.NativeSessionFileProvider;
3939
import com.google.firebase.crashlytics.internal.analytics.AnalyticsEventLogger;
4040
import com.google.firebase.crashlytics.internal.metadata.LogFileManager;
41+
import com.google.firebase.crashlytics.internal.metadata.UserMetadata;
4142
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
4243
import com.google.firebase.crashlytics.internal.persistence.FileStore;
4344
import com.google.firebase.crashlytics.internal.settings.Settings;
@@ -52,6 +53,7 @@
5253
import java.util.TreeSet;
5354
import java.util.concurrent.Executor;
5455
import java.util.concurrent.TimeUnit;
56+
import org.junit.Test;
5557
import org.mockito.ArgumentCaptor;
5658

5759
public class CrashlyticsControllerTest extends CrashlyticsTestCase {
@@ -101,22 +103,33 @@ private class ControllerBuilder {
101103
private CrashlyticsNativeComponent nativeComponent = null;
102104
private AnalyticsEventLogger analyticsEventLogger;
103105
private SessionReportingCoordinator sessionReportingCoordinator;
106+
107+
private CrashlyticsBackgroundWorker backgroundWorker;
104108
private LogFileManager logFileManager = null;
105109

110+
private UserMetadata userMetadata = null;
111+
106112
ControllerBuilder() {
107113
dataCollectionArbiter = mockDataCollectionArbiter;
108114
nativeComponent = mockNativeComponent;
109115

110116
analyticsEventLogger = mock(AnalyticsEventLogger.class);
111117

112118
sessionReportingCoordinator = mockSessionReportingCoordinator;
119+
120+
backgroundWorker = new CrashlyticsBackgroundWorker(new SameThreadExecutorService());
113121
}
114122

115123
ControllerBuilder setDataCollectionArbiter(DataCollectionArbiter arbiter) {
116124
dataCollectionArbiter = arbiter;
117125
return this;
118126
}
119127

128+
ControllerBuilder setUserMetadata(UserMetadata userMetadata) {
129+
this.userMetadata = userMetadata;
130+
return this;
131+
}
132+
120133
public ControllerBuilder setNativeComponent(CrashlyticsNativeComponent nativeComponent) {
121134
this.nativeComponent = nativeComponent;
122135
return this;
@@ -153,13 +166,13 @@ public CrashlyticsController build() {
153166
final CrashlyticsController controller =
154167
new CrashlyticsController(
155168
testContext.getApplicationContext(),
156-
new CrashlyticsBackgroundWorker(new SameThreadExecutorService()),
169+
backgroundWorker,
157170
idManager,
158171
dataCollectionArbiter,
159172
testFileStore,
160173
crashMarker,
161174
appData,
162-
null,
175+
userMetadata,
163176
logFileManager,
164177
sessionReportingCoordinator,
165178
nativeComponent,
@@ -210,6 +223,26 @@ public void testFatalException_callsSessionReportingCoordinatorPersistFatal() th
210223
.persistFatalEvent(eq(fatal), eq(thread), eq(sessionId), anyLong());
211224
}
212225

226+
@Test
227+
public void testOnDemandFatal_call() {
228+
Thread thread = Thread.currentThread();
229+
Exception fatal = new RuntimeException("Fatal");
230+
Thread.UncaughtExceptionHandler exceptionHandler = mock(Thread.UncaughtExceptionHandler.class);
231+
UserMetadata mockUserMetadata = mock(UserMetadata.class);
232+
when(mockSessionReportingCoordinator.listSortedOpenSessionIds())
233+
.thenReturn(new TreeSet<>(Collections.singleton(SESSION_ID)).descendingSet());
234+
235+
final CrashlyticsController controller =
236+
builder()
237+
.setLogFileManager(new LogFileManager(testFileStore))
238+
.setUserMetadata(mockUserMetadata)
239+
.build();
240+
controller.enableExceptionHandling(SESSION_ID, exceptionHandler, testSettingsProvider);
241+
controller.logFatalException(thread, fatal);
242+
243+
verify(mockUserMetadata).setNewSession(any(String.class));
244+
}
245+
213246
public void testNativeCrashDataCausesNativeReport() throws Exception {
214247
final String sessionId = "sessionId_1_new";
215248
final String previousSessionId = "sessionId_0_previous";

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ public Task<Void> call() throws Exception {
214214

215215
doWriteAppExceptionMarker(timestampMillis);
216216
doCloseSessions(settingsProvider);
217-
doOpenSession(new CLSUUID(idManager).toString());
217+
doOpenSession(new CLSUUID(idManager).toString(), isOnDemand);
218218

219219
// If automatic data collection is disabled, we'll need to wait until the next run
220220
// of the app.
@@ -496,7 +496,7 @@ void openSession(String sessionIdentifier) {
496496
new Callable<Void>() {
497497
@Override
498498
public Void call() throws Exception {
499-
doOpenSession(sessionIdentifier);
499+
doOpenSession(sessionIdentifier, false);
500500
return null;
501501
}
502502
});
@@ -548,7 +548,7 @@ boolean finalizeSessions(SettingsProvider settingsProvider) {
548548
* Not synchronized/locked. Must be executed from the single thread executor service used by this
549549
* class.
550550
*/
551-
private void doOpenSession(String sessionIdentifier) {
551+
private void doOpenSession(String sessionIdentifier, Boolean isOnDemand) {
552552
final long startedAtSeconds = getCurrentTimestampSeconds();
553553

554554
Logger.getLogger().d("Opening a new session with ID " + sessionIdentifier);
@@ -566,6 +566,17 @@ private void doOpenSession(String sessionIdentifier) {
566566
startedAtSeconds,
567567
StaticSessionData.create(appData, osData, deviceData));
568568

569+
// If is on-demand fatal, we need to update the session id for userMetadata
570+
// as well(since we don't really change the object to a new one for a new session).
571+
// all the information in the previous session is still in memory, but we do need to
572+
// manually writing them into persistence for the new session.
573+
if (isOnDemand) {
574+
final String currentSessionIdOnDemand = getCurrentSessionId();
575+
if (currentSessionIdOnDemand != null) {
576+
userMetadata.setNewSession(currentSessionIdOnDemand);
577+
}
578+
}
579+
569580
logFileManager.setCurrentSession(sessionIdentifier);
570581
reportingCoordinator.onBeginSession(sessionIdentifier, startedAtSeconds);
571582
}

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class UserMetadata {
4141

4242
private final MetaDataStore metaDataStore;
4343
private final CrashlyticsBackgroundWorker backgroundWorker;
44-
private final String sessionIdentifier;
44+
private String sessionIdentifier;
4545

4646
// The following references contain a marker bit, which is true if the data maintained in the
4747
// associated reference has been serialized since the last time it was updated.
@@ -77,6 +77,26 @@ public UserMetadata(
7777
this.backgroundWorker = backgroundWorker;
7878
}
7979

80+
/**
81+
* Refresh the userMetadata to reflect the status of the new session. This API is mainly for
82+
* on-demand fatal feature since we need to close and update to a new session. UserMetadata also
83+
* need to make this update instead of updating session id, we also need to manually writing the
84+
* into persistence for the new session.
85+
*/
86+
public void setNewSession(String sessionId) {
87+
synchronized (sessionIdentifier) {
88+
sessionIdentifier = sessionId;
89+
backgroundWorker.submit(
90+
() -> {
91+
if (getUserId() != null) {
92+
metaDataStore.writeUserData(sessionIdentifier, getUserId());
93+
}
94+
// TODO(themis): adding feature rollouts later
95+
return null;
96+
});
97+
}
98+
}
99+
80100
@Nullable
81101
public String getUserId() {
82102
return userId.getReference();

0 commit comments

Comments
 (0)