blob: 1a719a3bbe5455b5ae2f10cefda4d9c5f99e30e6 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.android.apps.common.testing.ui.espresso.base;
import com.google.common.base.Optional;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import java.lang.reflect.Field;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Extracts ThreadPoolExecutors used by pieces of android.
*
* We do some work to ensure that we load the classes containing these thread pools
* on the main thread, since they may have static initialization that assumes access
* to the main looper.
*/
@Singleton
final class ThreadPoolExecutorExtractor {
private static final String ASYNC_TASK_CLASS_NAME = "android.os.AsyncTask";
private static final String MODERN_ASYNC_TASK_CLASS_NAME =
"android.support.v4.content.ModernAsyncTask";
private static final String MODERN_ASYNC_TASK_FIELD_NAME = "THREAD_POOL_EXECUTOR";
private static final String LEGACY_ASYNC_TASK_FIELD_NAME = "sExecutor";
private final Handler mainHandler;
@Inject
ThreadPoolExecutorExtractor(Looper looper) {
mainHandler = new Handler(looper);
}
public ThreadPoolExecutor getAsyncTaskThreadPool() {
FutureTask<Optional<ThreadPoolExecutor>> getTask = null;
if (Build.VERSION.SDK_INT < 11) {
getTask = new FutureTask<Optional<ThreadPoolExecutor>>(LEGACY_ASYNC_TASK_EXECUTOR);
} else {
getTask = new FutureTask<Optional<ThreadPoolExecutor>>(POST_HONEYCOMB_ASYNC_TASK_EXECUTOR);
}
try {
return runOnMainThread(getTask).get().get();
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted while trying to get the async task executor!", ie);
} catch (ExecutionException ee) {
throw new RuntimeException(ee.getCause());
}
}
public Optional<ThreadPoolExecutor> getCompatAsyncTaskThreadPool() {
try {
return runOnMainThread(
new FutureTask<Optional<ThreadPoolExecutor>>(MODERN_ASYNC_TASK_EXTRACTOR)).get();
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted while trying to get the compat async executor!", ie);
} catch (ExecutionException ee) {
throw new RuntimeException(ee.getCause());
}
}
private <T> FutureTask<T> runOnMainThread(final FutureTask<T> futureToRun) {
if (Looper.myLooper() != Looper.getMainLooper()) {
final CountDownLatch latch = new CountDownLatch(1);
mainHandler.post(new Runnable() {
@Override
public void run() {
try {
futureToRun.run();
} finally {
latch.countDown();
}
}
});
try {
latch.await();
} catch (InterruptedException ie) {
if (!futureToRun.isDone()) {
throw new RuntimeException("Interrupted while waiting for task to complete.");
}
}
} else {
futureToRun.run();
}
return futureToRun;
}
private static final Callable<Optional<ThreadPoolExecutor>> MODERN_ASYNC_TASK_EXTRACTOR =
new Callable<Optional<ThreadPoolExecutor>>() {
@Override
public Optional<ThreadPoolExecutor> call() throws Exception {
try {
Class<?> modernClazz = Class.forName(MODERN_ASYNC_TASK_CLASS_NAME);
Field executorField = modernClazz.getField(MODERN_ASYNC_TASK_FIELD_NAME);
return Optional.of((ThreadPoolExecutor) executorField.get(null));
} catch (ClassNotFoundException cnfe) {
return Optional.<ThreadPoolExecutor>absent();
}
}
};
private static final Callable<Class<?>> LOAD_ASYNC_TASK_CLASS =
new Callable<Class<?>>() {
@Override
public Class<?> call() throws Exception {
return Class.forName(ASYNC_TASK_CLASS_NAME);
}
};
private static final Callable<Optional<ThreadPoolExecutor>> LEGACY_ASYNC_TASK_EXECUTOR =
new Callable<Optional<ThreadPoolExecutor>>() {
@Override
public Optional<ThreadPoolExecutor> call() throws Exception {
Field executorField = LOAD_ASYNC_TASK_CLASS.call()
.getDeclaredField(LEGACY_ASYNC_TASK_FIELD_NAME);
executorField.setAccessible(true);
return Optional.of((ThreadPoolExecutor) executorField.get(null));
}
};
private static final Callable<Optional<ThreadPoolExecutor>> POST_HONEYCOMB_ASYNC_TASK_EXECUTOR =
new Callable<Optional<ThreadPoolExecutor>>() {
@Override
public Optional<ThreadPoolExecutor> call() throws Exception {
Field executorField = LOAD_ASYNC_TASK_CLASS.call()
.getField(MODERN_ASYNC_TASK_FIELD_NAME);
return Optional.of((ThreadPoolExecutor) executorField.get(null));
}
};
}