From 7125a401d72fbe0fe85990d23d1cf28478b8095f Mon Sep 17 00:00:00 2001 From: Vladimir Kryachko Date: Thu, 1 Dec 2022 19:33:04 -0500 Subject: [PATCH 1/4] WIP Use dagger for common. --- firebase-common/firebase-common.gradle | 7 + .../com/google/firebase/AppComponent.java | 124 ++++++++++++++++++ .../java/com/google/firebase/FirebaseApp.java | 100 +++++--------- .../google/firebase/annotations/AppScope.java | 6 + .../DefaultHeartBeatController.java | 12 +- .../heartbeatinfo/HeartBeatInfoStorage.java | 8 +- .../internal/DataCollectionConfigStorage.java | 9 +- .../DefaultUserAgentPublisher.java | 4 + .../platforminfo/LibraryVersionsModule.java | 18 +++ 9 files changed, 211 insertions(+), 77 deletions(-) create mode 100644 firebase-common/src/main/java/com/google/firebase/AppComponent.java create mode 100644 firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java create mode 100644 firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java diff --git a/firebase-common/firebase-common.gradle b/firebase-common/firebase-common.gradle index 16c38ff1db6..66354446635 100644 --- a/firebase-common/firebase-common.gradle +++ b/firebase-common/firebase-common.gradle @@ -14,6 +14,7 @@ plugins { id 'firebase-library' + id 'firebase-vendor' } firebaseLibrary { @@ -68,6 +69,12 @@ dependencies { implementation "com.google.android.gms:play-services-tasks:18.0.1" implementation 'androidx.concurrent:concurrent-futures:1.1.0' + implementation 'javax.inject:javax.inject:1' + vendor ('com.google.dagger:dagger:2.43.2') { + exclude group: "javax.inject", module: "javax.inject" + } + annotationProcessor 'com.google.dagger:dagger-compiler:2.43.2' + // FirebaseApp references storage, so storage needs to be on classpath when dokka runs. javadocClasspath project(path: ':firebase-storage') javadocClasspath 'com.google.auto.value:auto-value-annotations:1.6.6' diff --git a/firebase-common/src/main/java/com/google/firebase/AppComponent.java b/firebase-common/src/main/java/com/google/firebase/AppComponent.java new file mode 100644 index 00000000000..181ab3f778f --- /dev/null +++ b/firebase-common/src/main/java/com/google/firebase/AppComponent.java @@ -0,0 +1,124 @@ +package com.google.firebase; + +import android.content.Context; +import androidx.core.os.UserManagerCompat; +import com.google.firebase.annotations.AppScope; +import com.google.firebase.annotations.concurrent.Background; +import com.google.firebase.components.ComponentDiscovery; +import com.google.firebase.components.ComponentDiscoveryService; +import com.google.firebase.components.ComponentRegistrar; +import com.google.firebase.components.ComponentRuntime; +import com.google.firebase.components.Qualified; +import com.google.firebase.concurrent.ExecutorsRegistrar; +import com.google.firebase.events.Publisher; +import com.google.firebase.heartbeatinfo.HeartBeatConsumer; +import com.google.firebase.inject.Provider; +import com.google.firebase.platforminfo.GlobalLibraryVersionRegistrar; +import com.google.firebase.platforminfo.LibraryVersionsModule; +import com.google.firebase.provider.FirebaseInitProvider; +import com.google.firebase.tracing.ComponentMonitor; +import com.google.firebase.tracing.FirebaseTrace; +import dagger.BindsInstance; +import dagger.Component; +import dagger.Module; +import dagger.Provides; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Executor; +import javax.inject.Named; + +/** @hide */ +@Component(modules = {AppComponent.MainModule.class, LibraryVersionsModule.class}) +@AppScope +interface AppComponent { + + FirebaseApp getApp(); + + @Component.Builder + interface Builder { + @BindsInstance + Builder setApplicationContext(Context ctx); + + @BindsInstance + Builder setName(@Named("appName") String name); + + @BindsInstance + Builder setOptions(FirebaseOptions options); + + AppComponent build(); + } + + @Module + interface MainModule { + @Provides + @Named("persistenceKey") + static String providesPersistenceKey(@Named("appName") String name, FirebaseOptions options) { + return FirebaseApp.getPersistenceKey(name, options); + } + + @Provides + @AppScope + static ComponentRuntime provideComponentRuntime( + Context applicationContext, + FirebaseOptions options, + javax.inject.Provider app) { + FirebaseTrace.pushTrace("ComponentDiscovery"); + List> registrars = + ComponentDiscovery.forContext(applicationContext, ComponentDiscoveryService.class) + .discoverLazy(); + FirebaseTrace.popTrace(); // ComponentDiscovery + + FirebaseTrace.pushTrace("Runtime"); + ComponentRuntime.Builder builder = + ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE) + .addLazyComponentRegistrars(registrars) + .addComponentRegistrar(new FirebaseCommonRegistrar()) + .addComponentRegistrar(new ExecutorsRegistrar()) + .addComponent( + com.google.firebase.components.Component.of(applicationContext, Context.class)) + .addComponent( + com.google.firebase.components.Component.builder(FirebaseApp.class) + .factory(c -> app.get()) + .build()) + .addComponent( + com.google.firebase.components.Component.of(options, FirebaseOptions.class)) + .setProcessor(new ComponentMonitor()); + + StartupTime startupTime = FirebaseInitProvider.getStartupTime(); + // Don't provide StartupTime in direct boot mode or if Firebase was manually started + if (UserManagerCompat.isUserUnlocked(applicationContext) + && FirebaseInitProvider.isCurrentlyInitializing()) { + builder.addComponent( + com.google.firebase.components.Component.of(startupTime, StartupTime.class)); + } + + ComponentRuntime componentRuntime = builder.build(); + FirebaseTrace.popTrace(); // Runtime + return componentRuntime; + } + + @Provides + @AppScope + static Publisher providesPublisher(ComponentRuntime runtime) { + return runtime.get(Publisher.class); + } + + @Provides + @AppScope + static Set providesHeartBeatConsumers(ComponentRuntime runtime) { + return runtime.setOf(HeartBeatConsumer.class); + } + + @Provides + @Background + @AppScope + static Executor providesBgExecutor(ComponentRuntime runtime) { + return runtime.get(Qualified.qualified(Background.class, Executor.class)); + } + + @Provides + static GlobalLibraryVersionRegistrar providesGlobalLibraryRegistrar() { + return GlobalLibraryVersionRegistrar.getInstance(); + } + } +} diff --git a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java index ee16ff412e6..4d63fac2b06 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -39,20 +39,10 @@ import com.google.android.gms.common.internal.Preconditions; import com.google.android.gms.common.util.PlatformVersion; import com.google.android.gms.common.util.ProcessUtils; -import com.google.firebase.components.Component; -import com.google.firebase.components.ComponentDiscovery; -import com.google.firebase.components.ComponentDiscoveryService; -import com.google.firebase.components.ComponentRegistrar; +import com.google.firebase.annotations.AppScope; import com.google.firebase.components.ComponentRuntime; -import com.google.firebase.components.Lazy; -import com.google.firebase.concurrent.ExecutorsRegistrar; -import com.google.firebase.events.Publisher; import com.google.firebase.heartbeatinfo.DefaultHeartBeatController; -import com.google.firebase.inject.Provider; import com.google.firebase.internal.DataCollectionConfigStorage; -import com.google.firebase.provider.FirebaseInitProvider; -import com.google.firebase.tracing.ComponentMonitor; -import com.google.firebase.tracing.FirebaseTrace; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; @@ -61,6 +51,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import javax.inject.Inject; +import javax.inject.Named; /** * The entry point of Firebase SDKs. It holds common configuration and state for Firebase APIs. Most @@ -88,6 +80,7 @@ * Use of Firebase in processes other than the main process is not supported and will likely cause * problems related to resource contention. */ +@AppScope public class FirebaseApp { private static final String LOG_TAG = "FirebaseApp"; @@ -109,8 +102,8 @@ public class FirebaseApp { // enabled is a backwards incompatible change. private final AtomicBoolean automaticResourceManagementEnabled = new AtomicBoolean(false); private final AtomicBoolean deleted = new AtomicBoolean(); - private final Lazy dataCollectionConfigStorage; - private final Provider defaultHeartBeatController; + private final javax.inject.Provider dataCollectionConfigStorage; + private final javax.inject.Provider defaultHeartBeatController; private final List backgroundStateChangeListeners = new CopyOnWriteArrayList<>(); private final List lifecycleListeners = @@ -293,7 +286,14 @@ public static FirebaseApp initializeApp( "FirebaseApp name " + normalizedName + " already exists!"); Preconditions.checkNotNull(applicationContext, "Application context cannot be null."); - firebaseApp = new FirebaseApp(applicationContext, normalizedName, options); + + firebaseApp = + DaggerAppComponent.builder() + .setApplicationContext(applicationContext) + .setName(name) + .setOptions(options) + .build() + .getApp(); INSTANCES.put(normalizedName, firebaseApp); } @@ -401,61 +401,24 @@ public void setDataCollectionDefaultEnabled(boolean enabled) { } /** - * Default constructor. + * Default constructor * * @hide */ - protected FirebaseApp(Context applicationContext, String name, FirebaseOptions options) { - this.applicationContext = Preconditions.checkNotNull(applicationContext); - this.name = Preconditions.checkNotEmpty(name); - this.options = Preconditions.checkNotNull(options); - StartupTime startupTime = FirebaseInitProvider.getStartupTime(); - - FirebaseTrace.pushTrace("Firebase"); - - FirebaseTrace.pushTrace("ComponentDiscovery"); - List> registrars = - ComponentDiscovery.forContext(applicationContext, ComponentDiscoveryService.class) - .discoverLazy(); - FirebaseTrace.popTrace(); // ComponentDiscovery - - FirebaseTrace.pushTrace("Runtime"); - ComponentRuntime.Builder builder = - ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE) - .addLazyComponentRegistrars(registrars) - .addComponentRegistrar(new FirebaseCommonRegistrar()) - .addComponentRegistrar(new ExecutorsRegistrar()) - .addComponent(Component.of(applicationContext, Context.class)) - .addComponent(Component.of(this, FirebaseApp.class)) - .addComponent(Component.of(options, FirebaseOptions.class)) - .setProcessor(new ComponentMonitor()); - - // Don't provide StartupTime in direct boot mode or if Firebase was manually started - if (UserManagerCompat.isUserUnlocked(applicationContext) - && FirebaseInitProvider.isCurrentlyInitializing()) { - builder.addComponent(Component.of(startupTime, StartupTime.class)); - } - - componentRuntime = builder.build(); - FirebaseTrace.popTrace(); // Runtime - - dataCollectionConfigStorage = - new Lazy<>( - () -> - new DataCollectionConfigStorage( - applicationContext, - getPersistenceKey(), - componentRuntime.get(Publisher.class))); - defaultHeartBeatController = componentRuntime.getProvider(DefaultHeartBeatController.class); - - addBackgroundStateChangeListener( - background -> { - if (!background) { - defaultHeartBeatController.get().registerHeartBeat(); - } - }); - - FirebaseTrace.popTrace(); // Firebase + @Inject + protected FirebaseApp( + Context applicationContext, + @Named("appName") String name, + FirebaseOptions options, + ComponentRuntime componentRuntime, + javax.inject.Provider dataCollectionConfigStorage, + javax.inject.Provider heartBeatController) { + this.applicationContext = applicationContext; + this.name = name; + this.options = options; + this.componentRuntime = componentRuntime; + this.dataCollectionConfigStorage = dataCollectionConfigStorage; + this.defaultHeartBeatController = heartBeatController; } private void checkNotDeleted() { @@ -522,10 +485,7 @@ public void removeBackgroundStateChangeListener(BackgroundStateChangeListener li */ @KeepForSdk public String getPersistenceKey() { - return encodeUrlSafeNoPadding(getName().getBytes(Charset.defaultCharset())) - + "+" - + encodeUrlSafeNoPadding( - getOptions().getApplicationId().getBytes(Charset.defaultCharset())); + return getPersistenceKey(getName(), getOptions()); } /** diff --git a/firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java b/firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java new file mode 100644 index 00000000000..623d1143d39 --- /dev/null +++ b/firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java @@ -0,0 +1,6 @@ +package com.google.firebase.annotations; + +import javax.inject.Scope; + +@Scope +public @interface AppScope {} diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java index ff9ba5123fc..5a89217f18c 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java @@ -18,26 +18,28 @@ import android.util.Base64; import android.util.Base64OutputStream; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import androidx.core.os.UserManagerCompat; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import com.google.firebase.FirebaseApp; +import com.google.firebase.annotations.AppScope; import com.google.firebase.annotations.concurrent.Background; import com.google.firebase.components.Component; import com.google.firebase.components.Dependency; import com.google.firebase.components.Qualified; -import com.google.firebase.inject.Provider; import com.google.firebase.platforminfo.UserAgentPublisher; import java.io.ByteArrayOutputStream; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import java.util.zip.GZIPOutputStream; +import javax.inject.Inject; +import javax.inject.Provider; import org.json.JSONArray; import org.json.JSONObject; /** Provides a function to store heartbeats and another function to retrieve stored heartbeats. */ +@AppScope public class DefaultHeartBeatController implements HeartBeatController, HeartBeatInfo { private final Provider storageProvider; @@ -123,11 +125,11 @@ private DefaultHeartBeatController( context); } - @VisibleForTesting + @Inject DefaultHeartBeatController( Provider testStorage, Set consumers, - Executor executor, + @Background Executor executor, Provider userAgentProvider, Context context) { storageProvider = testStorage; @@ -152,7 +154,7 @@ private DefaultHeartBeatController( c.get(Context.class), c.get(FirebaseApp.class).getPersistenceKey(), c.setOf(HeartBeatConsumer.class), - c.getProvider(UserAgentPublisher.class), + () -> c.getProvider(UserAgentPublisher.class).get(), c.get(backgroundExecutor))) .build(); } diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java index 88deb18a36c..6a0f3f62257 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java @@ -19,6 +19,7 @@ import android.os.Build; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; +import com.google.firebase.annotations.AppScope; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; @@ -31,12 +32,15 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; /** * Class responsible for storing all heartbeat related information. * *

This exposes functions to store heartbeats and retrieve them in the form of HeartBeatResult. */ +@AppScope class HeartBeatInfoStorage { private static HeartBeatInfoStorage instance = null; @@ -55,7 +59,9 @@ class HeartBeatInfoStorage { private final SharedPreferences firebaseSharedPreferences; - public HeartBeatInfoStorage(Context applicationContext, String persistenceKey) { + @Inject + public HeartBeatInfoStorage( + Context applicationContext, @Named("persistenceKey") String persistenceKey) { this.firebaseSharedPreferences = applicationContext.getSharedPreferences( HEARTBEAT_PREFERENCES_NAME + persistenceKey, Context.MODE_PRIVATE); diff --git a/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java b/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java index 42faed2d497..75255df5de9 100644 --- a/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java @@ -22,10 +22,14 @@ import androidx.annotation.VisibleForTesting; import androidx.core.content.ContextCompat; import com.google.firebase.DataCollectionDefaultChange; +import com.google.firebase.annotations.AppScope; import com.google.firebase.events.Event; import com.google.firebase.events.Publisher; +import javax.inject.Inject; +import javax.inject.Named; /** Encapsulates data collection configuration. */ +@AppScope public class DataCollectionConfigStorage { private static final String FIREBASE_APP_PREFS = "com.google.firebase.common.prefs:"; @@ -38,8 +42,11 @@ public class DataCollectionConfigStorage { private final Publisher publisher; private boolean dataCollectionDefaultEnabled; + @Inject public DataCollectionConfigStorage( - Context applicationContext, String persistenceKey, Publisher publisher) { + Context applicationContext, + @Named("persistenceKey") String persistenceKey, + Publisher publisher) { this.deviceProtectedContext = directBootSafe(applicationContext); this.sharedPreferences = deviceProtectedContext.getSharedPreferences( diff --git a/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java b/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java index 6a912141461..a1c976a7843 100644 --- a/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java +++ b/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java @@ -14,10 +14,12 @@ package com.google.firebase.platforminfo; +import com.google.firebase.annotations.AppScope; import com.google.firebase.components.Component; import com.google.firebase.components.Dependency; import java.util.Iterator; import java.util.Set; +import javax.inject.Inject; /** * Provides a user agent string that captures the SDKs and their corresponding versions. @@ -25,10 +27,12 @@ *

Example user agent string: "firebase-common/16.1.1 firebase-firestore/16.1.2 * firebase-database/16.1.2" */ +@AppScope public class DefaultUserAgentPublisher implements UserAgentPublisher { private final String javaSDKVersionUserAgent; private final GlobalLibraryVersionRegistrar gamesSDKRegistrar; + @Inject DefaultUserAgentPublisher( Set libraryVersions, GlobalLibraryVersionRegistrar gamesSDKRegistrar) { this.javaSDKVersionUserAgent = toUserAgent(libraryVersions); diff --git a/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java b/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java new file mode 100644 index 00000000000..8845eae347a --- /dev/null +++ b/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java @@ -0,0 +1,18 @@ +package com.google.firebase.platforminfo; + +import com.google.firebase.components.ComponentRuntime; +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import java.util.Set; + +@Module +public interface LibraryVersionsModule { + @Provides + static Set providesLibraryVersions(ComponentRuntime runtime) { + return runtime.setOf(LibraryVersion.class); + } + + @Binds + UserAgentPublisher bindsPublisher(DefaultUserAgentPublisher publisher); +} From 8eb5561d73ffae0961f487e095d00638890f7f81 Mon Sep 17 00:00:00 2001 From: Vladimir Kryachko Date: Fri, 2 Dec 2022 10:38:39 -0500 Subject: [PATCH 2/4] Updates --- .../com/google/firebase/AppComponent.java | 44 ++++++++++++++++--- .../java/com/google/firebase/FirebaseApp.java | 4 +- .../firebase/FirebaseCommonRegistrar.java | 4 -- .../google/firebase/annotations/AppScope.java | 6 --- .../DefaultHeartBeatController.java | 42 +----------------- .../heartbeatinfo/HeartBeatInfoStorage.java | 8 +--- .../internal/DataCollectionConfigStorage.java | 4 +- .../DefaultUserAgentPublisher.java | 17 +------ .../platforminfo/LibraryVersionsModule.java | 14 ++++++ 9 files changed, 61 insertions(+), 82 deletions(-) delete mode 100644 firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java diff --git a/firebase-common/src/main/java/com/google/firebase/AppComponent.java b/firebase-common/src/main/java/com/google/firebase/AppComponent.java index 181ab3f778f..22e68247fb1 100644 --- a/firebase-common/src/main/java/com/google/firebase/AppComponent.java +++ b/firebase-common/src/main/java/com/google/firebase/AppComponent.java @@ -1,8 +1,21 @@ +// Copyright 2022 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; import android.content.Context; import androidx.core.os.UserManagerCompat; -import com.google.firebase.annotations.AppScope; import com.google.firebase.annotations.concurrent.Background; import com.google.firebase.components.ComponentDiscovery; import com.google.firebase.components.ComponentDiscoveryService; @@ -11,10 +24,15 @@ import com.google.firebase.components.Qualified; import com.google.firebase.concurrent.ExecutorsRegistrar; import com.google.firebase.events.Publisher; +import com.google.firebase.heartbeatinfo.DefaultHeartBeatController; import com.google.firebase.heartbeatinfo.HeartBeatConsumer; +import com.google.firebase.heartbeatinfo.HeartBeatController; +import com.google.firebase.heartbeatinfo.HeartBeatInfo; import com.google.firebase.inject.Provider; +import com.google.firebase.platforminfo.DefaultUserAgentPublisher; import com.google.firebase.platforminfo.GlobalLibraryVersionRegistrar; import com.google.firebase.platforminfo.LibraryVersionsModule; +import com.google.firebase.platforminfo.UserAgentPublisher; import com.google.firebase.provider.FirebaseInitProvider; import com.google.firebase.tracing.ComponentMonitor; import com.google.firebase.tracing.FirebaseTrace; @@ -26,10 +44,11 @@ import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Named; +import javax.inject.Singleton; /** @hide */ @Component(modules = {AppComponent.MainModule.class, LibraryVersionsModule.class}) -@AppScope +@Singleton interface AppComponent { FirebaseApp getApp(); @@ -57,11 +76,13 @@ static String providesPersistenceKey(@Named("appName") String name, FirebaseOpti } @Provides - @AppScope + @Singleton static ComponentRuntime provideComponentRuntime( Context applicationContext, FirebaseOptions options, - javax.inject.Provider app) { + javax.inject.Provider app, + DefaultUserAgentPublisher userAgentPublisher, + DefaultHeartBeatController heartBeatController) { FirebaseTrace.pushTrace("ComponentDiscovery"); List> registrars = ComponentDiscovery.forContext(applicationContext, ComponentDiscoveryService.class) @@ -74,6 +95,15 @@ static ComponentRuntime provideComponentRuntime( .addLazyComponentRegistrars(registrars) .addComponentRegistrar(new FirebaseCommonRegistrar()) .addComponentRegistrar(new ExecutorsRegistrar()) + .addComponent( + com.google.firebase.components.Component.of( + userAgentPublisher, UserAgentPublisher.class)) + .addComponent( + com.google.firebase.components.Component.of( + heartBeatController, + DefaultHeartBeatController.class, + HeartBeatController.class, + HeartBeatInfo.class)) .addComponent( com.google.firebase.components.Component.of(applicationContext, Context.class)) .addComponent( @@ -98,20 +128,20 @@ static ComponentRuntime provideComponentRuntime( } @Provides - @AppScope + @Singleton static Publisher providesPublisher(ComponentRuntime runtime) { return runtime.get(Publisher.class); } @Provides - @AppScope + @Singleton static Set providesHeartBeatConsumers(ComponentRuntime runtime) { return runtime.setOf(HeartBeatConsumer.class); } @Provides @Background - @AppScope + @Singleton static Executor providesBgExecutor(ComponentRuntime runtime) { return runtime.get(Qualified.qualified(Background.class, Executor.class)); } diff --git a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java index 4d63fac2b06..2c7eea8a050 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -39,7 +39,6 @@ import com.google.android.gms.common.internal.Preconditions; import com.google.android.gms.common.util.PlatformVersion; import com.google.android.gms.common.util.ProcessUtils; -import com.google.firebase.annotations.AppScope; import com.google.firebase.components.ComponentRuntime; import com.google.firebase.heartbeatinfo.DefaultHeartBeatController; import com.google.firebase.internal.DataCollectionConfigStorage; @@ -53,6 +52,7 @@ import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; /** * The entry point of Firebase SDKs. It holds common configuration and state for Firebase APIs. Most @@ -80,7 +80,7 @@ * Use of Firebase in processes other than the main process is not supported and will likely cause * problems related to resource contention. */ -@AppScope +@Singleton public class FirebaseApp { private static final String LOG_TAG = "FirebaseApp"; diff --git a/firebase-common/src/main/java/com/google/firebase/FirebaseCommonRegistrar.java b/firebase-common/src/main/java/com/google/firebase/FirebaseCommonRegistrar.java index 4fb7c06a620..f6532a50757 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseCommonRegistrar.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseCommonRegistrar.java @@ -19,8 +19,6 @@ import android.os.Build; import com.google.firebase.components.Component; import com.google.firebase.components.ComponentRegistrar; -import com.google.firebase.heartbeatinfo.DefaultHeartBeatController; -import com.google.firebase.platforminfo.DefaultUserAgentPublisher; import com.google.firebase.platforminfo.KotlinDetector; import com.google.firebase.platforminfo.LibraryVersionComponent; import java.util.ArrayList; @@ -42,8 +40,6 @@ public class FirebaseCommonRegistrar implements ComponentRegistrar { @Override public List> getComponents() { List> result = new ArrayList<>(); - result.add(DefaultUserAgentPublisher.component()); - result.add(DefaultHeartBeatController.component()); result.add( LibraryVersionComponent.create(FIREBASE_ANDROID, String.valueOf(Build.VERSION.SDK_INT))); result.add(LibraryVersionComponent.create(FIREBASE_COMMON, BuildConfig.VERSION_NAME)); diff --git a/firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java b/firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java deleted file mode 100644 index 623d1143d39..00000000000 --- a/firebase-common/src/main/java/com/google/firebase/annotations/AppScope.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.google.firebase.annotations; - -import javax.inject.Scope; - -@Scope -public @interface AppScope {} diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java index 5a89217f18c..5880cc636fd 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java @@ -21,12 +21,7 @@ import androidx.core.os.UserManagerCompat; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; -import com.google.firebase.FirebaseApp; -import com.google.firebase.annotations.AppScope; import com.google.firebase.annotations.concurrent.Background; -import com.google.firebase.components.Component; -import com.google.firebase.components.Dependency; -import com.google.firebase.components.Qualified; import com.google.firebase.platforminfo.UserAgentPublisher; import java.io.ByteArrayOutputStream; import java.util.List; @@ -35,11 +30,12 @@ import java.util.zip.GZIPOutputStream; import javax.inject.Inject; import javax.inject.Provider; +import javax.inject.Singleton; import org.json.JSONArray; import org.json.JSONObject; /** Provides a function to store heartbeats and another function to retrieve stored heartbeats. */ -@AppScope +@Singleton public class DefaultHeartBeatController implements HeartBeatController, HeartBeatInfo { private final Provider storageProvider; @@ -111,20 +107,6 @@ public Task getHeartBeatsHeader() { }); } - private DefaultHeartBeatController( - Context context, - String persistenceKey, - Set consumers, - Provider userAgentProvider, - Executor backgroundExecutor) { - this( - () -> new HeartBeatInfoStorage(context, persistenceKey), - consumers, - backgroundExecutor, - userAgentProvider, - context); - } - @Inject DefaultHeartBeatController( Provider testStorage, @@ -139,26 +121,6 @@ private DefaultHeartBeatController( this.applicationContext = context; } - public static @NonNull Component component() { - Qualified backgroundExecutor = Qualified.qualified(Background.class, Executor.class); - return Component.builder( - DefaultHeartBeatController.class, HeartBeatController.class, HeartBeatInfo.class) - .add(Dependency.required(Context.class)) - .add(Dependency.required(FirebaseApp.class)) - .add(Dependency.setOf(HeartBeatConsumer.class)) - .add(Dependency.requiredProvider(UserAgentPublisher.class)) - .add(Dependency.required(backgroundExecutor)) - .factory( - c -> - new DefaultHeartBeatController( - c.get(Context.class), - c.get(FirebaseApp.class).getPersistenceKey(), - c.setOf(HeartBeatConsumer.class), - () -> c.getProvider(UserAgentPublisher.class).get(), - c.get(backgroundExecutor))) - .build(); - } - @Override @NonNull public synchronized HeartBeat getHeartBeatCode(@NonNull String heartBeatTag) { diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java index 6a0f3f62257..b7c307492a4 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java @@ -19,7 +19,6 @@ import android.os.Build; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; -import com.google.firebase.annotations.AppScope; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; @@ -34,20 +33,17 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; /** * Class responsible for storing all heartbeat related information. * *

This exposes functions to store heartbeats and retrieve them in the form of HeartBeatResult. */ -@AppScope +@Singleton class HeartBeatInfoStorage { - private static HeartBeatInfoStorage instance = null; - private static final String GLOBAL = "fire-global"; - private static final String PREFERENCES_NAME = "FirebaseAppHeartBeat"; - private static final String HEARTBEAT_PREFERENCES_NAME = "FirebaseHeartBeat"; private static final String HEART_BEAT_COUNT_TAG = "fire-count"; diff --git a/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java b/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java index 75255df5de9..449857bc4f1 100644 --- a/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/internal/DataCollectionConfigStorage.java @@ -22,14 +22,14 @@ import androidx.annotation.VisibleForTesting; import androidx.core.content.ContextCompat; import com.google.firebase.DataCollectionDefaultChange; -import com.google.firebase.annotations.AppScope; import com.google.firebase.events.Event; import com.google.firebase.events.Publisher; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; /** Encapsulates data collection configuration. */ -@AppScope +@Singleton public class DataCollectionConfigStorage { private static final String FIREBASE_APP_PREFS = "com.google.firebase.common.prefs:"; diff --git a/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java b/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java index a1c976a7843..773fa168f5a 100644 --- a/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java +++ b/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java @@ -14,12 +14,10 @@ package com.google.firebase.platforminfo; -import com.google.firebase.annotations.AppScope; -import com.google.firebase.components.Component; -import com.google.firebase.components.Dependency; import java.util.Iterator; import java.util.Set; import javax.inject.Inject; +import javax.inject.Singleton; /** * Provides a user agent string that captures the SDKs and their corresponding versions. @@ -27,7 +25,7 @@ *

Example user agent string: "firebase-common/16.1.1 firebase-firestore/16.1.2 * firebase-database/16.1.2" */ -@AppScope +@Singleton public class DefaultUserAgentPublisher implements UserAgentPublisher { private final String javaSDKVersionUserAgent; private final GlobalLibraryVersionRegistrar gamesSDKRegistrar; @@ -66,15 +64,4 @@ private static String toUserAgent(Set tokens) { } return sb.toString(); } - - /** Creates a component to codify a user agent string that captures SDK versions. */ - public static Component component() { - return Component.builder(UserAgentPublisher.class) - .add(Dependency.setOf(LibraryVersion.class)) - .factory( - c -> - new DefaultUserAgentPublisher( - c.setOf(LibraryVersion.class), GlobalLibraryVersionRegistrar.getInstance())) - .build(); - } } diff --git a/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java b/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java index 8845eae347a..271eb326149 100644 --- a/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java +++ b/firebase-common/src/main/java/com/google/firebase/platforminfo/LibraryVersionsModule.java @@ -1,3 +1,17 @@ +// Copyright 2022 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.platforminfo; import com.google.firebase.components.ComponentRuntime; From f431361044e1e704d26311fb10d69ba0e7c3fb52 Mon Sep 17 00:00:00 2001 From: Vladimir Kryachko Date: Fri, 2 Dec 2022 10:43:49 -0500 Subject: [PATCH 3/4] Add javadoc. --- .../src/main/java/com/google/firebase/AppComponent.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/firebase-common/src/main/java/com/google/firebase/AppComponent.java b/firebase-common/src/main/java/com/google/firebase/AppComponent.java index 22e68247fb1..5e4aa157b14 100644 --- a/firebase-common/src/main/java/com/google/firebase/AppComponent.java +++ b/firebase-common/src/main/java/com/google/firebase/AppComponent.java @@ -46,7 +46,13 @@ import javax.inject.Named; import javax.inject.Singleton; -/** @hide */ +/** + * Main Dagger entry point, used to initialize {@link FirebaseApp}s. + * + *

Use by {@link FirebaseApp#initializeApp(Context, FirebaseOptions, String)}. + * + * @hide + */ @Component(modules = {AppComponent.MainModule.class, LibraryVersionsModule.class}) @Singleton interface AppComponent { From fde652de3cdcbab975648f3de143d0d1e3c77f46 Mon Sep 17 00:00:00 2001 From: Vladimir Kryachko Date: Fri, 2 Dec 2022 11:17:09 -0500 Subject: [PATCH 4/4] Fix dependency cycle. --- .../heartbeatinfo/DefaultHeartBeatController.java | 14 +++++++------- .../platforminfo/DefaultUserAgentPublisher.java | 14 +++++++++----- .../DefaultHeartBeatControllerTest.java | 12 ++++++++---- .../DefaultUserAgentPublisherTest.java | 7 ++++--- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java index 5880cc636fd..d9d2dad7415 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java @@ -44,12 +44,12 @@ public class DefaultHeartBeatController implements HeartBeatController, HeartBea private final Provider userAgentProvider; - private final Set consumers; + private final Provider> consumers; - private final Executor backgroundExecutor; + private final Provider backgroundExecutor; public Task registerHeartBeat() { - if (consumers.size() <= 0) { + if (consumers.get().size() <= 0) { return Tasks.forResult(null); } boolean inDirectBoot = !UserManagerCompat.isUserUnlocked(applicationContext); @@ -58,7 +58,7 @@ public Task registerHeartBeat() { } return Tasks.call( - backgroundExecutor, + backgroundExecutor.get(), () -> { synchronized (DefaultHeartBeatController.this) { this.storageProvider @@ -78,7 +78,7 @@ public Task getHeartBeatsHeader() { return Tasks.forResult(""); } return Tasks.call( - backgroundExecutor, + backgroundExecutor.get(), () -> { synchronized (DefaultHeartBeatController.this) { HeartBeatInfoStorage storage = this.storageProvider.get(); @@ -110,8 +110,8 @@ public Task getHeartBeatsHeader() { @Inject DefaultHeartBeatController( Provider testStorage, - Set consumers, - @Background Executor executor, + Provider> consumers, + @Background Provider executor, Provider userAgentProvider, Context context) { storageProvider = testStorage; diff --git a/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java b/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java index 773fa168f5a..963efc63b91 100644 --- a/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java +++ b/firebase-common/src/main/java/com/google/firebase/platforminfo/DefaultUserAgentPublisher.java @@ -17,6 +17,7 @@ import java.util.Iterator; import java.util.Set; import javax.inject.Inject; +import javax.inject.Provider; import javax.inject.Singleton; /** @@ -27,13 +28,14 @@ */ @Singleton public class DefaultUserAgentPublisher implements UserAgentPublisher { - private final String javaSDKVersionUserAgent; + private final Provider> libraryVersions; private final GlobalLibraryVersionRegistrar gamesSDKRegistrar; @Inject DefaultUserAgentPublisher( - Set libraryVersions, GlobalLibraryVersionRegistrar gamesSDKRegistrar) { - this.javaSDKVersionUserAgent = toUserAgent(libraryVersions); + Provider> libraryVersions, + GlobalLibraryVersionRegistrar gamesSDKRegistrar) { + this.libraryVersions = libraryVersions; this.gamesSDKRegistrar = gamesSDKRegistrar; } @@ -46,10 +48,12 @@ public class DefaultUserAgentPublisher implements UserAgentPublisher { @Override public String getUserAgent() { if (gamesSDKRegistrar.getRegisteredVersions().isEmpty()) { - return javaSDKVersionUserAgent; + return toUserAgent(libraryVersions.get()); } - return javaSDKVersionUserAgent + ' ' + toUserAgent(gamesSDKRegistrar.getRegisteredVersions()); + return toUserAgent(libraryVersions.get()) + + ' ' + + toUserAgent(gamesSDKRegistrar.getRegisteredVersions()); } private static String toUserAgent(Set tokens) { diff --git a/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java b/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java index a39be1fe512..2894b3b1bbf 100644 --- a/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java +++ b/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java @@ -66,14 +66,18 @@ public void setUp() { when(publisher.getUserAgent()).thenReturn(DEFAULT_USER_AGENT); heartBeatController = new DefaultHeartBeatController( - () -> storage, logSources, executor, () -> publisher, applicationContext); + () -> storage, () -> logSources, () -> executor, () -> publisher, applicationContext); } @Test public void whenNoSource_dontStoreHeartBeat() throws InterruptedException, TimeoutException { DefaultHeartBeatController controller = new DefaultHeartBeatController( - () -> storage, new HashSet<>(), executor, () -> publisher, applicationContext); + () -> storage, + Collections::emptySet, + () -> executor, + () -> publisher, + applicationContext); await(controller.registerHeartBeat()); verify(storage, times(0)).storeHeartBeat(anyLong(), anyString()); } @@ -117,7 +121,7 @@ public void firstNewThenOld_synchronizedCorrectly() new HeartBeatInfoStorage(heartBeatSharedPreferences); DefaultHeartBeatController controller = new DefaultHeartBeatController( - () -> heartBeatInfoStorage, logSources, executor, () -> publisher, context); + () -> heartBeatInfoStorage, () -> logSources, () -> executor, () -> publisher, context); String emptyString = Base64.getUrlEncoder() .withoutPadding() @@ -140,7 +144,7 @@ public void firstOldThenNew_synchronizedCorrectly() new HeartBeatInfoStorage(heartBeatSharedPreferences); DefaultHeartBeatController controller = new DefaultHeartBeatController( - () -> heartBeatInfoStorage, logSources, executor, () -> publisher, context); + () -> heartBeatInfoStorage, () -> logSources, () -> executor, () -> publisher, context); String emptyString = compress("{\"heartbeats\":[],\"version\":\"2\"}"); await(controller.registerHeartBeat()); int heartBeatCode = controller.getHeartBeatCode("test").getCode(); diff --git a/firebase-common/src/test/java/com/google/firebase/platforminfo/DefaultUserAgentPublisherTest.java b/firebase-common/src/test/java/com/google/firebase/platforminfo/DefaultUserAgentPublisherTest.java index 9fbfde92ead..43bfbc1edd9 100644 --- a/firebase-common/src/test/java/com/google/firebase/platforminfo/DefaultUserAgentPublisherTest.java +++ b/firebase-common/src/test/java/com/google/firebase/platforminfo/DefaultUserAgentPublisherTest.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.when; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.junit.Before; @@ -43,7 +44,7 @@ public void before() { when(globalLibraryVersionRegistrar.getRegisteredVersions()).thenReturn(new HashSet<>()); userAgentPublisher = - new DefaultUserAgentPublisher(libraryVersions, globalLibraryVersionRegistrar); + new DefaultUserAgentPublisher(() -> libraryVersions, globalLibraryVersionRegistrar); } @Test @@ -59,7 +60,7 @@ public void getUserAgent_createsConcatenatedStringOfSdkVersions() { @Test public void getUserAgent_returnsEmptyString_whenVersionSetIsEmpty() { userAgentPublisher = - new DefaultUserAgentPublisher(new HashSet<>(), globalLibraryVersionRegistrar); + new DefaultUserAgentPublisher(Collections::emptySet, globalLibraryVersionRegistrar); assertThat(userAgentPublisher.getUserAgent()).isEqualTo(""); } @@ -73,7 +74,7 @@ public void getUserAgent_returnsEmptyString_whenVersionSetIsEmpty() { gamesLibraryVersions.add(LibraryVersion.create("buzz", "2")); when(globalLibraryVersionRegistrar.getRegisteredVersions()).thenReturn(gamesLibraryVersions); userAgentPublisher = - new DefaultUserAgentPublisher(libraryVersions, globalLibraryVersionRegistrar); + new DefaultUserAgentPublisher(() -> libraryVersions, globalLibraryVersionRegistrar); String[] actualUserAgent = userAgentPublisher.getUserAgent().split(" "); Arrays.sort(actualUserAgent);