Skip to content
1 change: 1 addition & 0 deletions firebase-inappmessaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Unreleased
* [changed] Migrate firebase-inappmessaging SDK to use common executor pool.

# 20.2.0
* [fixed] Fixed a bug that prevented marking more than one message as
Expand Down
1 change: 1 addition & 0 deletions firebase-inappmessaging/firebase-inappmessaging.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ dependencies {
testImplementation "io.grpc:grpc-testing:$grpcVersion"
testImplementation 'com.google.guava:guava:30.1-android'
testImplementation 'androidx.test:core:1.2.0'
testImplementation project(":integ-testing")

androidTestImplementation project(':integ-testing')
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.analytics.connector.AnalyticsConnector;
import com.google.firebase.concurrent.TestOnlyExecutors;
import com.google.firebase.events.Subscriber;
import com.google.firebase.inappmessaging.CommonTypesProto.Event;
import com.google.firebase.inappmessaging.CommonTypesProto.Priority;
Expand All @@ -65,6 +66,7 @@
import com.google.firebase.inappmessaging.internal.TestDeviceHelper;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.GrpcClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
import com.google.firebase.inappmessaging.model.BannerMessage;
Expand Down Expand Up @@ -270,7 +272,9 @@ public void setUp() {
.testSystemClockModule(new TestSystemClockModule(NOW))
.programmaticContextualTriggerFlowableModule(
new ProgrammaticContextualTriggerFlowableModule(
new ProgramaticContextualTriggers()));
new ProgramaticContextualTriggers()))
.executorsModule(
new ExecutorsModule(TestOnlyExecutors.background(), TestOnlyExecutors.blocking()));

TestUniversalComponent universalComponent = universalComponentBuilder.build();

