@@ -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,38 @@ 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 {
65
+ println (" session repo init" )
54
66
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 " )
67
+ sessionDataStore.data
68
+ .catch {
69
+ val newSession =
70
+ SessionData (
71
+ sessionDetails = sessionGenerator.generateNewSession(null ),
72
+ backgroundTime = timeProvider.currentTime()
73
+ )
74
+ Log .d(
75
+ TAG ,
76
+ " Init session datastore failed with exception message: ${it.message} . Emit fallback session ${newSession.sessionDetails.sessionId} "
77
+ )
78
+ emit(newSession)
79
+ }
80
+ .collect { sessionData ->
81
+ localSessionData = sessionData
82
+ val sessionId = sessionData.sessionDetails.sessionId
83
+ notifySubscribers(sessionId, NotificationType .GENERAL )
63
84
}
64
- }
65
85
}
66
86
}
67
87
@@ -74,9 +94,14 @@ constructor(
74
94
Log .d(TAG , " App backgrounded on ${getProcessName()} - $sessionData " )
75
95
76
96
CoroutineScope (backgroundDispatcher).launch {
77
- sessionDataStore.updateData {
78
- // TODO(mrober): Double check time makes sense?
79
- sessionData.copy(backgroundTime = timeProvider.currentTime())
97
+ try {
98
+ sessionDataStore.updateData {
99
+ // TODO(mrober): Double check time makes sense?
100
+ sessionData.copy(backgroundTime = timeProvider.currentTime())
101
+ }
102
+ } catch (ex: Exception ) {
103
+ Log .d(TAG , " App backgrounded, failed to update data. Message: ${ex.message} " )
104
+ localSessionData = localSessionData.copy(backgroundTime = timeProvider.currentTime())
80
105
}
81
106
}
82
107
}
@@ -91,20 +116,47 @@ constructor(
91
116
92
117
if (shouldInitiateNewSession(sessionData)) {
93
118
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
119
+ try {
120
+ sessionDataStore.updateData { currentSessionData ->
121
+ // Double-check pattern
122
+ if (shouldInitiateNewSession(currentSessionData)) {
123
+ val newSessionDetails =
124
+ sessionGenerator.generateNewSession(sessionData.sessionDetails)
125
+ sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
126
+ currentSessionData.copy(sessionDetails = newSessionDetails)
127
+ } else {
128
+ currentSessionData
129
+ }
102
130
}
131
+ } catch (ex: Exception ) {
132
+ Log .d(TAG , " App appForegrounded, failed to update data. Message: ${ex.message} " )
133
+ val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
134
+ localSessionData = localSessionData.copy(sessionDetails = newSessionDetails)
135
+ sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
136
+
137
+ val sessionId = newSessionDetails.sessionId
138
+ notifySubscribers(sessionId, NotificationType .FALLBACK )
103
139
}
104
140
}
105
141
}
106
142
}
107
143
144
+ private suspend fun notifySubscribers (sessionId : String , type : NotificationType ) {
145
+ FirebaseSessionsDependencies .getRegisteredSubscribers().values.forEach { subscriber ->
146
+ // Notify subscribers, regardless of sampling and data collection state
147
+ subscriber.onSessionChanged(SessionSubscriber .SessionDetails (sessionId))
148
+ when (type) {
149
+ NotificationType .GENERAL ->
150
+ Log .d(TAG , " Notified ${subscriber.sessionSubscriberName} of new session $sessionId " )
151
+ NotificationType .FALLBACK ->
152
+ Log .d(
153
+ TAG ,
154
+ " Notified ${subscriber.sessionSubscriberName} of new fallback session $sessionId "
155
+ )
156
+ }
157
+ }
158
+ }
159
+
108
160
private fun shouldInitiateNewSession (sessionData : SessionData ): Boolean {
109
161
val interval = timeProvider.currentTime() - sessionData.backgroundTime
110
162
return interval > sessionsSettings.sessionRestartTimeout
0 commit comments