Skip to content

Commit f548589

Browse files
authored
Merge 32ae332 into 248fd92
2 parents 248fd92 + 32ae332 commit f548589

File tree

8 files changed

+137
-87
lines changed

8 files changed

+137
-87
lines changed

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/ProcessDataManager.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ constructor(private val appContext: Context, uuidGenerator: UuidGenerator) : Pro
6363

6464
override val myUuid: String by lazy { uuidGenerator.next().toString() }
6565

66-
private val myProcessDetails by lazy {
67-
ProcessDetailsProvider.getCurrentProcessDetails(appContext)
68-
}
66+
private val myProcessDetails by lazy { ProcessDetailsProvider.getMyProcessDetails(appContext) }
6967

7068
private var hasGeneratedSession: Boolean = false
7169

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/ProcessDetailsProvider.kt

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,9 @@ import android.os.Build
2323
import android.os.Process
2424
import com.google.android.gms.common.util.ProcessUtils
2525

26-
/**
27-
* Provider of ProcessDetails.
28-
*
29-
* @hide
30-
*/
26+
/** Provide [ProcessDetails] for all app processes. */
3127
internal object ProcessDetailsProvider {
32-
/** Gets the details for all of this app's running processes. */
28+
/** Gets the details for all the app's running processes. */
3329
fun getAppProcessDetails(context: Context): List<ProcessDetails> {
3430
val appUid = context.applicationInfo.uid
3531
val defaultProcessName = context.applicationInfo.processName
@@ -53,27 +49,19 @@ internal object ProcessDetailsProvider {
5349
}
5450

5551
/**
56-
* Gets this app's current process details.
52+
* Gets this process's details.
5753
*
58-
* If the current process details are not found for whatever reason, returns process details with
59-
* just the current process name and pid set.
54+
* If this process's full details are not found for whatever reason, returns process details with
55+
* just the process name and pid set.
6056
*/
61-
fun getCurrentProcessDetails(context: Context): ProcessDetails {
57+
fun getMyProcessDetails(context: Context): ProcessDetails {
6258
val pid = Process.myPid()
6359
return getAppProcessDetails(context).find { it.pid == pid }
64-
?: buildProcessDetails(getProcessName(), pid)
60+
?: ProcessDetails(getProcessName(), pid, importance = 0, isDefaultProcess = false)
6561
}
6662

67-
/** Builds a ProcessDetails object. */
68-
private fun buildProcessDetails(
69-
processName: String,
70-
pid: Int = 0,
71-
importance: Int = 0,
72-
isDefaultProcess: Boolean = false
73-
) = ProcessDetails(processName, pid, importance, isDefaultProcess)
74-
7563
/** Gets the app's current process name. If it could not be found, returns an empty string. */
76-
internal fun getProcessName(): String {
64+
private fun getProcessName(): String {
7765
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) {
7866
return Process.myProcessName()
7967
}

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionEvents.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ internal object SessionEvents {
8383
versionName = packageInfo.versionName ?: buildVersion,
8484
appBuildVersion = buildVersion,
8585
deviceManufacturer = Build.MANUFACTURER,
86-
ProcessDetailsProvider.getCurrentProcessDetails(firebaseApp.applicationContext),
86+
ProcessDetailsProvider.getMyProcessDetails(firebaseApp.applicationContext),
8787
ProcessDetailsProvider.getAppProcessDetails(firebaseApp.applicationContext),
8888
),
8989
)

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SharedSessionRepository.kt

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package com.google.firebase.sessions
1919
import android.util.Log
2020
import androidx.datastore.core.DataStore
2121
import com.google.firebase.annotations.concurrent.Background
22-
import com.google.firebase.sessions.ProcessDetailsProvider.getProcessName
2322
import com.google.firebase.sessions.api.FirebaseSessionsDependencies
2423
import com.google.firebase.sessions.api.SessionSubscriber
2524
import com.google.firebase.sessions.settings.SessionsSettings
@@ -92,7 +91,7 @@ constructor(
9291
return
9392
}
9493
val sessionData = localSessionData
95-
Log.d(TAG, "App backgrounded on ${getProcessName()} - $sessionData")
94+
Log.d(TAG, "App backgrounded on ${processDataManager.myProcessName} - $sessionData")
9695

9796
CoroutineScope(backgroundDispatcher).launch {
9897
try {
@@ -113,32 +112,58 @@ constructor(
113112
return
114113
}
115114
val sessionData = localSessionData
116-
Log.d(TAG, "App foregrounded on ${getProcessName()} - $sessionData")
115+
Log.d(TAG, "App foregrounded on ${processDataManager.myProcessName} - $sessionData")
117116

118-
if (shouldInitiateNewSession(sessionData)) {
117+
// Check if maybe the session data needs to be updated
118+
if (isSessionExpired(sessionData) || isMyProcessStale(sessionData)) {
119119
CoroutineScope(backgroundDispatcher).launch {
120120
try {
121121
sessionDataStore.updateData { currentSessionData ->
122-
// Double-check pattern
123-
if (shouldInitiateNewSession(currentSessionData)) {
122+
// Check again using the current session data on disk
123+
val isSessionExpired = isSessionExpired(currentSessionData)
124+
val isColdStart = isColdStart(currentSessionData)
125+
val isMyProcessStale = isMyProcessStale(currentSessionData)
126+
127+
val newProcessDataMap =
128+
if (isColdStart) {
129+
// Generate a new process data map for cold app start
130+
processDataManager.generateProcessDataMap()
131+
} else if (isMyProcessStale) {
132+
// Update the data map with this process if stale
133+
processDataManager.updateProcessDataMap(currentSessionData.processDataMap)
134+
} else {
135+
// No change
136+
currentSessionData.processDataMap
137+
}
138+
139+
// This is an expression, and returns the updated session data
140+
if (isSessionExpired || isColdStart) {
124141
val newSessionDetails =
125-
sessionGenerator.generateNewSession(sessionData.sessionDetails)
142+
sessionGenerator.generateNewSession(currentSessionData.sessionDetails)
126143
sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
127144
processDataManager.onSessionGenerated()
128-
currentSessionData.copy(sessionDetails = newSessionDetails, backgroundTime = null)
145+
currentSessionData.copy(
146+
sessionDetails = newSessionDetails,
147+
backgroundTime = null,
148+
processDataMap = newProcessDataMap,
149+
)
150+
} else if (isMyProcessStale) {
151+
currentSessionData.copy(
152+
processDataMap = processDataManager.updateProcessDataMap(newProcessDataMap)
153+
)
129154
} else {
130155
currentSessionData
131156
}
132157
}
133158
} catch (ex: Exception) {
134159
Log.d(TAG, "App appForegrounded, failed to update data. Message: ${ex.message}")
135-
val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
136-
localSessionData =
137-
localSessionData.copy(sessionDetails = newSessionDetails, backgroundTime = null)
138-
sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
139-
140-
val sessionId = newSessionDetails.sessionId
141-
notifySubscribers(sessionId, NotificationType.FALLBACK)
160+
if (isSessionExpired(sessionData)) {
161+
val newSessionDetails = sessionGenerator.generateNewSession(sessionData.sessionDetails)
162+
localSessionData =
163+
sessionData.copy(sessionDetails = newSessionDetails, backgroundTime = null)
164+
sessionFirelogPublisher.mayLogSession(sessionDetails = newSessionDetails)
165+
notifySubscribers(newSessionDetails.sessionId, NotificationType.FALLBACK)
166+
}
142167
}
143168
}
144169
}
@@ -161,22 +186,47 @@ constructor(
161186
}
162187
}
163188

164-
private fun shouldInitiateNewSession(sessionData: SessionData): Boolean {
189+
/** Checks if the session has expired. If no background time, consider it not expired. */
190+
private fun isSessionExpired(sessionData: SessionData): Boolean {
165191
sessionData.backgroundTime?.let { backgroundTime ->
166192
val interval = timeProvider.currentTime() - backgroundTime
167-
if (interval > sessionsSettings.sessionRestartTimeout) {
168-
Log.d(TAG, "Passed session restart timeout, so initiate a new session")
169-
return true
193+
val sessionExpired = (interval > sessionsSettings.sessionRestartTimeout)
194+
if (sessionExpired) {
195+
Log.d(TAG, "Session ${sessionData.sessionDetails.sessionId} is expired")
170196
}
197+
return sessionExpired
171198
}
172199

200+
Log.d(TAG, "Session ${sessionData.sessionDetails.sessionId} has not backgrounded yet")
201+
return false
202+
}
203+
204+
/** Checks for cold app start. If no process data map, consider it a cold start. */
205+
private fun isColdStart(sessionData: SessionData): Boolean {
173206
sessionData.processDataMap?.let { processDataMap ->
174-
Log.d(TAG, "Has not passed session restart timeout, so check for cold app start")
175-
return processDataManager.isColdStart(processDataMap)
207+
val coldStart = processDataManager.isColdStart(processDataMap)
208+
if (coldStart) {
209+
Log.d(TAG, "Cold app start detected")
210+
}
211+
return coldStart
176212
}
177213

178-
Log.d(TAG, "No process has backgrounded yet and no process data, should not change the session")
179-
return false
214+
Log.d(TAG, "No process data map")
215+
return true
216+
}
217+
218+
/** Checks if this process is stale. If no process data map, consider the process stale. */
219+
private fun isMyProcessStale(sessionData: SessionData): Boolean {
220+
sessionData.processDataMap?.let { processDataMap ->
221+
val myProcessStale = processDataManager.isMyProcessStale(processDataMap)
222+
if (myProcessStale) {
223+
Log.d(TAG, "Process ${processDataManager.myProcessName} is stale")
224+
}
225+
return myProcessStale
226+
}
227+
228+
Log.d(TAG, "No process data for ${processDataManager.myProcessName}")
229+
return true
180230
}
181231

182232
private companion object {

firebase-sessions/src/test/kotlin/com/google/firebase/sessions/ApplicationInfoTest.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ class ApplicationInfoTest {
3636
@Test
3737
fun applicationInfo_populatesInfoCorrectly() {
3838
val firebaseApp = FakeFirebaseApp().firebaseApp
39-
val actualCurrentProcessDetails =
40-
ProcessDetailsProvider.getCurrentProcessDetails(firebaseApp.applicationContext)
41-
val actualAppProcessDetails =
39+
val myProcessDetails =
40+
ProcessDetailsProvider.getMyProcessDetails(firebaseApp.applicationContext)
41+
val appProcessDetails =
4242
ProcessDetailsProvider.getAppProcessDetails(firebaseApp.applicationContext)
4343
val applicationInfo = SessionEvents.getApplicationInfo(firebaseApp)
4444
assertThat(applicationInfo)
@@ -54,8 +54,8 @@ class ApplicationInfoTest {
5454
versionName = FakeFirebaseApp.MOCK_APP_VERSION,
5555
appBuildVersion = FakeFirebaseApp.MOCK_APP_BUILD_VERSION,
5656
deviceManufacturer = Build.MANUFACTURER,
57-
actualCurrentProcessDetails,
58-
actualAppProcessDetails,
57+
myProcessDetails,
58+
appProcessDetails,
5959
),
6060
)
6161
)
@@ -74,9 +74,9 @@ class ApplicationInfoTest {
7474
.build(),
7575
)
7676

77-
val actualCurrentProcessDetails =
78-
ProcessDetailsProvider.getCurrentProcessDetails(firebaseApp.applicationContext)
79-
val actualAppProcessDetails =
77+
val myProcessDetails =
78+
ProcessDetailsProvider.getMyProcessDetails(firebaseApp.applicationContext)
79+
val appProcessDetails =
8080
ProcessDetailsProvider.getAppProcessDetails(firebaseApp.applicationContext)
8181

8282
val applicationInfo = SessionEvents.getApplicationInfo(firebaseApp)
@@ -94,8 +94,8 @@ class ApplicationInfoTest {
9494
versionName = "0",
9595
appBuildVersion = "0",
9696
deviceManufacturer = Build.MANUFACTURER,
97-
actualCurrentProcessDetails,
98-
actualAppProcessDetails,
97+
myProcessDetails,
98+
appProcessDetails,
9999
),
100100
)
101101
)

0 commit comments

Comments
 (0)