Home | History | Annotate | Download | only in internal
      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