177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin/* 277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Copyright 2014 The Android Open Source Project 377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Licensed under the Apache License, Version 2.0 (the "License"); 577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * you may not use this file except in compliance with the License. 677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * You may obtain a copy of the License at 777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * http://www.apache.org/licenses/LICENSE-2.0 977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 1077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Unless required by applicable law or agreed to in writing, software 1177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * distributed under the License is distributed on an "AS IS" BASIS, 1277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * See the License for the specific language governing permissions and 1477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * limitations under the License. 1577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 1677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 1777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinpackage com.android.ex.camera2.utils; 1877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 1977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport android.os.SystemClock; 2077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport android.util.Log; 2177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 2277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport com.android.ex.camera2.exceptions.TimeoutRuntimeException; 2377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 2477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport java.util.Arrays; 2577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport java.util.Collection; 2677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport java.util.concurrent.LinkedBlockingQueue; 2777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport java.util.concurrent.TimeUnit; 2877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinimport java.util.concurrent.atomic.AtomicBoolean; 2977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 3077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin/** 3177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Block until a specific state change occurs. 3277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 3377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>Provides wait calls that block until the next unobserved state of the 3477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * requested type arrives. Unobserved states are states that have occurred since 3577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * the last wait, or that will be received from the camera device in the 3677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * future.</p> 3777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 3877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>Thread interruptions are not supported; interrupting a thread that is either 3977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * waiting with {@link #waitForState} / {@link #waitForAnyOfStates} or is currently in 4077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * {@link StateChangeListener#onStateChanged} (provided by {@link #getListener}) will result in an 4177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * {@link UnsupportedOperationException} being raised on that thread.</p> 4277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 4377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkinpublic final class StateWaiter { 4477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 4577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private static final String TAG = "StateWaiter"; 4677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 4777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 4877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private final String[] mStateNames; 4977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private final int mStateCount; 5077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private final StateChangeListener mListener; 5177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 5277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin /** Guard waitForState, waitForAnyState to only have one waiter */ 5377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private final AtomicBoolean mWaiting = new AtomicBoolean(false); 5477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 5577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private final LinkedBlockingQueue<Integer> mQueuedStates = new LinkedBlockingQueue<>(); 5677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 5777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin /** 5877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Create a new state waiter. 5977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 6077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>All {@code state}/{@code states} arguments used in other methods must be 6177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * in the range of {@code [0, stateNames.length - 1]}.</p> 6277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 6377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @param stateNames an array of string names, used to mark the range of the valid states 6477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 6577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public StateWaiter(String[] stateNames) { 6677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin mStateCount = stateNames.length; 6777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin mStateNames = new String[mStateCount]; 6877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin System.arraycopy(stateNames, /*srcPos*/0, mStateNames, /*dstPos*/0, mStateCount); 6977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 7077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin mListener = new StateChangeListener() { 7177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin @Override 7277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public void onStateChanged(int state) { 7377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin queueStateTransition(checkStateInRange(state)); 7477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 7577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin }; 7677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 7777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 7877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public StateChangeListener getListener() { 7977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin return mListener; 8077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 8177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 8277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin /** 8377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Wait until the desired state is observed, checking all state 8477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * transitions since the last time a state was waited on. 8577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 8677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>Any intermediate state transitions that is not {@code state} are ignored.</p> 8777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 8877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>Note: Only one waiter allowed at a time!</p> 8977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 905a772174d14175474e76701b07cc0be86c3df32aJiawen Chen * @param state state to observe a transition to 9177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @param timeoutMs how long to wait in milliseconds 9277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 9377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @throws IllegalArgumentException if {@code state} was out of range 9477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @throws TimeoutRuntimeException if the desired state is not observed before timeout. 9577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @throws IllegalStateException if another thread is already waiting for a state transition 9677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 9777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public void waitForState(int state, long timeoutMs) { 9877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin Integer[] stateArray = { checkStateInRange(state) }; 9977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 10077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin waitForAnyOfStates(Arrays.asList(stateArray), timeoutMs); 10177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 10277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 10377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin /** 10477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Wait until the one of the desired {@code states} is observed, checking all 10577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * state transitions since the last time a state was waited on. 10677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 10777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>Any intermediate state transitions that are not in {@code states} are ignored.</p> 10877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 10977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * <p>Note: Only one waiter allowed at a time!</p> 11077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 11177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @param states Set of desired states to observe a transition to. 11277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @param timeoutMs how long to wait in milliseconds 11377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 11477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @return the state reached 11577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * 11677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @throws IllegalArgumentException if {@code state} was out of range 11777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @throws TimeoutRuntimeException if none of the states is observed before timeout. 11877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * @throws IllegalStateException if another thread is already waiting for a state transition 11977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 12077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public int waitForAnyOfStates(Collection<Integer> states, final long timeoutMs) { 12177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin checkStateCollectionInRange(states); 12277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 12377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin // Acquire exclusive waiting privileges 12477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (mWaiting.getAndSet(true)) { 12577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin throw new IllegalStateException("Only one waiter allowed at a time"); 12677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 12777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 12877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin Integer nextState = null; 12977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin try { 13077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (VERBOSE) { 13177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin StringBuilder s = new StringBuilder("Waiting for state(s) "); 13277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin appendStateNames(s, states); 13377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin Log.v(TAG, s.toString()); 13477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 13577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 13677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin long timeoutLeft = timeoutMs; 13777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin long startMs = SystemClock.elapsedRealtime(); 13877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin while ((nextState = mQueuedStates.poll(timeoutLeft, TimeUnit.MILLISECONDS)) != null) { 13977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (VERBOSE) { 14077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin Log.v(TAG, " Saw transition to " + getStateName(nextState)); 14177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 14277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 14377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (states.contains(nextState)) { 14477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin break; 14577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 14677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 14777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin long endMs = SystemClock.elapsedRealtime(); 14877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin timeoutLeft -= (endMs - startMs); 14977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin startMs = endMs; 15077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 15177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } catch (InterruptedException e) { 15277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin throw new UnsupportedOperationException("Does not support interrupts on waits", e); 15377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } finally { 15477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin // Release exclusive waiting privileges 15577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin mWaiting.set(false); 15677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 15777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 15877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (!states.contains(nextState)) { 15977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin StringBuilder s = new StringBuilder("Timed out after "); 16077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin s.append(timeoutMs); 16177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin s.append(" ms waiting for state(s) "); 16277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin appendStateNames(s, states); 16377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 16477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin throw new TimeoutRuntimeException(s.toString()); 16577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 16677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 16777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin return nextState; 16877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 16977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 17077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin /** 17177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Convert state integer to a String 17277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 17377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public String getStateName(int state) { 17477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin return mStateNames[checkStateInRange(state)]; 17577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 17677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 17777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin /** 17877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin * Append all states to string 17977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin */ 18077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin public void appendStateNames(StringBuilder s, Collection<Integer> states) { 18177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin checkStateCollectionInRange(states); 18277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 18377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin boolean start = true; 18477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin for (Integer state : states) { 18577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (!start) { 18677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin s.append(" "); 18777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 18877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 18977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin s.append(getStateName(state)); 19077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin start = false; 19177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 19277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 19377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 19477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private void queueStateTransition(int state) { 19577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (VERBOSE) Log.v(TAG, "setCurrentState - state now " + getStateName(state)); 19677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 19777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin try { 19877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin mQueuedStates.put(state); 1995a772174d14175474e76701b07cc0be86c3df32aJiawen Chen } catch (InterruptedException e) { 20077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin throw new UnsupportedOperationException("Unable to set current state", e); 20177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 20277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 20377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 20477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private int checkStateInRange(int state) { 20577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin if (state < 0 || state >= mStateCount) { 20677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin throw new IllegalArgumentException("State out of range " + state); 20777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 20877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 20977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin return state; 21077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 21177b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 21277b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin private Collection<Integer> checkStateCollectionInRange(Collection<Integer> states) { 21377b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin for (int state : states) { 21477b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin checkStateInRange(state); 21577b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 21677b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 21777b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin return states; 21877b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin } 21977b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin 22077b7bbe039393c2d8c1107c07c65028886eef252Igor Murashkin} 221