1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.setupcompat.internal; 18 19 import androidx.annotation.Nullable; 20 import androidx.annotation.VisibleForTesting; 21 import java.util.concurrent.ArrayBlockingQueue; 22 import java.util.concurrent.Executor; 23 import java.util.concurrent.ExecutorService; 24 import java.util.concurrent.ThreadPoolExecutor; 25 import java.util.concurrent.TimeUnit; 26 27 /** 28 * Utility class to provide executors. 29 * 30 * <p>It allows the executors to be mocked in Robolectric, redirecting to Robolectric's schedulers 31 * rather than using real threads. 32 */ 33 public final class ExecutorProvider<T extends Executor> { 34 35 private static final int SETUP_METRICS_LOGGER_MAX_QUEUED = 50; 36 private static final int SETUP_COMPAT_BINDBACK_MAX_QUEUED = 1; 37 /** 38 * Creates a single threaded {@link ExecutorService} with a maximum pool size {@code maxSize}. 39 * Jobs submitted when the pool is full causes {@link 40 * java.util.concurrent.RejectedExecutionException} to be thrown. 41 */ 42 public static final ExecutorProvider<ExecutorService> setupCompatServiceInvoker = 43 new ExecutorProvider<>( 44 createSizeBoundedExecutor("SetupCompatServiceInvoker", SETUP_METRICS_LOGGER_MAX_QUEUED)); 45 46 public static final ExecutorProvider<ExecutorService> setupCompatExecutor = 47 new ExecutorProvider<>( 48 createSizeBoundedExecutor( 49 "SetupBindbackServiceExecutor", SETUP_COMPAT_BINDBACK_MAX_QUEUED)); 50 51 private final T executor; 52 53 @Nullable private T injectedExecutor; 54 55 private ExecutorProvider(T executor) { 56 this.executor = executor; 57 } 58 59 public T get() { 60 if (injectedExecutor != null) { 61 return injectedExecutor; 62 } 63 return executor; 64 } 65 66 /** 67 * Injects an executor for testing use for this provider. Subsequent calls to {@link #get} will 68 * return this instance instead, until {@link #resetExecutors()} is called. 69 */ 70 @VisibleForTesting 71 public void injectExecutor(T executor) { 72 this.injectedExecutor = executor; 73 } 74 75 @VisibleForTesting 76 public static void resetExecutors() { 77 setupCompatServiceInvoker.injectedExecutor = null; 78 } 79 80 @VisibleForTesting 81 public static ExecutorService createSizeBoundedExecutor(String threadName, int maxSize) { 82 return new ThreadPoolExecutor( 83 /* corePoolSize= */ 1, 84 /* maximumPoolSize= */ 1, 85 /* keepAliveTime= */ 0, 86 TimeUnit.SECONDS, 87 new ArrayBlockingQueue<>(maxSize), 88 runnable -> new Thread(runnable, threadName)); 89 } 90 } 91