// Copyright 2016 Google Inc. All Rights Reserved. package com.android.contacts.util.concurrent; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import androidx.annotation.NonNull; import com.google.common.util.concurrent.ForwardingFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.Delayed; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RunnableScheduledFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * Provides some common executors for use with {@link Futures} */ public class ContactsExecutors { private ContactsExecutors() {} private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; // AsyncTask.THREAD_POOL_EXECUTOR is a ThreadPoolExecutor so we should end up always using that // but we have a fallback in case the platform implementation changes in some future release. private static final ListeningExecutorService DEFAULT_THREAD_POOL_EXECUTOR = (AsyncTask.THREAD_POOL_EXECUTOR instanceof ExecutorService) ? MoreExecutors.listeningDecorator( (ExecutorService) AsyncTask.THREAD_POOL_EXECUTOR) : MoreExecutors.listeningDecorator( Executors.newFixedThreadPool(CORE_POOL_SIZE)); // We initialize this lazily since in some cases we may never even read from the SIM card private static ListeningExecutorService sSimExecutor; /** * Returns the default thread pool that can be used for background work. */ public static ListeningExecutorService getDefaultThreadPoolExecutor() { return DEFAULT_THREAD_POOL_EXECUTOR; } /** * Creates an executor that runs commands on the application UI thread */ public static ScheduledExecutorService newUiThreadExecutor() { return newHandlerExecutor(new Handler(Looper.getMainLooper())); } /** * Create an executor that posts commands to the provided handler */ public static ScheduledExecutorService newHandlerExecutor(final Handler handler) { return new HandlerExecutorService(handler); } /** * Returns an ExecutorService that can be used to read from the SIM card. * *
See b/32831092
*A different executor than {@link ContactsExecutors#getDefaultThreadPoolExecutor()} is * provided for this case because reads of the SIM card can block for long periods of time * and if they do we might exhaust our thread pool. Additionally it appears that reading from * the SIM provider from multiple threads concurrently can cause problems. *
*/ public synchronized static ListeningExecutorService getSimReadExecutor() { if (sSimExecutor == null) { final ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueueThis class is useful for testability because Handler can't be mocked since it's * methods are final. It might be better to just use Executors.newSingleThreadScheduledExecutor * in the cases where we need to run some time based tasks. *
*/ private static class HandlerExecutorService extends AbstractExecutorService implements ScheduledExecutorService { private final Handler mHandler; private HandlerExecutorService(Handler handler) { mHandler = handler; } @NonNull @Override public ScheduledFuture> schedule(final Runnable command, long delay, TimeUnit unit) { final HandlerFuture