Skip to content

Commit 681baf2

Browse files
authored
Merge 9982ffc into 5191689
2 parents 5191689 + 9982ffc commit 681baf2

19 files changed

+844
-710
lines changed

firebase-sessions/firebase-sessions.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
@file:Suppress("UnstableApiUsage")
18+
1719
plugins {
1820
id("firebase-library")
1921
id("kotlin-android")
@@ -45,7 +47,7 @@ android {
4547
dependencies {
4648
implementation("androidx.datastore:datastore-preferences:1.0.0")
4749
implementation("com.google.android.datatransport:transport-api:3.0.0")
48-
implementation("com.google.firebase:firebase-common-ktx:20.3.2")
50+
implementation("com.google.firebase:firebase-common-ktx:20.3.3")
4951
implementation("com.google.firebase:firebase-components:17.1.0")
5052
implementation("com.google.firebase:firebase-encoders-json:18.0.1")
5153
implementation("com.google.firebase:firebase-encoders:17.0.0")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ internal constructor(
143143

144144
/** Calculate whether we should sample events using [sessionSettings] data. */
145145
private fun shouldCollectEvents(): Boolean {
146-
// Sampling rate of 1 means we do not sample.
146+
// Sampling rate of 1 means the SDK will send every event.
147147
val randomValue = Math.random()
148148
return randomValue <= sessionSettings.samplingRate
149149
}

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/LocalOverrideSettings.kt

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,59 +18,61 @@ package com.google.firebase.sessions.settings
1818

1919
import android.content.Context
2020
import android.content.pm.PackageManager
21+
import android.os.Build
22+
import android.os.Bundle
2123
import kotlin.time.Duration
2224
import kotlin.time.DurationUnit
2325
import kotlin.time.toDuration
2426

25-
internal class LocalOverrideSettings(val context: Context) : SettingsProvider {
26-
27-
private val sessions_metadata_flag_sessionsEnabled = "firebase_sessions_enabled"
28-
private val sessions_metadata_flag_sessionRestartTimeout =
29-
"firebase_sessions_sessions_restart_timeout"
30-
private val sessions_metadata_flag_samplingRate = "firebase_sessions_sampling_rate"
27+
internal class LocalOverrideSettings(context: Context) : SettingsProvider {
3128
private val metadata =
32-
context.packageManager
33-
.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
34-
.metaData
29+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
30+
context.packageManager
31+
.getApplicationInfo(
32+
context.packageName,
33+
PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong()),
34+
)
35+
.metaData
36+
} else {
37+
@Suppress("DEPRECATION") // For older API levels.
38+
context.packageManager
39+
.getApplicationInfo(
40+
context.packageName,
41+
PackageManager.GET_META_DATA,
42+
)
43+
.metaData
44+
}
45+
// Default to an empty bundle, meaning no cached values.
46+
?: Bundle.EMPTY
3547

3648
override val sessionEnabled: Boolean?
37-
get() {
38-
metadata?.let {
39-
if (it.containsKey(sessions_metadata_flag_sessionsEnabled)) {
40-
return it.getBoolean(sessions_metadata_flag_sessionsEnabled)
41-
}
49+
get() =
50+
if (metadata.containsKey(SESSIONS_ENABLED)) {
51+
metadata.getBoolean(SESSIONS_ENABLED)
52+
} else {
53+
null
4254
}
43-
return null
44-
}
4555

4656
override val sessionRestartTimeout: Duration?
47-
get() {
48-
metadata?.let {
49-
if (it.containsKey(sessions_metadata_flag_sessionRestartTimeout)) {
50-
val timeoutInSeconds = it.getInt(sessions_metadata_flag_sessionRestartTimeout)
51-
val duration = timeoutInSeconds.toDuration(DurationUnit.SECONDS)
52-
return duration
53-
}
57+
get() =
58+
if (metadata.containsKey(SESSION_RESTART_TIMEOUT)) {
59+
val timeoutInSeconds = metadata.getInt(SESSION_RESTART_TIMEOUT)
60+
timeoutInSeconds.toDuration(DurationUnit.SECONDS)
61+
} else {
62+
null
5463
}
55-
return null
56-
}
5764

5865
override val samplingRate: Double?
59-
get() {
60-
metadata?.let {
61-
if (it.containsKey(sessions_metadata_flag_samplingRate)) {
62-
return it.getDouble(sessions_metadata_flag_samplingRate)
63-
}
66+
get() =
67+
if (metadata.containsKey(SAMPLING_RATE)) {
68+
metadata.getDouble(SAMPLING_RATE)
69+
} else {
70+
null
6471
}
65-
return null
66-
}
67-
68-
override fun updateSettings() {
69-
// Nothing to be done here since there is nothing to be updated.
70-
}
7172

72-
override fun isSettingsStale(): Boolean {
73-
// Settings are never stale since all of these are from Manifest file.
74-
return false
73+
private companion object {
74+
const val SESSIONS_ENABLED = "firebase_sessions_enabled"
75+
const val SESSION_RESTART_TIMEOUT = "firebase_sessions_sessions_restart_timeout"
76+
const val SAMPLING_RATE = "firebase_sessions_sampling_rate"
7577
}
7678
}

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/RemoteSettings.kt

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
package com.google.firebase.sessions.settings
1818

19-
import android.content.Context
2019
import android.os.Build
2120
import android.util.Log
22-
import androidx.datastore.preferences.preferencesDataStore
21+
import androidx.annotation.VisibleForTesting
22+
import androidx.datastore.core.DataStore
23+
import androidx.datastore.preferences.core.Preferences
2324
import com.google.firebase.installations.FirebaseInstallationsApi
2425
import com.google.firebase.sessions.ApplicationInfo
2526
import java.util.concurrent.atomic.AtomicBoolean
@@ -33,19 +34,14 @@ import org.json.JSONException
3334
import org.json.JSONObject
3435

3536
internal class RemoteSettings(
36-
context: Context,
37-
blockingDispatcher: CoroutineContext,
3837
private val backgroundDispatcher: CoroutineContext,
3938
private val firebaseInstallationsApi: FirebaseInstallationsApi,
4039
private val appInfo: ApplicationInfo,
41-
private val configsFetcher: CrashlyticsSettingsFetcher =
42-
RemoteSettingsFetcher(appInfo, blockingDispatcher),
43-
dataStoreName: String = SESSION_CONFIGS_NAME,
40+
private val configsFetcher: CrashlyticsSettingsFetcher,
41+
dataStore: DataStore<Preferences>,
4442
) : SettingsProvider {
45-
private val Context.dataStore by
46-
preferencesDataStore(name = dataStoreName, scope = CoroutineScope(backgroundDispatcher))
47-
private val settingsCache = SettingsCache(context.dataStore)
48-
private var fetchInProgress = AtomicBoolean(false)
43+
private val settingsCache = SettingsCache(dataStore)
44+
private val fetchInProgress = AtomicBoolean(false)
4945

5046
override val sessionEnabled: Boolean?
5147
get() = settingsCache.sessionsEnabled()
@@ -63,6 +59,7 @@ internal class RemoteSettings(
6359

6460
override fun isSettingsStale(): Boolean = settingsCache.hasCacheExpired()
6561

62+
@VisibleForTesting
6663
internal fun clearCachedSettings() {
6764
val scope = CoroutineScope(backgroundDispatcher)
6865
scope.launch { settingsCache.removeConfigs() }
@@ -81,6 +78,7 @@ internal class RemoteSettings(
8178

8279
fetchInProgress.set(true)
8380

81+
// TODO(mrober): Avoid sending the fid here, and avoid fetching it when data collection is off.
8482
// Get the installations ID before making a remote config fetch
8583
val installationId = firebaseInstallationsApi.id.await()
8684
if (installationId == null) {
@@ -155,9 +153,9 @@ internal class RemoteSettings(
155153
return s.replace(FORWARD_SLASH_STRING.toRegex(), "")
156154
}
157155

158-
companion object {
159-
private const val SESSION_CONFIGS_NAME = "firebase_session_settings"
160-
private const val TAG = "SessionConfigFetcher"
161-
private const val FORWARD_SLASH_STRING: String = "/"
156+
private companion object {
157+
const val TAG = "SessionConfigFetcher"
158+
159+
const val FORWARD_SLASH_STRING: String = "/"
162160
}
163161
}

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SessionsSettings.kt

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,48 @@
1717
package com.google.firebase.sessions.settings
1818

1919
import android.content.Context
20+
import androidx.datastore.core.DataStore
21+
import androidx.datastore.preferences.core.Preferences
22+
import androidx.datastore.preferences.preferencesDataStore
2023
import com.google.firebase.installations.FirebaseInstallationsApi
2124
import com.google.firebase.sessions.ApplicationInfo
2225
import kotlin.coroutines.CoroutineContext
2326
import kotlin.time.Duration
2427
import kotlin.time.Duration.Companion.minutes
2528

26-
/**
27-
* [SessionsSettings] manages all the configs that are relevant to the sessions library.
28-
*
29-
* @hide
30-
*/
29+
/** [SessionsSettings] manages all the configs that are relevant to the sessions library. */
3130
internal class SessionsSettings(
32-
val context: Context,
33-
val blockingDispatcher: CoroutineContext,
34-
val backgroundDispatcher: CoroutineContext,
35-
val firebaseInstallationsApi: FirebaseInstallationsApi,
36-
val appInfo: ApplicationInfo,
37-
private val localOverrideSettings: LocalOverrideSettings = LocalOverrideSettings(context),
38-
private val remoteSettings: RemoteSettings =
39-
RemoteSettings(
40-
context,
41-
blockingDispatcher,
42-
backgroundDispatcher,
43-
firebaseInstallationsApi,
44-
appInfo
45-
)
31+
private val localOverrideSettings: SettingsProvider,
32+
private val remoteSettings: SettingsProvider,
4633
) {
34+
constructor(
35+
context: Context,
36+
blockingDispatcher: CoroutineContext,
37+
backgroundDispatcher: CoroutineContext,
38+
firebaseInstallationsApi: FirebaseInstallationsApi,
39+
appInfo: ApplicationInfo,
40+
) : this(
41+
localOverrideSettings = LocalOverrideSettings(context),
42+
remoteSettings =
43+
RemoteSettings(
44+
backgroundDispatcher,
45+
firebaseInstallationsApi,
46+
appInfo,
47+
configsFetcher =
48+
RemoteSettingsFetcher(
49+
appInfo,
50+
blockingDispatcher,
51+
),
52+
dataStore = context.dataStore,
53+
),
54+
)
55+
4756
// Order of preference for all the configs below:
4857
// 1. Honor local overrides
4958
// 2. If no local overrides, use remote config
5059
// 3. If no remote config, fall back to SDK defaults.
5160

52-
// Setting to qualify if sessions service is enabled.
61+
/** Setting to qualify if sessions service is enabled. */
5362
val sessionsEnabled: Boolean
5463
get() {
5564
localOverrideSettings.sessionEnabled?.let {
@@ -62,36 +71,56 @@ internal class SessionsSettings(
6271
return true
6372
}
6473

65-
// Setting that provides the sessions sampling rate.
74+
/** Setting that provides the sessions sampling rate. */
6675
val samplingRate: Double
6776
get() {
6877
localOverrideSettings.samplingRate?.let {
69-
return it
78+
if (isValidSamplingRate(it)) {
79+
return it
80+
}
7081
}
7182
remoteSettings.samplingRate?.let {
72-
return it
83+
if (isValidSamplingRate(it)) {
84+
return it
85+
}
7386
}
7487
// SDK Default
7588
return 1.0
7689
}
7790

78-
// Background timeout config value before which a new session is generated
91+
/** Background timeout config value before which a new session is generated. */
7992
val sessionRestartTimeout: Duration
8093
get() {
8194
localOverrideSettings.sessionRestartTimeout?.let {
82-
return it
95+
if (isValidSessionRestartTimeout(it)) {
96+
return it
97+
}
8398
}
8499
remoteSettings.sessionRestartTimeout?.let {
85-
return it
100+
if (isValidSessionRestartTimeout(it)) {
101+
return it
102+
}
86103
}
87104
// SDK Default
88105
return 30.minutes
89106
}
90107

91-
// Update the settings for all the settings providers
108+
private fun isValidSamplingRate(samplingRate: Double): Boolean = samplingRate in 0.0..1.0
109+
110+
private fun isValidSessionRestartTimeout(sessionRestartTimeout: Duration): Boolean {
111+
return sessionRestartTimeout.isPositive() && sessionRestartTimeout.isFinite()
112+
}
113+
114+
/** Update the settings for all the settings providers. */
92115
fun updateSettings() {
93-
// Placeholder to initiate settings update on different sources
94116
localOverrideSettings.updateSettings()
95117
remoteSettings.updateSettings()
96118
}
119+
120+
private companion object {
121+
const val SESSION_CONFIGS_NAME = "firebase_session_settings"
122+
123+
private val Context.dataStore: DataStore<Preferences> by
124+
preferencesDataStore(name = SESSION_CONFIGS_NAME)
125+
}
97126
}

0 commit comments

Comments
 (0)