1 /*
2  * Copyright (C) 2014 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.android.ex.camera2.portability;
18 
19 import android.os.SystemClock;
20 
21 import com.android.ex.camera2.portability.debug.Log;
22 
23 public abstract class CameraStateHolder {
24     private static final Log.Tag TAG = new Log.Tag("CamStateHolder");
25 
26     private int mState;
27     private boolean mInvalid;
28 
29     /**
30      * Construct a new instance of @{link CameraStateHolder} with an initial state.
31      *
32      * @param state The initial state.
33      */
CameraStateHolder(int state)34     public CameraStateHolder(int state) {
35         setState(state);
36         mInvalid = false;
37     }
38 
39     /**
40      * Change to a new state.
41      *
42      * @param state The new state.
43      */
setState(int state)44     public synchronized void setState(int state) {
45         if (mState != state) {
46             Log.v(TAG, "setState - state = " + Integer.toBinaryString(state));
47         }
48         mState = state;
49         this.notifyAll();
50     }
51 
52     /**
53      * Obtain the current state.
54      *
55      * @return The current state.
56      */
getState()57     public synchronized int getState() {
58         return mState;
59     }
60 
61     /**
62      * Change the state to be invalid. Once invalidated, the state will be invalid forever.
63      */
invalidate()64     public synchronized void invalidate() {
65         mInvalid = true;
66     }
67 
68     /**
69      * Whether the state is invalid.
70      *
71      * @return True if the state is invalid.
72      */
isInvalid()73     public synchronized boolean isInvalid() {
74         return mInvalid;
75     }
76 
77     private static interface ConditionChecker {
78         /**
79          * @return Whether the condition holds.
80          */
success()81         boolean success();
82     }
83 
84     /**
85      * A helper method used by {@link #waitToAvoidStates(int)} and
86      * {@link #waitForStates(int)}. This method will wait until the
87      * condition is successful.
88      *
89      * @param stateChecker The state checker to be used.
90      * @param timeoutMs The timeout limit in milliseconds.
91      * @return {@code false} if the wait is interrupted or timeout limit is
92      *         reached.
93      */
waitForCondition(ConditionChecker stateChecker, long timeoutMs)94     private boolean waitForCondition(ConditionChecker stateChecker,
95             long timeoutMs) {
96         long timeBound = SystemClock.uptimeMillis() + timeoutMs;
97         synchronized (this) {
98             while (!stateChecker.success()) {
99                 try {
100                     this.wait(timeoutMs);
101                 } catch (InterruptedException ex) {
102                     if (SystemClock.uptimeMillis() > timeBound) {
103                         // Timeout.
104                         Log.w(TAG, "Timeout waiting.");
105                     }
106                     return false;
107                 }
108             }
109         }
110         return true;
111     }
112 
113     /**
114      * Block the current thread until the state becomes one of the
115      * specified.
116      *
117      * @param states Expected states.
118      * @return {@code false} if the wait is interrupted or timeout limit is
119      *         reached.
120      */
waitForStates(final int states)121     public boolean waitForStates(final int states) {
122         Log.v(TAG, "waitForStates - states = " + Integer.toBinaryString(states));
123         return waitForCondition(new ConditionChecker() {
124             @Override
125             public boolean success() {
126                 return (states | getState()) == states;
127             }
128         }, CameraAgent.CAMERA_OPERATION_TIMEOUT_MS);
129     }
130 
131     /**
132      * Block the current thread until the state becomes NOT one of the
133      * specified.
134      *
135      * @param states States to avoid.
136      * @return {@code false} if the wait is interrupted or timeout limit is
137      *         reached.
138      */
139     public boolean waitToAvoidStates(final int states) {
140         Log.v(TAG, "waitToAvoidStates - states = " + Integer.toBinaryString(states));
141         return waitForCondition(new ConditionChecker() {
142             @Override
143             public boolean success() {
144                 return (states & getState()) == 0;
145             }
146         }, CameraAgent.CAMERA_OPERATION_TIMEOUT_MS);
147     }
148 }
149