1f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev/* 2f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Copyright (C) 2014 The Android Open Source Project 3f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * 4f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Licensed under the Apache License, Version 2.0 (the "License"); 5f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * you may not use this file except in compliance with the License. 6f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * You may obtain a copy of the License at 7f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * 8f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * http://www.apache.org/licenses/LICENSE-2.0 9f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * 10f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Unless required by applicable law or agreed to in writing, software 11f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * distributed under the License is distributed on an "AS IS" BASIS, 12f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * See the License for the specific language governing permissions and 14f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * limitations under the License. 15f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev */ 16f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 17f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevpackage com.google.android.apps.common.testing.ui.espresso.base; 18f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 19f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport com.google.common.base.Optional; 20f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 21f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.os.Build; 22f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.os.Handler; 23f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport android.os.Looper; 24f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 25f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.lang.reflect.Field; 26f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.Callable; 27f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.CountDownLatch; 28f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.ExecutionException; 29f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.FutureTask; 30f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport java.util.concurrent.ThreadPoolExecutor; 31f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 32f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport javax.inject.Inject; 33f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevimport javax.inject.Singleton; 34f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 35f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev/** 36f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * Extracts ThreadPoolExecutors used by pieces of android. 37f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * 38f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * We do some work to ensure that we load the classes containing these thread pools 39f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * on the main thread, since they may have static initialization that assumes access 40f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev * to the main looper. 41f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev */ 42f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev@Singleton 43f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelevfinal class ThreadPoolExecutorExtractor { 44f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final String ASYNC_TASK_CLASS_NAME = "android.os.AsyncTask"; 45f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final String MODERN_ASYNC_TASK_CLASS_NAME = 46f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev "android.support.v4.content.ModernAsyncTask"; 47f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final String MODERN_ASYNC_TASK_FIELD_NAME = "THREAD_POOL_EXECUTOR"; 48f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final String LEGACY_ASYNC_TASK_FIELD_NAME = "sExecutor"; 49f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private final Handler mainHandler; 50f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 51f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev @Inject 52f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev ThreadPoolExecutorExtractor(Looper looper) { 53f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev mainHandler = new Handler(looper); 54f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 55f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 56f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 57f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public ThreadPoolExecutor getAsyncTaskThreadPool() { 58f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev FutureTask<Optional<ThreadPoolExecutor>> getTask = null; 59f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev if (Build.VERSION.SDK_INT < 11) { 60f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev getTask = new FutureTask<Optional<ThreadPoolExecutor>>(LEGACY_ASYNC_TASK_EXECUTOR); 61f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } else { 62f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev getTask = new FutureTask<Optional<ThreadPoolExecutor>>(POST_HONEYCOMB_ASYNC_TASK_EXECUTOR); 63f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 64f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 65f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev try { 66f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return runOnMainThread(getTask).get().get(); 67f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } catch (InterruptedException ie) { 68f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev throw new RuntimeException("Interrupted while trying to get the async task executor!", ie); 69f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } catch (ExecutionException ee) { 70f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev throw new RuntimeException(ee.getCause()); 71f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 72f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 73f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 74f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public Optional<ThreadPoolExecutor> getCompatAsyncTaskThreadPool() { 75f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev try { 76f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return runOnMainThread( 77f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev new FutureTask<Optional<ThreadPoolExecutor>>(MODERN_ASYNC_TASK_EXTRACTOR)).get(); 78f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } catch (InterruptedException ie) { 79f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev throw new RuntimeException("Interrupted while trying to get the compat async executor!", ie); 80f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } catch (ExecutionException ee) { 81f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev throw new RuntimeException(ee.getCause()); 82f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 83f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 84f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 85f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private <T> FutureTask<T> runOnMainThread(final FutureTask<T> futureToRun) { 86f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev if (Looper.myLooper() != Looper.getMainLooper()) { 87f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev final CountDownLatch latch = new CountDownLatch(1); 88f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev mainHandler.post(new Runnable() { 89f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev @Override 90f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public void run() { 91f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev try { 92f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev futureToRun.run(); 93f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } finally { 94f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev latch.countDown(); 95f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 96f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 97f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev }); 98f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev try { 99f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev latch.await(); 100f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } catch (InterruptedException ie) { 101f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev if (!futureToRun.isDone()) { 102f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev throw new RuntimeException("Interrupted while waiting for task to complete."); 103f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 104f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 105f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } else { 106f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev futureToRun.run(); 107f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 108f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 109f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return futureToRun; 110f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 111f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 112f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final Callable<Optional<ThreadPoolExecutor>> MODERN_ASYNC_TASK_EXTRACTOR = 113f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev new Callable<Optional<ThreadPoolExecutor>>() { 114f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev @Override 115f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public Optional<ThreadPoolExecutor> call() throws Exception { 116f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev try { 117f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev Class<?> modernClazz = Class.forName(MODERN_ASYNC_TASK_CLASS_NAME); 118f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev Field executorField = modernClazz.getField(MODERN_ASYNC_TASK_FIELD_NAME); 119f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return Optional.of((ThreadPoolExecutor) executorField.get(null)); 120f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } catch (ClassNotFoundException cnfe) { 121f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return Optional.<ThreadPoolExecutor>absent(); 122f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 123f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 124f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev }; 125f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 126f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final Callable<Class<?>> LOAD_ASYNC_TASK_CLASS = 127f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev new Callable<Class<?>>() { 128f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev @Override 129f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public Class<?> call() throws Exception { 130f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return Class.forName(ASYNC_TASK_CLASS_NAME); 131f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 132f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev }; 133f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 134f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final Callable<Optional<ThreadPoolExecutor>> LEGACY_ASYNC_TASK_EXECUTOR = 135f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev new Callable<Optional<ThreadPoolExecutor>>() { 136f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev @Override 137f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public Optional<ThreadPoolExecutor> call() throws Exception { 138f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev Field executorField = LOAD_ASYNC_TASK_CLASS.call() 139f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev .getDeclaredField(LEGACY_ASYNC_TASK_FIELD_NAME); 140f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev executorField.setAccessible(true); 141f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return Optional.of((ThreadPoolExecutor) executorField.get(null)); 142f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 143f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev }; 144f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev 145f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev private static final Callable<Optional<ThreadPoolExecutor>> POST_HONEYCOMB_ASYNC_TASK_EXECUTOR = 146f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev new Callable<Optional<ThreadPoolExecutor>>() { 147f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev @Override 148f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev public Optional<ThreadPoolExecutor> call() throws Exception { 149f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev Field executorField = LOAD_ASYNC_TASK_CLASS.call() 150f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev .getField(MODERN_ASYNC_TASK_FIELD_NAME); 151f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev return Optional.of((ThreadPoolExecutor) executorField.get(null)); 152f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev } 153f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev }; 154f69eb9ac2856f470cb79f57141f711ed3ceed99dNick Korostelev} 155