@@ -27,7 +27,9 @@ import javax.inject.Inject
27
27
import javax.inject.Singleton
28
28
import kotlin.coroutines.CoroutineContext
29
29
import kotlinx.coroutines.CoroutineScope
30
+ import kotlinx.coroutines.flow.catch
30
31
import kotlinx.coroutines.launch
32
+ import org.jetbrains.annotations.VisibleForTesting
31
33
32
34
/* * Repository to persist session data to be shared between all app processes. */
33
35
internal interface SharedSessionRepository {
@@ -48,20 +50,37 @@ constructor(
48
50
@Background private val backgroundDispatcher: CoroutineContext ,
49
51
) : SharedSessionRepository {
50
52
/* * Local copy of the session data. Can get out of sync, must be double-checked in datastore. */
51
- private lateinit var localSessionData: SessionData
53
+ @VisibleForTesting lateinit var localSessionData: SessionData
54
+
55
+ /* *
56
+ * Either notify the subscribers with general multi-process supported session or fallback local
57
+ * session
58
+ */
59
+ private enum class NotificationType {
60
+ GENERAL ,
61
+ FALLBACK
62
+ }
52
63
53
64
init {
54
65
CoroutineScope (backgroundDispatcher).launch {
55
- sessionDataStore.data.collect { sessionData ->
56
- localSessionData = sessionData
57
- val sessionId = sessionData.sessionDetails.sessionId
58
-
59
- FirebaseSessionsDependencies .getRegisteredSubscribers().values.forEach { subscriber ->
60
- // Notify subscribers, regardless of sampling and data collection state
61
- subscriber.onSessionChanged(SessionSubscriber .SessionDetails (sessionId))
62
- Log .d(TAG , " Notified ${subscriber.sessionSubscriberName} of new session $sessionId " )
66
+ sessionDataStore.data
67
+ .catch {
68
+ val newSession =
69
+ SessionData (
70
+ sessionDetails = sessionGenerator.generateNewSession(null ),
71
+ backgroundTime = timeProvider.currentTime()
72
+ )
73
+ Log .d(
74
+ TAG ,
75
+ " Init session datastore failed with exception message: ${it.message} . Emit fallback session ${newSession.sessionDetails.sessionId} "
76
+ )
77
+ emit(newSession)
78
+ }
79
+ .collect { sessionData ->
80
+ localSessionData = sessionData
81
+ val sessionId = sessionData.sessionDetails.sessionId
82
+ notifySubscribers(sessionId, NotificationType .GENERAL )
63
83
}
64
- }
65
84
}
66
85
}
67
86
@@ -74,9 +93,14 @@ constructor(
74
93
Log .d(TAG , " App backgrounded on ${getProcessName()} - $sessionData " )
75
94
76
95
CoroutineScope (backgroundDispatcher).launch {
77
- sessionDataStore.updateData {
78
- // TODO(mrober): Double check time makes sense?
79
- sessionData.copy(backgroundTime = timeProvider.currentTime())
96
+ try {
97
+ sessionDataStore.updateData {
98
+ // TODO(mrober): Double check time makes sense?
99
+ sessionData.copy(backgroundTime = timeProvider.currentTime())
100
+ }
101
+ } catch (ex: Exception ) {
102
+ Log .d(TAG , " App backgrounded, failed to update data. Message: ${ex.message} " )
103
+ localSessionData = localSessionData.copy(backgroundTime = timeProvider.currentTime())
80
104
}
81
105
}
82
106
}
@@ -91,15 +115,43 @@ constructor(
91
115
92
116
if (shouldInitiateNewSession(sessionData)) {
93
117
CoroutineScope (backgroundDispatcher).launch {
94
- sessionDataStore.updateData { currentSessionData ->
95
- // Double-check pattern
96
- if (shouldInitiateNewSession(currentSessionData)) {
97
- val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
98
- sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
99
- currentSessionData.copy(sessionDetails = newSessionDetails)
100
- } else {
101
- currentSessionData
118
+ try {
119
+ sessionDataStore.updateData { currentSessionData ->
120
+ // Double-check pattern
121
+ if (shouldInitiateNewSession(currentSessionData)) {
122
+ val newSessionDetails =
123
+ sessionGenerator.generateNewSession(sessionData.sessionDetails)
124
+ sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
125
+ currentSessionData.copy(sessionDetails = newSessionDetails)
126
+ } else {
127
+ currentSessionData
128
+ }
102
129
}
130
+ } catch (ex: Exception ) {
131
+ Log .d(TAG , " App appForegrounded, failed to update data. Message: ${ex.message} " )
132
+ val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
133
+ localSessionData = localSessionData.copy(sessionDetails = newSessionDetails)
134
+ sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
135
+
136
+ val sessionId = newSessionDetails.sessionId
137
+ notifySubscribers(sessionId, NotificationType .FALLBACK )
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ private fun notifySubscribers (sessionId : String , type : NotificationType ) {
144
+ CoroutineScope (backgroundDispatcher).launch {
145
+ FirebaseSessionsDependencies .getRegisteredSubscribers().values.forEach { subscriber ->
146
+ // Notify subscribers, regardless of sampling and data collection state
147
+ subscriber.onSessionChanged(SessionSubscriber .SessionDetails (sessionId))
148
+ if (type == NotificationType .GENERAL ) {
149
+ Log .d(TAG , " Notified ${subscriber.sessionSubscriberName} of new session $sessionId " )
150
+ } else {
151
+ Log .d(
152
+ TAG ,
153
+ " Notified ${subscriber.sessionSubscriberName} of new fallback session $sessionId "
154
+ )
103
155
}
104
156
}
105
157
}
0 commit comments