1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.datamodel.action;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Handler;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.util.SimpleArrayMap;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert.RunsOnAnyThread;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert.RunsOnMainThread;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil;
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ThreadUtil;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.annotations.VisibleForTesting;
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.text.SimpleDateFormat;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Date;
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.TimeZone;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Base class for action monitors
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Actions come in various flavors but
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *  o) Fire and forget - no monitor
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *  o) Immediate local processing only - will trigger ActionCompletedListener when done
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *  o) Background worker processing only - will trigger ActionCompletedListener when done
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *  o) Immediate local processing followed by background work followed by more local processing
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      - will trigger ActionExecutedListener once local processing complete and
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *        ActionCompletedListener when second set of local process (dealing with background
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *         worker response) is complete
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class ActionMonitor {
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Interface used to notify on completion of local execution for an action
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public interface ActionExecutedListener {
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param result value returned by {@link Action#executeAction}
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @RunsOnMainThread
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        abstract void onActionExecuted(ActionMonitor monitor, final Action action,
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final Object data, final Object result);
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Interface used to notify action completion
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public interface ActionCompletedListener {
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param result object returned from processing the action. This is the value returned by
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         *               {@link Action#executeAction} if there is no background work, or
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         *               else the value returned by
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         *               {@link Action#processBackgroundResponse}
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @RunsOnMainThread
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        abstract void onActionSucceeded(ActionMonitor monitor,
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final Action action, final Object data, final Object result);
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param result value returned by {@link Action#processBackgroundFailure}
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @RunsOnMainThread
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        abstract void onActionFailed(ActionMonitor monitor, final Action action,
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final Object data, final Object result);
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Interface for being notified of action state changes - used for profiling, testing only
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected interface ActionStateChangedListener {
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param action the action that is changing state
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param state the new state of the action
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @RunsOnAnyThread
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        void onActionStateChanged(Action action, int state);
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Operations always start out as STATE_CREATED and finish as STATE_COMPLETE.
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Some common state transition sequences in between include:
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * <ul>
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *   <li>Local data change only : STATE_QUEUED - STATE_EXECUTING
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *   <li>Background worker request only : STATE_BACKGROUND_ACTIONS_QUEUED
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_EXECUTING_BACKGROUND_ACTION
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_BACKGROUND_COMPLETION_QUEUED
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_PROCESSING_BACKGROUND_RESPONSE
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *   <li>Local plus background worker request : STATE_QUEUED - STATE_EXECUTING
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_BACKGROUND_ACTIONS_QUEUED
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_EXECUTING_BACKGROUND_ACTION
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_BACKGROUND_COMPLETION_QUEUED
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *      - STATE_PROCESSING_BACKGROUND_RESPONSE
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * </ul>
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_UNDEFINED = 0;
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_CREATED = 1; // Just created
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_QUEUED = 2; // Action queued for processing
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_EXECUTING = 3; // Action processing on datamodel thread
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_BACKGROUND_ACTIONS_QUEUED = 4;
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_EXECUTING_BACKGROUND_ACTION = 5;
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // The background work has completed, either returning a success response or resulting in a
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // failure
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_BACKGROUND_COMPLETION_QUEUED = 6;
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_PROCESSING_BACKGROUND_RESPONSE = 7;
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final int STATE_COMPLETE = 8; // Action complete
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Lock used to protect access to state and listeners
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Object mLock = new Object();
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Current state of action
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected int mState;
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Listener which is notified on action completion
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private ActionCompletedListener mCompletedListener;
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Listener which is notified on action executed
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private ActionExecutedListener mExecutedListener;
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Listener which is notified of state changes
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private ActionStateChangedListener mStateChangedListener;
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Handler used to post results back to caller
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Handler mHandler;
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Data passed back to listeners (associated with the action when it is created)
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Object mData;
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The action key is used to determine equivalence of operations and their requests
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final String mActionKey;
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Get action key identifying associated action
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public String getActionKey() {
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mActionKey;
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Unregister listeners so that they will not be called back - override this method if needed
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void unregister() {
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        clearListeners();
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Unregister listeners so that they will not be called
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected final void clearListeners() {
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mCompletedListener = null;
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mExecutedListener = null;
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Create a monitor associated with a particular action instance
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected ActionMonitor(final int initialState, final String actionKey,
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Object data) {
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mHandler = ThreadUtil.getMainThreadHandler();
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mActionKey = actionKey;
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mState = initialState;
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mData = data;
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Return flag to indicate if action is complete
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public boolean isComplete() {
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        boolean complete = false;
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            complete = (mState == STATE_COMPLETE);
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return complete;
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Set listener that will be called with action completed result
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected final void setCompletedListener(final ActionCompletedListener listener) {
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mCompletedListener = listener;
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Set listener that will be called with local execution result
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected final void setExecutedListener(final ActionExecutedListener listener) {
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mExecutedListener = listener;
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Set listener that will be called with local execution result
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected final void setStateChangedListener(final ActionStateChangedListener listener) {
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mStateChangedListener = listener;
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Perform a state update transition
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action whose state is updating
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expectedOldState - expected existing state of action (can be UNKNOWN)
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param newState - new state which will be set
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected void updateState(final Action action, final int expectedOldState,
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int newState) {
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        ActionStateChangedListener listener = null;
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (expectedOldState != STATE_UNDEFINED &&
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mState != expectedOldState) {
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                throw new IllegalStateException("On updateState to " + newState + " was " + mState
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + " expecting " + expectedOldState);
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (newState != mState) {
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mState = newState;
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                listener = mStateChangedListener;
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (listener != null) {
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            listener.onActionStateChanged(action, newState);
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Perform a state update transition
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action whose state is updating
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expectedOldState - expected existing state of action (can be UNKNOWN)
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param newState - new state which will be set
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static void setState(final Action action, final int expectedOldState,
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int newState) {
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int oldMonitorState = expectedOldState;
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int newMonitorState = newState;
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final ActionMonitor monitor
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                = ActionMonitor.lookupActionMonitor(action.actionKey);
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (monitor != null) {
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            oldMonitorState = monitor.mState;
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            monitor.updateState(action, expectedOldState, newState);
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            newMonitorState = monitor.mState;
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            df.setTimeZone(TimeZone.getTimeZone("UTC"));
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.v(TAG, "Operation-" + action.actionKey + ": @" + df.format(new Date())
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + "UTC State = " + oldMonitorState + " - " + newMonitorState);
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Mark action complete
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action whose state is updating
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expectedOldState - expected existing state of action (can be UNKNOWN)
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param result - object returned from processing the action. This is the value returned by
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *                 {@link Action#executeAction} if there is no background work, or
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *                 else the value returned by {@link Action#processBackgroundResponse}
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *                 or {@link Action#processBackgroundFailure}
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final void complete(final Action action,
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int expectedOldState, final Object result,
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final boolean succeeded) {
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        ActionCompletedListener completedListener = null;
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            setState(action, expectedOldState, STATE_COMPLETE);
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            completedListener = mCompletedListener;
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mExecutedListener = null;
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mStateChangedListener = null;
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (completedListener != null) {
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Marshal to UI thread
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mHandler.post(new Runnable() {
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                @Override
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                public void run() {
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    ActionCompletedListener listener = null;
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    synchronized (mLock) {
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        if (mCompletedListener != null) {
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            listener = mCompletedListener;
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        }
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        mCompletedListener = null;
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (listener != null) {
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        if (succeeded) {
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            listener.onActionSucceeded(ActionMonitor.this,
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                    action, mData, result);
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        } else {
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            listener.onActionFailed(ActionMonitor.this,
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                    action, mData, result);
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        }
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            });
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Mark action complete
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action whose state is updating
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expectedOldState - expected existing state of action (can be UNKNOWN)
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param result - object returned from processing the action. This is the value returned by
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *                 {@link Action#executeAction} if there is no background work, or
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *                 else the value returned by {@link Action#processBackgroundResponse}
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *                 or {@link Action#processBackgroundFailure}
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static void setCompleteState(final Action action, final int expectedOldState,
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Object result, final boolean succeeded) {
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int oldMonitorState = expectedOldState;
342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final ActionMonitor monitor
343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                = ActionMonitor.lookupActionMonitor(action.actionKey);
344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (monitor != null) {
345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            oldMonitorState = monitor.mState;
346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            monitor.complete(action, expectedOldState, result, succeeded);
347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            unregisterActionMonitorIfComplete(action.actionKey, monitor);
348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            df.setTimeZone(TimeZone.getTimeZone("UTC"));
352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.v(TAG, "Operation-" + action.actionKey + ": @" + df.format(new Date())
353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + "UTC State = " + oldMonitorState + " - " + STATE_COMPLETE);
354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Mark action complete
359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action whose state is updating
360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expectedOldState - expected existing state of action (can be UNKNOWN)
361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param hasBackgroundActions - has the completing action requested background work
362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param result - the return value of {@link Action#executeAction}
363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    final void executed(final Action action,
365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int expectedOldState, final boolean hasBackgroundActions, final Object result) {
366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        ActionExecutedListener executedListener = null;
367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mLock) {
368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (hasBackgroundActions) {
369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                setState(action, expectedOldState, STATE_BACKGROUND_ACTIONS_QUEUED);
370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            executedListener = mExecutedListener;
372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (executedListener != null) {
374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Marshal to UI thread
375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mHandler.post(new Runnable() {
376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                @Override
377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                public void run() {
378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    ActionExecutedListener listener = null;
379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    synchronized (mLock) {
380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        if (mExecutedListener != null) {
381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            listener = mExecutedListener;
382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            mExecutedListener = null;
383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        }
384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (listener != null) {
386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        listener.onActionExecuted(ActionMonitor.this,
387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                action, mData, result);
388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            });
391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Mark action complete
396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param action - action whose state is updating
397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expectedOldState - expected existing state of action (can be UNKNOWN)
398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param hasBackgroundActions - has the completing action requested background work
399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param result - the return value of {@link Action#executeAction}
400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static void setExecutedState(final Action action,
402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int expectedOldState, final boolean hasBackgroundActions, final Object result) {
403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int oldMonitorState = expectedOldState;
404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final ActionMonitor monitor
405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                = ActionMonitor.lookupActionMonitor(action.actionKey);
406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (monitor != null) {
407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            oldMonitorState = monitor.mState;
408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            monitor.executed(action, expectedOldState, hasBackgroundActions, result);
409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            df.setTimeZone(TimeZone.getTimeZone("UTC"));
413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.v(TAG, "Operation-" + action.actionKey + ": @" + df.format(new Date())
414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + "UTC State = " + oldMonitorState + " - EXECUTED");
415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Map of action monitors indexed by actionKey
420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static SimpleArrayMap<String, ActionMonitor> sActionMonitors =
423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            new SimpleArrayMap<String, ActionMonitor>();
424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Insert new monitor into map
427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static void registerActionMonitor(final String actionKey,
429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ActionMonitor monitor) {
430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (monitor != null
431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                && (TextUtils.isEmpty(monitor.getActionKey())
432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        || TextUtils.isEmpty(actionKey)
433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        || !actionKey.equals(monitor.getActionKey()))) {
434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            throw new IllegalArgumentException("Monitor key " + monitor.getActionKey()
435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + " not compatible with action key " + actionKey);
436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (sActionMonitors) {
438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            sActionMonitors.put(actionKey, monitor);
439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Find monitor associated with particular action
444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static ActionMonitor lookupActionMonitor(final String actionKey) {
446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        ActionMonitor monitor = null;
447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (sActionMonitors) {
448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            monitor = sActionMonitors.get(actionKey);
449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return monitor;
451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Remove monitor from map
455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @VisibleForTesting
457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static void unregisterActionMonitor(final String actionKey,
458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ActionMonitor monitor) {
459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (monitor != null) {
460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            synchronized (sActionMonitors) {
461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                sActionMonitors.remove(actionKey);
462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Remove monitor from map if the action is complete
468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static void unregisterActionMonitorIfComplete(final String actionKey,
470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ActionMonitor monitor) {
471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (monitor != null && monitor.isComplete()) {
472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            synchronized (sActionMonitors) {
473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                sActionMonitors.remove(actionKey);
474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
478