Skip to content

Commit c1ffa6f

Browse files
mrobertejasd
authored andcommitted
Use Dagger for dependency injection in Sessions (#6745)
1 parent 04b1e21 commit c1ffa6f

14 files changed

+238
-124
lines changed

firebase-sessions/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
* [changed] Use Dagger for dependency injection
34
* [changed] Updated datastore dependency to `1.1.3` to
45
fix [CVE-2024-7254](https://github.com/advisories/GHSA-735f-pc8j-v9w8).
56

firebase-sessions/firebase-sessions.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
plugins {
2020
id("firebase-library")
21+
id("firebase-vendor")
2122
id("kotlin-android")
2223
id("kotlin-kapt")
2324
}
@@ -67,12 +68,18 @@ dependencies {
6768
exclude(group = "com.google.firebase", module = "firebase-common")
6869
exclude(group = "com.google.firebase", module = "firebase-components")
6970
}
70-
implementation("com.google.android.datatransport:transport-api:3.2.0")
71+
7172
api("com.google.firebase:firebase-annotations:16.2.0")
7273
api("com.google.firebase:firebase-encoders:17.0.0")
7374
api("com.google.firebase:firebase-encoders-json:18.0.1")
75+
76+
implementation("com.google.android.datatransport:transport-api:3.2.0")
77+
implementation(libs.javax.inject)
7478
implementation(libs.androidx.annotation)
7579
implementation(libs.androidx.datastore.preferences)
80+
81+
vendor(libs.dagger.dagger) { exclude(group = "javax.inject", module = "javax.inject") }
82+
7683
compileOnly(libs.errorprone.annotations)
7784

7885
runtimeOnly("com.google.firebase:firebase-installations:18.0.0") {
@@ -85,6 +92,7 @@ dependencies {
8592
}
8693

8794
kapt(project(":encoders:firebase-encoders-processor"))
95+
kapt(libs.dagger.compiler)
8896

8997
testImplementation(project(":integ-testing")) {
9098
exclude(group = "com.google.firebase", module = "firebase-common")

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import com.google.android.datatransport.Encoding
2121
import com.google.android.datatransport.Event
2222
import com.google.android.datatransport.TransportFactory
2323
import com.google.firebase.inject.Provider
24+
import javax.inject.Inject
25+
import javax.inject.Singleton
2426

2527
/**
2628
* The [EventGDTLoggerInterface] is for testing purposes so that we can mock EventGDTLogger in other
@@ -38,19 +40,17 @@ internal fun interface EventGDTLoggerInterface {
3840
*
3941
* @hide
4042
*/
41-
internal class EventGDTLogger(private val transportFactoryProvider: Provider<TransportFactory>) :
43+
@Singleton
44+
internal class EventGDTLogger
45+
@Inject
46+
constructor(private val transportFactoryProvider: Provider<TransportFactory>) :
4247
EventGDTLoggerInterface {
4348

4449
// Logs a [SessionEvent] to FireLog
4550
override fun log(sessionEvent: SessionEvent) {
4651
transportFactoryProvider
4752
.get()
48-
.getTransport(
49-
AQS_LOG_SOURCE,
50-
SessionEvent::class.java,
51-
Encoding.of("json"),
52-
this::encode,
53-
)
53+
.getTransport(AQS_LOG_SOURCE, SessionEvent::class.java, Encoding.of("json"), this::encode)
5454
.send(Event.ofData(sessionEvent))
5555
}
5656

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,24 @@ import android.app.Application
2020
import android.util.Log
2121
import com.google.firebase.Firebase
2222
import com.google.firebase.FirebaseApp
23+
import com.google.firebase.annotations.concurrent.Background
2324
import com.google.firebase.app
2425
import com.google.firebase.sessions.api.FirebaseSessionsDependencies
2526
import com.google.firebase.sessions.settings.SessionsSettings
27+
import javax.inject.Inject
28+
import javax.inject.Singleton
2629
import kotlin.coroutines.CoroutineContext
2730
import kotlinx.coroutines.CoroutineScope
2831
import kotlinx.coroutines.launch
2932

3033
/** Responsible for initializing AQS */
31-
internal class FirebaseSessions(
34+
@Singleton
35+
internal class FirebaseSessions
36+
@Inject
37+
constructor(
3238
private val firebaseApp: FirebaseApp,
3339
private val settings: SessionsSettings,
34-
backgroundDispatcher: CoroutineContext,
40+
@Background backgroundDispatcher: CoroutineContext,
3541
lifecycleServiceBinder: SessionLifecycleServiceBinder,
3642
) {
3743

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.sessions
18+
19+
import android.content.Context
20+
import com.google.android.datatransport.TransportFactory
21+
import com.google.firebase.FirebaseApp
22+
import com.google.firebase.annotations.concurrent.Background
23+
import com.google.firebase.annotations.concurrent.Blocking
24+
import com.google.firebase.inject.Provider
25+
import com.google.firebase.installations.FirebaseInstallationsApi
26+
import com.google.firebase.sessions.settings.SessionsSettings
27+
import dagger.Binds
28+
import dagger.BindsInstance
29+
import dagger.Component
30+
import dagger.Module
31+
import dagger.Provides
32+
import javax.inject.Singleton
33+
import kotlin.coroutines.CoroutineContext
34+
35+
/** Dagger component to provide [FirebaseSessions] and its dependencies. */
36+
@Singleton
37+
@Component(modules = [FirebaseSessionsComponent.MainModule::class])
38+
internal interface FirebaseSessionsComponent {
39+
val firebaseSessions: FirebaseSessions
40+
41+
val sessionDatastore: SessionDatastore
42+
val sessionFirelogPublisher: SessionFirelogPublisher
43+
val sessionGenerator: SessionGenerator
44+
val sessionsSettings: SessionsSettings
45+
46+
@Component.Builder
47+
interface Builder {
48+
@BindsInstance fun appContext(appContext: Context): Builder
49+
50+
@BindsInstance
51+
fun backgroundDispatcher(@Background backgroundDispatcher: CoroutineContext): Builder
52+
53+
@BindsInstance fun blockingDispatcher(@Blocking blockingDispatcher: CoroutineContext): Builder
54+
55+
@BindsInstance fun firebaseApp(firebaseApp: FirebaseApp): Builder
56+
57+
@BindsInstance
58+
fun firebaseInstallationsApi(firebaseInstallationsApi: FirebaseInstallationsApi): Builder
59+
60+
@BindsInstance
61+
fun transportFactoryProvider(transportFactoryProvider: Provider<TransportFactory>): Builder
62+
63+
fun build(): FirebaseSessionsComponent
64+
}
65+
66+
@Module
67+
interface MainModule {
68+
@Binds @Singleton fun eventGDTLoggerInterface(impl: EventGDTLogger): EventGDTLoggerInterface
69+
70+
@Binds @Singleton fun sessionDatastore(impl: SessionDatastoreImpl): SessionDatastore
71+
72+
@Binds
73+
@Singleton
74+
fun sessionFirelogPublisher(impl: SessionFirelogPublisherImpl): SessionFirelogPublisher
75+
76+
@Binds
77+
@Singleton
78+
fun sessionLifecycleServiceBinder(
79+
impl: SessionLifecycleServiceBinderImpl
80+
): SessionLifecycleServiceBinder
81+
82+
companion object {
83+
@Provides @Singleton fun sessionGenerator() = SessionGenerator(timeProvider = WallClock)
84+
}
85+
}
86+
}

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

Lines changed: 24 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.firebase.sessions
1818

19+
import android.content.Context
1920
import androidx.annotation.Keep
2021
import com.google.android.datatransport.TransportFactory
2122
import com.google.firebase.FirebaseApp
@@ -28,7 +29,6 @@ import com.google.firebase.components.Qualified.qualified
2829
import com.google.firebase.components.Qualified.unqualified
2930
import com.google.firebase.installations.FirebaseInstallationsApi
3031
import com.google.firebase.platforminfo.LibraryVersionComponent
31-
import com.google.firebase.sessions.settings.SessionsSettings
3232
import kotlinx.coroutines.CoroutineDispatcher
3333

3434
/**
@@ -42,87 +42,41 @@ internal class FirebaseSessionsRegistrar : ComponentRegistrar {
4242
listOf(
4343
Component.builder(FirebaseSessions::class.java)
4444
.name(LIBRARY_NAME)
45-
.add(Dependency.required(firebaseApp))
46-
.add(Dependency.required(sessionsSettings))
47-
.add(Dependency.required(backgroundDispatcher))
48-
.add(Dependency.required(sessionLifecycleServiceBinder))
49-
.factory { container ->
50-
FirebaseSessions(
51-
container[firebaseApp],
52-
container[sessionsSettings],
53-
container[backgroundDispatcher],
54-
container[sessionLifecycleServiceBinder],
55-
)
56-
}
45+
.add(Dependency.required(firebaseSessionsComponent))
46+
.factory { container -> container[firebaseSessionsComponent].firebaseSessions }
5747
.eagerInDefaultApp()
5848
.build(),
59-
Component.builder(SessionGenerator::class.java)
60-
.name("session-generator")
61-
.factory { SessionGenerator(timeProvider = WallClock) }
62-
.build(),
63-
Component.builder(SessionFirelogPublisher::class.java)
64-
.name("session-publisher")
65-
.add(Dependency.required(firebaseApp))
66-
.add(Dependency.required(firebaseInstallationsApi))
67-
.add(Dependency.required(sessionsSettings))
68-
.add(Dependency.requiredProvider(transportFactory))
49+
Component.builder(FirebaseSessionsComponent::class.java)
50+
.name("fire-sessions-component")
51+
.add(Dependency.required(appContext))
6952
.add(Dependency.required(backgroundDispatcher))
70-
.factory { container ->
71-
SessionFirelogPublisherImpl(
72-
container[firebaseApp],
73-
container[firebaseInstallationsApi],
74-
container[sessionsSettings],
75-
EventGDTLogger(container.getProvider(transportFactory)),
76-
container[backgroundDispatcher],
77-
)
78-
}
79-
.build(),
80-
Component.builder(SessionsSettings::class.java)
81-
.name("sessions-settings")
82-
.add(Dependency.required(firebaseApp))
8353
.add(Dependency.required(blockingDispatcher))
84-
.add(Dependency.required(backgroundDispatcher))
85-
.add(Dependency.required(firebaseInstallationsApi))
86-
.factory { container ->
87-
SessionsSettings(
88-
container[firebaseApp],
89-
container[blockingDispatcher],
90-
container[backgroundDispatcher],
91-
container[firebaseInstallationsApi],
92-
)
93-
}
94-
.build(),
95-
Component.builder(SessionDatastore::class.java)
96-
.name("sessions-datastore")
9754
.add(Dependency.required(firebaseApp))
98-
.add(Dependency.required(backgroundDispatcher))
55+
.add(Dependency.required(firebaseInstallationsApi))
56+
.add(Dependency.requiredProvider(transportFactory))
9957
.factory { container ->
100-
SessionDatastoreImpl(
101-
container[firebaseApp].applicationContext,
102-
container[backgroundDispatcher],
103-
)
58+
DaggerFirebaseSessionsComponent.builder()
59+
.appContext(container[appContext])
60+
.backgroundDispatcher(container[backgroundDispatcher])
61+
.blockingDispatcher(container[blockingDispatcher])
62+
.firebaseApp(container[firebaseApp])
63+
.firebaseInstallationsApi(container[firebaseInstallationsApi])
64+
.transportFactoryProvider(container.getProvider(transportFactory))
65+
.build()
10466
}
10567
.build(),
106-
Component.builder(SessionLifecycleServiceBinder::class.java)
107-
.name("sessions-service-binder")
108-
.add(Dependency.required(firebaseApp))
109-
.factory { container -> SessionLifecycleServiceBinderImpl(container[firebaseApp]) }
110-
.build(),
11168
LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME),
11269
)
11370

11471
private companion object {
115-
private const val LIBRARY_NAME = "fire-sessions"
72+
const val LIBRARY_NAME = "fire-sessions"
11673

117-
private val firebaseApp = unqualified(FirebaseApp::class.java)
118-
private val firebaseInstallationsApi = unqualified(FirebaseInstallationsApi::class.java)
119-
private val backgroundDispatcher =
120-
qualified(Background::class.java, CoroutineDispatcher::class.java)
121-
private val blockingDispatcher =
122-
qualified(Blocking::class.java, CoroutineDispatcher::class.java)
123-
private val transportFactory = unqualified(TransportFactory::class.java)
124-
private val sessionsSettings = unqualified(SessionsSettings::class.java)
125-
private val sessionLifecycleServiceBinder =
126-
unqualified(SessionLifecycleServiceBinder::class.java)
74+
val appContext = unqualified(Context::class.java)
75+
val firebaseApp = unqualified(FirebaseApp::class.java)
76+
val firebaseInstallationsApi = unqualified(FirebaseInstallationsApi::class.java)
77+
val backgroundDispatcher = qualified(Background::class.java, CoroutineDispatcher::class.java)
78+
val blockingDispatcher = qualified(Blocking::class.java, CoroutineDispatcher::class.java)
79+
val transportFactory = unqualified(TransportFactory::class.java)
80+
val firebaseSessionsComponent = unqualified(FirebaseSessionsComponent::class.java)
12781
}
12882
}

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ import androidx.datastore.preferences.core.emptyPreferences
2626
import androidx.datastore.preferences.core.stringPreferencesKey
2727
import androidx.datastore.preferences.preferencesDataStore
2828
import com.google.firebase.Firebase
29+
import com.google.firebase.annotations.concurrent.Background
2930
import com.google.firebase.app
3031
import com.google.firebase.sessions.ProcessDetailsProvider.getProcessName
3132
import java.io.IOException
3233
import java.util.concurrent.atomic.AtomicReference
34+
import javax.inject.Inject
35+
import javax.inject.Singleton
3336
import kotlin.coroutines.CoroutineContext
3437
import kotlinx.coroutines.CoroutineScope
3538
import kotlinx.coroutines.flow.Flow
@@ -53,13 +56,16 @@ internal interface SessionDatastore {
5356

5457
companion object {
5558
val instance: SessionDatastore
56-
get() = Firebase.app[SessionDatastore::class.java]
59+
get() = Firebase.app[FirebaseSessionsComponent::class.java].sessionDatastore
5760
}
5861
}
5962

60-
internal class SessionDatastoreImpl(
61-
private val context: Context,
62-
private val backgroundDispatcher: CoroutineContext,
63+
@Singleton
64+
internal class SessionDatastoreImpl
65+
@Inject
66+
constructor(
67+
private val appContext: Context,
68+
@Background private val backgroundDispatcher: CoroutineContext,
6369
) : SessionDatastore {
6470

6571
/** Most recent session from datastore is updated asynchronously whenever it changes */
@@ -70,7 +76,7 @@ internal class SessionDatastoreImpl(
7076
}
7177

7278
private val firebaseSessionDataFlow: Flow<FirebaseSessionsData> =
73-
context.dataStore.data
79+
appContext.dataStore.data
7480
.catch { exception ->
7581
Log.e(TAG, "Error reading stored session data.", exception)
7682
emit(emptyPreferences())
@@ -86,24 +92,19 @@ internal class SessionDatastoreImpl(
8692
override fun updateSessionId(sessionId: String) {
8793
CoroutineScope(backgroundDispatcher).launch {
8894
try {
89-
context.dataStore.edit { preferences ->
95+
appContext.dataStore.edit { preferences ->
9096
preferences[FirebaseSessionDataKeys.SESSION_ID] = sessionId
9197
}
9298
} catch (e: IOException) {
93-
Log.w(
94-
TAG,
95-
"Failed to update session Id: $e",
96-
)
99+
Log.w(TAG, "Failed to update session Id: $e")
97100
}
98101
}
99102
}
100103

101104
override fun getCurrentSessionId() = currentSessionFromDatastore.get()?.sessionId
102105

103106
private fun mapSessionsData(preferences: Preferences): FirebaseSessionsData =
104-
FirebaseSessionsData(
105-
preferences[FirebaseSessionDataKeys.SESSION_ID],
106-
)
107+
FirebaseSessionsData(preferences[FirebaseSessionDataKeys.SESSION_ID])
107108

108109
private companion object {
109110
private const val TAG = "FirebaseSessionsRepo"

0 commit comments

Comments
 (0)