Expand Down Expand Up @@ -315,6 +319,8 @@ public void onAppOpen_whenAnalyticsAbsent_notifiesSubscriber() {
TestUniversalComponent analyticsLessUniversalComponent =
universalComponentBuilder
.appMeasurementModule(new AppMeasurementModule(handler -> {}, firebaseEventSubscriber))
.executorsModule(
new ExecutorsModule(TestOnlyExecutors.background(), TestOnlyExecutors.blocking()))
.build();
TestAppComponent appComponent =
appComponentBuilder.universalComponent(analyticsLessUniversalComponent).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.firebase.inappmessaging.internal.injection.modules.AnalyticsEventsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProtoStorageClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.RateLimitModule;
Expand All @@ -41,5 +42,6 @@
ProtoStorageClientModule.class,
RateLimitModule.class,
AppMeasurementModule.class,
ExecutorsModule.class,
})
public interface TestUniversalComponent extends UniversalComponent {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
import com.google.firebase.abt.FirebaseABTesting;
import com.google.firebase.abt.component.AbtComponent;
import com.google.firebase.analytics.connector.AnalyticsConnector;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentContainer;
import com.google.firebase.components.ComponentRegistrar;
import com.google.firebase.components.Dependency;
import com.google.firebase.components.Qualified;
import com.google.firebase.events.Subscriber;
import com.google.firebase.inappmessaging.internal.AbtIntegrationHelper;
import com.google.firebase.inappmessaging.internal.ProgramaticContextualTriggers;
Expand All @@ -37,13 +40,15 @@
import com.google.firebase.inappmessaging.internal.injection.modules.ApiClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.GrpcClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
import com.google.firebase.inject.Deferred;
import com.google.firebase.installations.FirebaseInstallationsApi;
import com.google.firebase.platforminfo.LibraryVersionComponent;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;

/**
* Registers {@link FirebaseInAppMessaging}.
Expand All @@ -53,6 +58,10 @@
@Keep
public class FirebaseInAppMessagingRegistrar implements ComponentRegistrar {
private static final String LIBRARY_NAME = "fire-fiam";
private Qualified<Executor> backgroundExecutor =
Qualified.qualified(Background.class, Executor.class);
private Qualified<Executor> blockingExecutor =
Qualified.qualified(Blocking.class, Executor.class);

@Override
@Keep
Expand All @@ -67,6 +76,8 @@ public List<Component<?>> getComponents() {
.add(Dependency.deferred(AnalyticsConnector.class))
.add(Dependency.required(TransportFactory.class))
.add(Dependency.required(Subscriber.class))
.add(Dependency.required(backgroundExecutor))
.add(Dependency.required(blockingExecutor))
.factory(this::providesFirebaseInAppMessaging)
.eagerInDefaultApp()
.build(),
Expand All @@ -91,6 +102,9 @@ private FirebaseInAppMessaging providesFirebaseInAppMessaging(ComponentContainer
.programmaticContextualTriggerFlowableModule(
new ProgrammaticContextualTriggerFlowableModule(
new ProgramaticContextualTriggers()))
.executorsModule(
new ExecutorsModule(
container.get(backgroundExecutor), container.get(blockingExecutor)))
.build();

AppComponent instance =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,26 @@

package com.google.firebase.inappmessaging.internal;

import android.annotation.SuppressLint;
import androidx.annotation.VisibleForTesting;
import com.google.firebase.abt.AbtException;
import com.google.firebase.abt.AbtExperimentInfo;
import com.google.firebase.abt.FirebaseABTesting;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.inappmessaging.ExperimentPayloadProto;
import com.google.firebase.inappmessaging.internal.injection.scopes.FirebaseAppScope;
import com.google.internal.firebase.inappmessaging.v1.CampaignProto;
import com.google.internal.firebase.inappmessaging.v1.sdkserving.FetchEligibleCampaignsResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.inject.Inject;

/** @hide */
@FirebaseAppScope
public class AbtIntegrationHelper {
private final FirebaseABTesting abTesting;

// TODO(b/258280977): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
@VisibleForTesting
Executor executor = Executors.newSingleThreadExecutor();
@Inject @Blocking @VisibleForTesting Executor executor;

@Inject
public AbtIntegrationHelper(FirebaseABTesting abTesting) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@

package com.google.firebase.inappmessaging.internal;

import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.inappmessaging.FirebaseInAppMessagingClickListener;
import com.google.firebase.inappmessaging.FirebaseInAppMessagingDismissListener;
import com.google.firebase.inappmessaging.FirebaseInAppMessagingDisplayCallbacks;
Expand All @@ -25,13 +24,7 @@
import com.google.firebase.inappmessaging.model.InAppMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* A class used to manage and schedule events to registered (ie: developer-defined) or expensive
Expand All @@ -42,11 +35,7 @@
@SuppressWarnings("JavaDoc")
public class DeveloperListenerManager {

// We limit to 1 so there is minimial impact to device performance
private static final int POOL_SIZE = 1;
// Keep alive to minimize chance of having to restart a thread to handle both impression and click
private static final int KEEP_ALIVE_TIME_SECONDS = 15;
public static DeveloperListenerManager instance = new DeveloperListenerManager();
private final Executor backgroundExecutor;
private Map<FirebaseInAppMessagingClickListener, ClicksExecutorAndListener>
registeredClickListeners = new HashMap<>();
private Map<FirebaseInAppMessagingDismissListener, DismissExecutorAndListener>
Expand All @@ -56,28 +45,15 @@ public class DeveloperListenerManager {
private Map<FirebaseInAppMessagingImpressionListener, ImpressionExecutorAndListener>
registeredImpressionListeners = new HashMap<>();

private static BlockingQueue<Runnable> mCallbackQueue = new LinkedBlockingQueue<>();

// TODO(b/258280977): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
private static final ThreadPoolExecutor CALLBACK_QUEUE_EXECUTOR =
new ThreadPoolExecutor(
POOL_SIZE,
POOL_SIZE,
KEEP_ALIVE_TIME_SECONDS,
TimeUnit.SECONDS,
mCallbackQueue,
new FIAMThreadFactory("EventListeners-"));

static {
CALLBACK_QUEUE_EXECUTOR.allowCoreThreadTimeOut(true);
public DeveloperListenerManager(@Background Executor backgroundExecutor) {
this.backgroundExecutor = backgroundExecutor;
}

// Used internally by MetricsLoggerClient
public void impressionDetected(InAppMessage inAppMessage) {
for (ImpressionExecutorAndListener listener : registeredImpressionListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().impressionDetected(inAppMessage));
}
}
Expand All @@ -87,23 +63,23 @@ public void displayErrorEncountered(
FirebaseInAppMessagingDisplayCallbacks.InAppMessagingErrorReason errorReason) {
for (ErrorsExecutorAndListener listener : registeredErrorListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().displayErrorEncountered(inAppMessage, errorReason));
}
}

public void messageClicked(InAppMessage inAppMessage, Action action) {
for (ClicksExecutorAndListener listener : registeredClickListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().messageClicked(inAppMessage, action));
}
}

public void messageDismissed(InAppMessage inAppMessage) {
for (DismissExecutorAndListener listener : registeredDismissListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().messageDismissed(inAppMessage));
}
}
Expand Down Expand Up @@ -175,29 +151,6 @@ public void removeAllListeners() {
registeredErrorListeners.clear();
}

/** The thread factory for Storage threads. */
static class FIAMThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String mNameSuffix;

FIAMThreadFactory(@NonNull String suffix) {
mNameSuffix = suffix;
}

@SuppressWarnings("ThreadPriorityCheck")
@Override
// TODO(b/258280977): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
public Thread newThread(@NonNull Runnable r) {
Thread t = new Thread(r, "FIAM-" + mNameSuffix + threadNumber.getAndIncrement());
t.setDaemon(false);
t.setPriority(
android.os.Process.THREAD_PRIORITY_BACKGROUND
+ android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE);
return t;
}
}

