diff --git a/firebase-functions/CHANGELOG.md b/firebase-functions/CHANGELOG.md index 102fbfa7fa8..ffd54ae2733 100644 --- a/firebase-functions/CHANGELOG.md +++ b/firebase-functions/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased * [changed] Avoid executing code on the UI thread as much as possible. +* [changed] Internal infrastructure improvements. # 20.2.1 * [changed] Updated dependency of `firebase-iid` to its latest diff --git a/firebase-functions/firebase-functions.gradle b/firebase-functions/firebase-functions.gradle index 2f81c7d30a2..84975158b39 100644 --- a/firebase-functions/firebase-functions.gradle +++ b/firebase-functions/firebase-functions.gradle @@ -14,6 +14,7 @@ plugins { id 'firebase-library' + id 'firebase-vendor' } firebaseLibrary { @@ -68,6 +69,12 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:3.12.1' + 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' + annotationProcessor 'com.google.auto.value:auto-value:1.6.2' javadocClasspath 'com.google.code.findbugs:jsr305:3.0.2' diff --git a/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java b/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java index 8f38593e85a..4029cfdc384 100644 --- a/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java +++ b/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java @@ -85,7 +85,6 @@ public void testToken() throws InterruptedException, ExecutionException { // Override the normal token provider to simulate FirebaseAuth being logged in. FirebaseFunctions functions = new FirebaseFunctions( - app, app.getApplicationContext(), app.getOptions().getProjectId(), "us-central1", @@ -108,7 +107,6 @@ public void testInstanceId() throws InterruptedException, ExecutionException { // Override the normal token provider to simulate FirebaseAuth being logged in. FirebaseFunctions functions = new FirebaseFunctions( - app, app.getApplicationContext(), app.getOptions().getProjectId(), "us-central1", @@ -131,7 +129,6 @@ public void testAppCheck() throws InterruptedException, ExecutionException { // Override the normal token provider to simulate FirebaseAuth being logged in. FirebaseFunctions functions = new FirebaseFunctions( - app, app.getApplicationContext(), app.getOptions().getProjectId(), "us-central1", diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.java b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.java index 0fcae7aef87..a3b85444b24 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.java @@ -26,8 +26,11 @@ import com.google.firebase.internal.api.FirebaseNoSignedInUserException; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; +import javax.inject.Inject; +import javax.inject.Singleton; /** A ContextProvider that uses FirebaseAuth to get the token. */ +@Singleton class FirebaseContextProvider implements ContextProvider { private final String TAG = "FirebaseContextProvider"; @@ -37,6 +40,7 @@ class FirebaseContextProvider implements ContextProvider { new AtomicReference<>(); private final Executor executor; + @Inject FirebaseContextProvider( Provider tokenProvider, Provider instanceId, diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java index 1ebe2d9c441..01877019f2d 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java @@ -30,6 +30,8 @@ import com.google.firebase.annotations.concurrent.UiThread; import com.google.firebase.emulators.EmulatedServiceSettings; import com.google.firebase.functions.FirebaseFunctionsException.Code; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedInject; import java.io.IOException; import java.io.InterruptedIOException; import java.net.MalformedURLException; @@ -37,6 +39,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; +import javax.inject.Named; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; @@ -59,9 +62,6 @@ public class FirebaseFunctions { */ private static boolean providerInstallStarted = false; - // The FirebaseApp instance - private final FirebaseApp app; - // The network client to use for HTTPS requests. private final OkHttpClient client; @@ -88,15 +88,14 @@ public class FirebaseFunctions { // Emulator settings @Nullable private EmulatedServiceSettings emulatorSettings; + @AssistedInject FirebaseFunctions( - FirebaseApp app, Context context, - String projectId, - String regionOrCustomDomain, + @Named("projectId") String projectId, + @Assisted String regionOrCustomDomain, ContextProvider contextProvider, @Lightweight Executor executor, @UiThread Executor uiExecutor) { - this.app = app; this.executor = executor; this.client = new OkHttpClient(); this.serializer = new Serializer(); diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsComponent.java b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsComponent.java new file mode 100644 index 00000000000..f4e2409701a --- /dev/null +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsComponent.java @@ -0,0 +1,78 @@ +// Copyright 2018 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.functions; + +import android.content.Context; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.annotations.concurrent.Lightweight; +import com.google.firebase.annotations.concurrent.UiThread; +import com.google.firebase.appcheck.interop.InternalAppCheckTokenProvider; +import com.google.firebase.auth.internal.InternalAuthProvider; +import com.google.firebase.iid.internal.FirebaseInstanceIdInternal; +import com.google.firebase.inject.Deferred; +import com.google.firebase.inject.Provider; +import dagger.Binds; +import dagger.BindsInstance; +import dagger.Component; +import dagger.Module; +import dagger.Provides; +import java.util.concurrent.Executor; +import javax.inject.Named; +import javax.inject.Singleton; + +/** @hide */ +@Component(modules = FunctionsComponent.MainModule.class) +@Singleton +interface FunctionsComponent { + FunctionsMultiResourceComponent getMultiResourceComponent(); + + @Component.Builder + interface Builder { + @BindsInstance + Builder setApplicationContext(Context applicationContext); + + @BindsInstance + Builder setFirebaseOptions(FirebaseOptions options); + + @BindsInstance + Builder setLiteExecutor(@Lightweight Executor executor); + + @BindsInstance + Builder setUiExecutor(@UiThread Executor executor); + + @BindsInstance + Builder setAuth(Provider auth); + + @BindsInstance + Builder setIid(Provider iid); + + @BindsInstance + Builder setAppCheck(Deferred appCheck); + + FunctionsComponent build(); + } + + @Module + interface MainModule { + @Provides + @Named("projectId") + static String bindProjectId(FirebaseOptions options) { + return options.getProjectId(); + } + + @Binds + ContextProvider contextProvider(FirebaseContextProvider provider); + } +} diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java index 56fad42e8ba..4a36b27297f 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java @@ -14,16 +14,16 @@ package com.google.firebase.functions; -import android.content.Context; import androidx.annotation.GuardedBy; -import androidx.annotation.UiThread; -import com.google.firebase.FirebaseApp; -import com.google.firebase.annotations.concurrent.Lightweight; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Executor; +import javax.inject.Inject; +import javax.inject.Singleton; /** Multi-resource container for Functions. */ +@Singleton class FunctionsMultiResourceComponent { /** * A static map from instance key to FirebaseFunctions instances. Instance keys region names. @@ -33,41 +33,24 @@ class FunctionsMultiResourceComponent { @GuardedBy("this") private final Map instances = new HashMap<>(); - private final Context applicationContext; - private final ContextProvider contextProvider; - private final FirebaseApp app; - private final Executor liteExecutor; - private final Executor uiExecutor; + private final FirebaseFunctionsFactory functionsFactory; - FunctionsMultiResourceComponent( - Context applicationContext, - ContextProvider contextProvider, - FirebaseApp app, - @Lightweight Executor liteExecutor, - @UiThread Executor uiExecutor) { - this.applicationContext = applicationContext; - this.contextProvider = contextProvider; - this.app = app; - this.liteExecutor = liteExecutor; - this.uiExecutor = uiExecutor; + @Inject + FunctionsMultiResourceComponent(FirebaseFunctionsFactory functionsFactory) { + this.functionsFactory = functionsFactory; } synchronized FirebaseFunctions get(String regionOrCustomDomain) { FirebaseFunctions functions = instances.get(regionOrCustomDomain); - String projectId = app.getOptions().getProjectId(); - if (functions == null) { - functions = - new FirebaseFunctions( - app, - applicationContext, - projectId, - regionOrCustomDomain, - contextProvider, - liteExecutor, - uiExecutor); + functions = functionsFactory.create(regionOrCustomDomain); instances.put(regionOrCustomDomain, functions); } return functions; } + + @AssistedFactory + interface FirebaseFunctionsFactory { + FirebaseFunctions create(@Assisted String regionOrCustomDomain); + } } diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java index 8e50b8f699e..0f9d1e4c1f7 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java @@ -16,7 +16,7 @@ import android.content.Context; import androidx.annotation.Keep; -import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; import com.google.firebase.annotations.concurrent.Lightweight; import com.google.firebase.annotations.concurrent.UiThread; import com.google.firebase.appcheck.interop.InternalAppCheckTokenProvider; @@ -45,34 +45,27 @@ public List> getComponents() { Qualified liteExecutor = Qualified.qualified(Lightweight.class, Executor.class); Qualified uiExecutor = Qualified.qualified(UiThread.class, Executor.class); return Arrays.asList( - Component.builder(ContextProvider.class) - .add(Dependency.optionalProvider(InternalAuthProvider.class)) - .add(Dependency.requiredProvider(FirebaseInstanceIdInternal.class)) - .add(Dependency.deferred(InternalAppCheckTokenProvider.class)) - .add(Dependency.required(liteExecutor)) - .factory( - c -> - new FirebaseContextProvider( - c.getProvider(InternalAuthProvider.class), - c.getProvider(FirebaseInstanceIdInternal.class), - c.getDeferred(InternalAppCheckTokenProvider.class), - c.get(liteExecutor))) - .build(), Component.builder(FunctionsMultiResourceComponent.class) .name(LIBRARY_NAME) .add(Dependency.required(Context.class)) - .add(Dependency.required(ContextProvider.class)) - .add(Dependency.required(FirebaseApp.class)) + .add(Dependency.required(FirebaseOptions.class)) + .add(Dependency.optionalProvider(InternalAuthProvider.class)) + .add(Dependency.requiredProvider(FirebaseInstanceIdInternal.class)) + .add(Dependency.deferred(InternalAppCheckTokenProvider.class)) .add(Dependency.required(liteExecutor)) .add(Dependency.required(uiExecutor)) .factory( c -> - new FunctionsMultiResourceComponent( - c.get(Context.class), - c.get(ContextProvider.class), - c.get(FirebaseApp.class), - c.get(liteExecutor), - c.get(uiExecutor))) + DaggerFunctionsComponent.builder() + .setApplicationContext(c.get(Context.class)) + .setFirebaseOptions(c.get(FirebaseOptions.class)) + .setLiteExecutor(c.get(liteExecutor)) + .setUiExecutor(c.get(uiExecutor)) + .setAuth(c.getProvider(InternalAuthProvider.class)) + .setIid(c.getProvider(FirebaseInstanceIdInternal.class)) + .setAppCheck(c.getDeferred(InternalAppCheckTokenProvider.class)) + .build() + .getMultiResourceComponent()) .build(), LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)); } diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.java b/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.java index ec880d92a2d..52201464b58 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.java @@ -38,7 +38,7 @@ class Serializer { private final DateFormat dateFormat; - public Serializer() { + Serializer() { // Encode Dates as UTC ISO 8601 strings. dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));