diff --git a/firebase-sessions/CHANGELOG.md b/firebase-sessions/CHANGELOG.md index 2473b64a1cf..d5293913dc9 100644 --- a/firebase-sessions/CHANGELOG.md +++ b/firebase-sessions/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +* [changed] Use Dagger for dependency injection * [changed] Updated datastore dependency to `1.1.3` to fix [CVE-2024-7254](https://github.com/advisories/GHSA-735f-pc8j-v9w8). diff --git a/firebase-sessions/firebase-sessions.gradle.kts b/firebase-sessions/firebase-sessions.gradle.kts index 0a09740bd77..b136a281660 100644 --- a/firebase-sessions/firebase-sessions.gradle.kts +++ b/firebase-sessions/firebase-sessions.gradle.kts @@ -18,6 +18,7 @@ plugins { id("firebase-library") + id("firebase-vendor") id("kotlin-android") id("kotlin-kapt") } @@ -67,12 +68,18 @@ dependencies { exclude(group = "com.google.firebase", module = "firebase-common") exclude(group = "com.google.firebase", module = "firebase-components") } - implementation("com.google.android.datatransport:transport-api:3.2.0") + api("com.google.firebase:firebase-annotations:16.2.0") api("com.google.firebase:firebase-encoders:17.0.0") api("com.google.firebase:firebase-encoders-json:18.0.1") + + implementation("com.google.android.datatransport:transport-api:3.2.0") + implementation(libs.javax.inject) implementation(libs.androidx.annotation) implementation(libs.androidx.datastore.preferences) + + vendor(libs.dagger.dagger) { exclude(group = "javax.inject", module = "javax.inject") } + compileOnly(libs.errorprone.annotations) runtimeOnly("com.google.firebase:firebase-installations:18.0.0") { @@ -85,6 +92,7 @@ dependencies { } kapt(project(":encoders:firebase-encoders-processor")) + kapt(libs.dagger.compiler) testImplementation(project(":integ-testing")) { exclude(group = "com.google.firebase", module = "firebase-common") diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/EventGDTLogger.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/EventGDTLogger.kt index a11b20a7d5c..496cc70d36d 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/EventGDTLogger.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/EventGDTLogger.kt @@ -21,6 +21,8 @@ import com.google.android.datatransport.Encoding import com.google.android.datatransport.Event import com.google.android.datatransport.TransportFactory import com.google.firebase.inject.Provider +import javax.inject.Inject +import javax.inject.Singleton /** * The [EventGDTLoggerInterface] is for testing purposes so that we can mock EventGDTLogger in other @@ -38,19 +40,17 @@ internal fun interface EventGDTLoggerInterface { * * @hide */ -internal class EventGDTLogger(private val transportFactoryProvider: Provider) : +@Singleton +internal class EventGDTLogger +@Inject +constructor(private val transportFactoryProvider: Provider) : EventGDTLoggerInterface { // Logs a [SessionEvent] to FireLog override fun log(sessionEvent: SessionEvent) { transportFactoryProvider .get() - .getTransport( - AQS_LOG_SOURCE, - SessionEvent::class.java, - Encoding.of("json"), - this::encode, - ) + .getTransport(AQS_LOG_SOURCE, SessionEvent::class.java, Encoding.of("json"), this::encode) .send(Event.ofData(sessionEvent)) } diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessions.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessions.kt index 0dec3b98150..18b9961724b 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessions.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessions.kt @@ -20,18 +20,24 @@ import android.app.Application import android.util.Log import com.google.firebase.Firebase import com.google.firebase.FirebaseApp +import com.google.firebase.annotations.concurrent.Background import com.google.firebase.app import com.google.firebase.sessions.api.FirebaseSessionsDependencies import com.google.firebase.sessions.settings.SessionsSettings +import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** Responsible for initializing AQS */ -internal class FirebaseSessions( +@Singleton +internal class FirebaseSessions +@Inject +constructor( private val firebaseApp: FirebaseApp, private val settings: SessionsSettings, - backgroundDispatcher: CoroutineContext, + @Background backgroundDispatcher: CoroutineContext, lifecycleServiceBinder: SessionLifecycleServiceBinder, ) { diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsComponent.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsComponent.kt new file mode 100644 index 00000000000..aa60f3f41df --- /dev/null +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsComponent.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.sessions + +import android.content.Context +import com.google.android.datatransport.TransportFactory +import com.google.firebase.FirebaseApp +import com.google.firebase.annotations.concurrent.Background +import com.google.firebase.annotations.concurrent.Blocking +import com.google.firebase.inject.Provider +import com.google.firebase.installations.FirebaseInstallationsApi +import com.google.firebase.sessions.settings.SessionsSettings +import dagger.Binds +import dagger.BindsInstance +import dagger.Component +import dagger.Module +import dagger.Provides +import javax.inject.Singleton +import kotlin.coroutines.CoroutineContext + +/** Dagger component to provide [FirebaseSessions] and its dependencies. */ +@Singleton +@Component(modules = [FirebaseSessionsComponent.MainModule::class]) +internal interface FirebaseSessionsComponent { + val firebaseSessions: FirebaseSessions + + val sessionDatastore: SessionDatastore + val sessionFirelogPublisher: SessionFirelogPublisher + val sessionGenerator: SessionGenerator + val sessionsSettings: SessionsSettings + + @Component.Builder + interface Builder { + @BindsInstance fun appContext(appContext: Context): Builder + + @BindsInstance + fun backgroundDispatcher(@Background backgroundDispatcher: CoroutineContext): Builder + + @BindsInstance fun blockingDispatcher(@Blocking blockingDispatcher: CoroutineContext): Builder + + @BindsInstance fun firebaseApp(firebaseApp: FirebaseApp): Builder + + @BindsInstance + fun firebaseInstallationsApi(firebaseInstallationsApi: FirebaseInstallationsApi): Builder + + @BindsInstance + fun transportFactoryProvider(transportFactoryProvider: Provider): Builder + + fun build(): FirebaseSessionsComponent + } + + @Module + interface MainModule { + @Binds @Singleton fun eventGDTLoggerInterface(impl: EventGDTLogger): EventGDTLoggerInterface + + @Binds @Singleton fun sessionDatastore(impl: SessionDatastoreImpl): SessionDatastore + + @Binds + @Singleton + fun sessionFirelogPublisher(impl: SessionFirelogPublisherImpl): SessionFirelogPublisher + + @Binds + @Singleton + fun sessionLifecycleServiceBinder( + impl: SessionLifecycleServiceBinderImpl + ): SessionLifecycleServiceBinder + + companion object { + @Provides @Singleton fun sessionGenerator() = SessionGenerator(timeProvider = WallClock) + } + } +} diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsRegistrar.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsRegistrar.kt index caad2de6ff8..1043ad74800 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsRegistrar.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessionsRegistrar.kt @@ -16,6 +16,7 @@ package com.google.firebase.sessions +import android.content.Context import androidx.annotation.Keep import com.google.android.datatransport.TransportFactory import com.google.firebase.FirebaseApp @@ -28,7 +29,6 @@ import com.google.firebase.components.Qualified.qualified import com.google.firebase.components.Qualified.unqualified import com.google.firebase.installations.FirebaseInstallationsApi import com.google.firebase.platforminfo.LibraryVersionComponent -import com.google.firebase.sessions.settings.SessionsSettings import kotlinx.coroutines.CoroutineDispatcher /** @@ -42,87 +42,41 @@ internal class FirebaseSessionsRegistrar : ComponentRegistrar { listOf( Component.builder(FirebaseSessions::class.java) .name(LIBRARY_NAME) - .add(Dependency.required(firebaseApp)) - .add(Dependency.required(sessionsSettings)) - .add(Dependency.required(backgroundDispatcher)) - .add(Dependency.required(sessionLifecycleServiceBinder)) - .factory { container -> - FirebaseSessions( - container[firebaseApp], - container[sessionsSettings], - container[backgroundDispatcher], - container[sessionLifecycleServiceBinder], - ) - } + .add(Dependency.required(firebaseSessionsComponent)) + .factory { container -> container[firebaseSessionsComponent].firebaseSessions } .eagerInDefaultApp() .build(), - Component.builder(SessionGenerator::class.java) - .name("session-generator") - .factory { SessionGenerator(timeProvider = WallClock) } - .build(), - Component.builder(SessionFirelogPublisher::class.java) - .name("session-publisher") - .add(Dependency.required(firebaseApp)) - .add(Dependency.required(firebaseInstallationsApi)) - .add(Dependency.required(sessionsSettings)) - .add(Dependency.requiredProvider(transportFactory)) + Component.builder(FirebaseSessionsComponent::class.java) + .name("fire-sessions-component") + .add(Dependency.required(appContext)) .add(Dependency.required(backgroundDispatcher)) - .factory { container -> - SessionFirelogPublisherImpl( - container[firebaseApp], - container[firebaseInstallationsApi], - container[sessionsSettings], - EventGDTLogger(container.getProvider(transportFactory)), - container[backgroundDispatcher], - ) - } - .build(), - Component.builder(SessionsSettings::class.java) - .name("sessions-settings") - .add(Dependency.required(firebaseApp)) .add(Dependency.required(blockingDispatcher)) - .add(Dependency.required(backgroundDispatcher)) - .add(Dependency.required(firebaseInstallationsApi)) - .factory { container -> - SessionsSettings( - container[firebaseApp], - container[blockingDispatcher], - container[backgroundDispatcher], - container[firebaseInstallationsApi], - ) - } - .build(), - Component.builder(SessionDatastore::class.java) - .name("sessions-datastore") .add(Dependency.required(firebaseApp)) - .add(Dependency.required(backgroundDispatcher)) + .add(Dependency.required(firebaseInstallationsApi)) + .add(Dependency.requiredProvider(transportFactory)) .factory { container -> - SessionDatastoreImpl( - container[firebaseApp].applicationContext, - container[backgroundDispatcher], - ) + DaggerFirebaseSessionsComponent.builder() + .appContext(container[appContext]) + .backgroundDispatcher(container[backgroundDispatcher]) + .blockingDispatcher(container[blockingDispatcher]) + .firebaseApp(container[firebaseApp]) + .firebaseInstallationsApi(container[firebaseInstallationsApi]) + .transportFactoryProvider(container.getProvider(transportFactory)) + .build() } .build(), - Component.builder(SessionLifecycleServiceBinder::class.java) - .name("sessions-service-binder") - .add(Dependency.required(firebaseApp)) - .factory { container -> SessionLifecycleServiceBinderImpl(container[firebaseApp]) } - .build(), LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME), ) private companion object { - private const val LIBRARY_NAME = "fire-sessions" + const val LIBRARY_NAME = "fire-sessions" - private val firebaseApp = unqualified(FirebaseApp::class.java) - private val firebaseInstallationsApi = unqualified(FirebaseInstallationsApi::class.java) - private val backgroundDispatcher = - qualified(Background::class.java, CoroutineDispatcher::class.java) - private val blockingDispatcher = - qualified(Blocking::class.java, CoroutineDispatcher::class.java) - private val transportFactory = unqualified(TransportFactory::class.java) - private val sessionsSettings = unqualified(SessionsSettings::class.java) - private val sessionLifecycleServiceBinder = - unqualified(SessionLifecycleServiceBinder::class.java) + val appContext = unqualified(Context::class.java) + val firebaseApp = unqualified(FirebaseApp::class.java) + val firebaseInstallationsApi = unqualified(FirebaseInstallationsApi::class.java) + val backgroundDispatcher = qualified(Background::class.java, CoroutineDispatcher::class.java) + val blockingDispatcher = qualified(Blocking::class.java, CoroutineDispatcher::class.java) + val transportFactory = unqualified(TransportFactory::class.java) + val firebaseSessionsComponent = unqualified(FirebaseSessionsComponent::class.java) } } diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionDatastore.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionDatastore.kt index 736761617fd..a2d46a48891 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionDatastore.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionDatastore.kt @@ -26,10 +26,13 @@ import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import com.google.firebase.Firebase +import com.google.firebase.annotations.concurrent.Background import com.google.firebase.app import com.google.firebase.sessions.ProcessDetailsProvider.getProcessName import java.io.IOException import java.util.concurrent.atomic.AtomicReference +import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -53,13 +56,16 @@ internal interface SessionDatastore { companion object { val instance: SessionDatastore - get() = Firebase.app[SessionDatastore::class.java] + get() = Firebase.app[FirebaseSessionsComponent::class.java].sessionDatastore } } -internal class SessionDatastoreImpl( - private val context: Context, - private val backgroundDispatcher: CoroutineContext, +@Singleton +internal class SessionDatastoreImpl +@Inject +constructor( + private val appContext: Context, + @Background private val backgroundDispatcher: CoroutineContext, ) : SessionDatastore { /** Most recent session from datastore is updated asynchronously whenever it changes */ @@ -70,7 +76,7 @@ internal class SessionDatastoreImpl( } private val firebaseSessionDataFlow: Flow = - context.dataStore.data + appContext.dataStore.data .catch { exception -> Log.e(TAG, "Error reading stored session data.", exception) emit(emptyPreferences()) @@ -86,14 +92,11 @@ internal class SessionDatastoreImpl( override fun updateSessionId(sessionId: String) { CoroutineScope(backgroundDispatcher).launch { try { - context.dataStore.edit { preferences -> + appContext.dataStore.edit { preferences -> preferences[FirebaseSessionDataKeys.SESSION_ID] = sessionId } } catch (e: IOException) { - Log.w( - TAG, - "Failed to update session Id: $e", - ) + Log.w(TAG, "Failed to update session Id: $e") } } } @@ -101,9 +104,7 @@ internal class SessionDatastoreImpl( override fun getCurrentSessionId() = currentSessionFromDatastore.get()?.sessionId private fun mapSessionsData(preferences: Preferences): FirebaseSessionsData = - FirebaseSessionsData( - preferences[FirebaseSessionDataKeys.SESSION_ID], - ) + FirebaseSessionsData(preferences[FirebaseSessionDataKeys.SESSION_ID]) private companion object { private const val TAG = "FirebaseSessionsRepo" diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionFirelogPublisher.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionFirelogPublisher.kt index d63d49e3fe5..6e4b6153f8d 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionFirelogPublisher.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionFirelogPublisher.kt @@ -19,10 +19,13 @@ package com.google.firebase.sessions import android.util.Log import com.google.firebase.Firebase import com.google.firebase.FirebaseApp +import com.google.firebase.annotations.concurrent.Background import com.google.firebase.app import com.google.firebase.installations.FirebaseInstallationsApi import com.google.firebase.sessions.api.FirebaseSessionsDependencies import com.google.firebase.sessions.settings.SessionsSettings +import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -35,7 +38,7 @@ internal fun interface SessionFirelogPublisher { companion object { val instance: SessionFirelogPublisher - get() = Firebase.app[SessionFirelogPublisher::class.java] + get() = Firebase.app[FirebaseSessionsComponent::class.java].sessionFirelogPublisher } } @@ -44,12 +47,15 @@ internal fun interface SessionFirelogPublisher { * * @hide */ -internal class SessionFirelogPublisherImpl( +@Singleton +internal class SessionFirelogPublisherImpl +@Inject +constructor( private val firebaseApp: FirebaseApp, private val firebaseInstallations: FirebaseInstallationsApi, private val sessionSettings: SessionsSettings, private val eventGDTLogger: EventGDTLoggerInterface, - private val backgroundDispatcher: CoroutineContext, + @Background private val backgroundDispatcher: CoroutineContext, ) : SessionFirelogPublisher { /** diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionGenerator.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionGenerator.kt index 3b4c3124c98..41aeb442cfb 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionGenerator.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionGenerator.kt @@ -20,6 +20,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue import com.google.firebase.Firebase import com.google.firebase.app import java.util.UUID +import javax.inject.Singleton /** * [SessionDetails] is a data class responsible for storing information about the current Session. @@ -35,9 +36,10 @@ internal data class SessionDetails( * The [SessionGenerator] is responsible for generating the Session ID, and keeping the * [SessionDetails] up to date with the latest values. */ +@Singleton internal class SessionGenerator( private val timeProvider: TimeProvider, - private val uuidGenerator: () -> UUID = UUID::randomUUID + private val uuidGenerator: () -> UUID = UUID::randomUUID, ) { private val firstSessionId = generateSessionId() private var sessionIndex = -1 @@ -59,7 +61,7 @@ internal class SessionGenerator( sessionId = if (sessionIndex == 0) firstSessionId else generateSessionId(), firstSessionId, sessionIndex, - sessionStartTimestampUs = timeProvider.currentTimeUs() + sessionStartTimestampUs = timeProvider.currentTimeUs(), ) return currentSession } @@ -68,6 +70,6 @@ internal class SessionGenerator( internal companion object { val instance: SessionGenerator - get() = Firebase.app[SessionGenerator::class.java] + get() = Firebase.app[FirebaseSessionsComponent::class.java].sessionGenerator } } diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleService.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleService.kt index bde6d138fbe..6807d8bec69 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleService.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleService.kt @@ -128,7 +128,6 @@ internal class SessionLifecycleService : Service() { /** Generates a new session id and sends it everywhere it's needed */ private fun newSession() { try { - // TODO(mrober): Consider migrating to Dagger, or update [FirebaseSessionsRegistrar]. SessionGenerator.instance.generateNewSession() Log.d(TAG, "Generated new session.") broadcastSession() diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleServiceBinder.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleServiceBinder.kt index 97a7d6b73ae..094a76ee51c 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleServiceBinder.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionLifecycleServiceBinder.kt @@ -21,7 +21,8 @@ import android.content.Intent import android.content.ServiceConnection import android.os.Messenger import android.util.Log -import com.google.firebase.FirebaseApp +import javax.inject.Inject +import javax.inject.Singleton /** Interface for binding with the [SessionLifecycleService]. */ internal fun interface SessionLifecycleServiceBinder { @@ -32,11 +33,12 @@ internal fun interface SessionLifecycleServiceBinder { fun bindToService(callback: Messenger, serviceConnection: ServiceConnection) } -internal class SessionLifecycleServiceBinderImpl(private val firebaseApp: FirebaseApp) : - SessionLifecycleServiceBinder { +@Singleton +internal class SessionLifecycleServiceBinderImpl +@Inject +constructor(private val appContext: Context) : SessionLifecycleServiceBinder { override fun bindToService(callback: Messenger, serviceConnection: ServiceConnection) { - val appContext: Context = firebaseApp.applicationContext.applicationContext Intent(appContext, SessionLifecycleService::class.java).also { intent -> Log.d(TAG, "Binding service to application.") // This is necessary for the onBind() to be called by each process diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SessionsSettings.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SessionsSettings.kt index fd2ee5dbddd..41b73f14a4e 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SessionsSettings.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SessionsSettings.kt @@ -25,17 +25,23 @@ import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.preferencesDataStore import com.google.firebase.Firebase import com.google.firebase.FirebaseApp +import com.google.firebase.annotations.concurrent.Background +import com.google.firebase.annotations.concurrent.Blocking import com.google.firebase.app import com.google.firebase.installations.FirebaseInstallationsApi import com.google.firebase.sessions.ApplicationInfo +import com.google.firebase.sessions.FirebaseSessionsComponent import com.google.firebase.sessions.ProcessDetailsProvider.getProcessName import com.google.firebase.sessions.SessionDataStoreConfigs import com.google.firebase.sessions.SessionEvents +import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.CoroutineContext import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes /** [SessionsSettings] manages all the configs that are relevant to the sessions library. */ +@Singleton internal class SessionsSettings( private val localOverrideSettings: SettingsProvider, private val remoteSettings: SettingsProvider, @@ -62,10 +68,11 @@ internal class SessionsSettings( ), ) + @Inject constructor( firebaseApp: FirebaseApp, - blockingDispatcher: CoroutineContext, - backgroundDispatcher: CoroutineContext, + @Blocking blockingDispatcher: CoroutineContext, + @Background backgroundDispatcher: CoroutineContext, firebaseInstallationsApi: FirebaseInstallationsApi, ) : this( firebaseApp.applicationContext, @@ -143,7 +150,7 @@ internal class SessionsSettings( private const val TAG = "SessionsSettings" val instance: SessionsSettings - get() = Firebase.app[SessionsSettings::class.java] + get() = Firebase.app[FirebaseSessionsComponent::class.java].sessionsSettings private val Context.dataStore: DataStore by preferencesDataStore( diff --git a/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeComponent.kt b/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeComponent.kt new file mode 100644 index 00000000000..eda16d8f0b4 --- /dev/null +++ b/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeComponent.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.sessions.testing + +import com.google.firebase.Firebase +import com.google.firebase.app +import com.google.firebase.sessions.FirebaseSessions +import com.google.firebase.sessions.FirebaseSessionsComponent +import com.google.firebase.sessions.SessionDatastore +import com.google.firebase.sessions.SessionFirelogPublisher +import com.google.firebase.sessions.SessionGenerator +import com.google.firebase.sessions.settings.SessionsSettings + +/** Bridge between FirebaseSessionsComponent and FirebaseSessionsFakeRegistrar. */ +internal class FirebaseSessionsFakeComponent : FirebaseSessionsComponent { + // TODO(mrober): Move tests to use Dagger for DI. + + override val firebaseSessions: FirebaseSessions + get() = Firebase.app[FirebaseSessions::class.java] + + override val sessionDatastore: SessionDatastore + get() = Firebase.app[SessionDatastore::class.java] + + override val sessionFirelogPublisher: SessionFirelogPublisher + get() = Firebase.app[SessionFirelogPublisher::class.java] + + override val sessionGenerator: SessionGenerator + get() = Firebase.app[SessionGenerator::class.java] + + override val sessionsSettings: SessionsSettings + get() = Firebase.app[SessionsSettings::class.java] +} diff --git a/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeRegistrar.kt b/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeRegistrar.kt index 9755a5e12d0..58855f622f3 100644 --- a/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeRegistrar.kt +++ b/firebase-sessions/src/test/kotlin/com/google/firebase/sessions/testing/FirebaseSessionsFakeRegistrar.kt @@ -17,7 +17,6 @@ package com.google.firebase.sessions.testing import androidx.annotation.Keep -import com.google.android.datatransport.TransportFactory import com.google.firebase.FirebaseApp import com.google.firebase.annotations.concurrent.Background import com.google.firebase.annotations.concurrent.Blocking @@ -26,10 +25,10 @@ import com.google.firebase.components.ComponentRegistrar import com.google.firebase.components.Dependency import com.google.firebase.components.Qualified.qualified import com.google.firebase.components.Qualified.unqualified -import com.google.firebase.installations.FirebaseInstallationsApi import com.google.firebase.platforminfo.LibraryVersionComponent import com.google.firebase.sessions.BuildConfig import com.google.firebase.sessions.FirebaseSessions +import com.google.firebase.sessions.FirebaseSessionsComponent import com.google.firebase.sessions.SessionDatastore import com.google.firebase.sessions.SessionFirelogPublisher import com.google.firebase.sessions.SessionGenerator @@ -75,6 +74,10 @@ internal class FirebaseSessionsFakeRegistrar : ComponentRegistrar { ) } .build(), + Component.builder(FirebaseSessionsComponent::class.java) + .name("fake-fire-sessions-component") + .factory { FirebaseSessionsFakeComponent() } + .build(), Component.builder(FakeSessionDatastore::class.java) .name("fake-sessions-datastore") .factory { FakeSessionDatastore() } @@ -97,21 +100,14 @@ internal class FirebaseSessionsFakeRegistrar : ComponentRegistrar { ) private companion object { - private const val LIBRARY_NAME = "fire-sessions" - - private val firebaseApp = unqualified(FirebaseApp::class.java) - private val firebaseInstallationsApi = unqualified(FirebaseInstallationsApi::class.java) - private val backgroundDispatcher = - qualified(Background::class.java, CoroutineDispatcher::class.java) - private val blockingDispatcher = - qualified(Blocking::class.java, CoroutineDispatcher::class.java) - private val transportFactory = unqualified(TransportFactory::class.java) - private val fakeFirelogPublisher = unqualified(FakeFirelogPublisher::class.java) - private val fakeDatastore = unqualified(FakeSessionDatastore::class.java) - private val fakeServiceBinder = unqualified(FakeSessionLifecycleServiceBinder::class.java) - private val sessionGenerator = unqualified(SessionGenerator::class.java) - private val sessionsSettings = unqualified(SessionsSettings::class.java) + const val LIBRARY_NAME = "fire-sessions" - private val fakeFirebaseInstallations = FakeFirebaseInstallations("FaKeFiD") + val firebaseApp = unqualified(FirebaseApp::class.java) + val backgroundDispatcher = qualified(Background::class.java, CoroutineDispatcher::class.java) + val blockingDispatcher = qualified(Blocking::class.java, CoroutineDispatcher::class.java) + val fakeFirelogPublisher = unqualified(FakeFirelogPublisher::class.java) + val fakeDatastore = unqualified(FakeSessionDatastore::class.java) + val fakeServiceBinder = unqualified(FakeSessionLifecycleServiceBinder::class.java) + val fakeFirebaseInstallations = FakeFirebaseInstallations("FaKeFiD") } }