private abstract static class ExecutorAndListener<T> {

private final Executor executor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.firebase.inappmessaging.internal.injection.modules.AnalyticsEventsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ForegroundFlowableModule;
import com.google.firebase.inappmessaging.internal.injection.modules.GrpcChannelModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
Expand Down Expand Up @@ -64,7 +65,8 @@
ProtoStorageClientModule.class,
SystemClockModule.class,
RateLimitModule.class,
AppMeasurementModule.class
AppMeasurementModule.class,
ExecutorsModule.class
})
public interface UniversalComponent {
ProviderInstaller probiderInstaller();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
package com.google.firebase.inappmessaging.internal.injection.modules;

import android.app.Application;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.inappmessaging.internal.DeveloperListenerManager;
import dagger.Module;
import dagger.Provides;
import java.util.concurrent.Executor;
import javax.inject.Singleton;

/**
Expand All @@ -41,7 +43,8 @@ public Application providesApplication() {

@Provides
@Singleton
public DeveloperListenerManager developerListenerManager() {
return new DeveloperListenerManager();
public DeveloperListenerManager developerListenerManager(
@Background Executor backgroundExecutor) {
return new DeveloperListenerManager(backgroundExecutor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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.inappmessaging.internal.injection.modules;

import androidx.annotation.NonNull;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import dagger.Module;
import dagger.Provides;
import java.util.concurrent.Executor;
import javax.inject.Singleton;

/** Provides executors for running tasks. */
@Module
public class ExecutorsModule {
private final Executor backgroundExecutor;
private final Executor blockingExecutor;

public ExecutorsModule(
@NonNull @Background Executor backgroundExecutor,
@NonNull @Blocking Executor blockingExecutor) {
this.backgroundExecutor = backgroundExecutor;
this.blockingExecutor = blockingExecutor;
}

@Provides
@Singleton
@Background
@NonNull
public Executor providesBackgroundExecutor() {
return backgroundExecutor;
}

@Provides
@Singleton
@Blocking
@NonNull
public Executor providesBlockingExecutor() {
return blockingExecutor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.concurrent.TestOnlyExecutors;
import com.google.firebase.inappmessaging.CommonTypesProto.Event;
import com.google.firebase.inappmessaging.CommonTypesProto.Priority;
import com.google.firebase.inappmessaging.CommonTypesProto.TriggeringCondition;
Expand Down Expand Up @@ -149,7 +150,11 @@ public Builder toBuilder() {
@Mock private DisplayCallbacksFactory displayCallbacksFactory;
@Mock private FirebaseInAppMessagingDisplayCallbacks displayCallbacks;
@Mock private ProgramaticContextualTriggers programaticContextualTriggers;
@Mock DeveloperListenerManager developerListenerManager = new DeveloperListenerManager();

@Mock
DeveloperListenerManager developerListenerManager =
new DeveloperListenerManager(TestOnlyExecutors.background());

FirebaseApp firebaseApp1;
FirebaseOptions options;

Expand Down
Loading