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
17package android.util;
18
19/**
20 * This class tracks the timing of important state changes in camera app (e.g latency
21 * of cold/warm start of the activity, mode switch duration, etc). We can then query
22 * these values from the instrument tests, which will be helpful for tracking camera
23 * app performance and regression tests.
24 */
25public class CameraPerformanceTracker {
26
27    // Event types to track.
28    public static final int ACTIVITY_START = 0;
29    public static final int ACTIVITY_PAUSE = 1;
30    public static final int ACTIVITY_RESUME = 2;
31    public static final int MODE_SWITCH_START = 3;
32    public static final int FIRST_PREVIEW_FRAME = 5;
33    public static final int UNSET = -1;
34
35    private static final String TAG = "CameraPerformanceTracker";
36    private static final boolean DEBUG = false;
37    private static CameraPerformanceTracker sInstance;
38
39    // Internal tracking time.
40    private long mAppStartTime = UNSET;
41    private long mAppResumeTime = UNSET;
42    private long mModeSwitchStartTime = UNSET;
43
44    // Duration and/or latency or later querying.
45    private long mFirstPreviewFrameLatencyColdStart = UNSET;
46    private long mFirstPreviewFrameLatencyWarmStart = UNSET;
47    // TODO: Need to how to best track the duration for each switch from/to pair.
48    private long mModeSwitchDuration = UNSET;
49
50    private CameraPerformanceTracker() {
51        // Private constructor to ensure that it can only be created from within
52        // the class.
53    }
54
55    /**
56     * This gets called when an important state change happens. Based on the type
57     * of the event/state change, either we will record the time of the event, or
58     * calculate the duration/latency.
59     *
60     * @param eventType type of a event to track
61     */
62    public static void onEvent(int eventType) {
63        if (sInstance == null) {
64            sInstance = new CameraPerformanceTracker();
65        }
66        long currentTime = System.currentTimeMillis();
67        switch (eventType) {
68            case ACTIVITY_START:
69                sInstance.mAppStartTime = currentTime;
70                break;
71            case ACTIVITY_PAUSE:
72                sInstance.mFirstPreviewFrameLatencyWarmStart = UNSET;
73                break;
74            case ACTIVITY_RESUME:
75                sInstance.mAppResumeTime = currentTime;
76                break;
77            case FIRST_PREVIEW_FRAME:
78                Log.d(TAG, "First preview frame received");
79                if (sInstance.mFirstPreviewFrameLatencyColdStart == UNSET) {
80                    // Cold start.
81                    sInstance.mFirstPreviewFrameLatencyColdStart =
82                            currentTime - sInstance.mAppStartTime;
83                } else {
84                    // Warm Start.
85                    sInstance.mFirstPreviewFrameLatencyWarmStart =
86                            currentTime - sInstance.mAppResumeTime;
87                }
88                // If the new frame is triggered by the mode switch, track the duration.
89                if (sInstance.mModeSwitchStartTime != UNSET) {
90                    sInstance.mModeSwitchDuration = currentTime - sInstance.mModeSwitchStartTime;
91                    sInstance.mModeSwitchStartTime = UNSET;
92                }
93                break;
94            case MODE_SWITCH_START:
95                sInstance.mModeSwitchStartTime = currentTime;
96                break;
97            default:
98                break;
99        }
100        if (DEBUG && eventType == FIRST_PREVIEW_FRAME) {
101            Log.d(TAG, "Mode switch duration: " + (sInstance.mModeSwitchDuration
102                == UNSET ? "UNSET" : sInstance.mModeSwitchDuration));
103            Log.d(TAG, "Cold start latency: " + (sInstance.mFirstPreviewFrameLatencyColdStart
104                == UNSET ? "UNSET" : sInstance.mFirstPreviewFrameLatencyColdStart));
105            Log.d(TAG, "Warm start latency: " + (sInstance.mFirstPreviewFrameLatencyWarmStart
106                == UNSET ? "UNSET" : sInstance.mFirstPreviewFrameLatencyWarmStart));
107        }
108    }
109
110    //TODO: Hook up these getters in the instrument tests.
111    /**
112     * Gets the latency of a cold start of the app, measured from the time onCreate
113     * gets called to the time first preview frame gets received.
114     *
115     * @return latency of a cold start. If no instances have been created, return
116     *         UNSET.
117     */
118    public static long getColdStartLatency() {
119        if (sInstance == null) {
120            return UNSET;
121        }
122        return sInstance.mFirstPreviewFrameLatencyColdStart;
123    }
124
125    /**
126     * Gets the latency of a warm start of the app, measured from the time onResume
127     * gets called to the time next preview frame gets received.
128     *
129     * @return latency of a warm start. If no instances have been created,
130     *         return UNSET.
131     */
132    public static long getWarmStartLatency() {
133        if (sInstance == null) {
134            return UNSET;
135        }
136        return sInstance.mFirstPreviewFrameLatencyWarmStart;
137    }
138
139    /**
140     * Gets the duration of the mode switch, measured from the start of a mode switch
141     * to the time next preview frame gets received.
142     *
143     * @return duration of the mode switch. If no instances have been created,
144     *         return UNSET.
145     */
146    public static long getModeSwitchDuration() {
147        if (sInstance == null) {
148            return UNSET;
149        }
150        return sInstance.mModeSwitchDuration;
151    }
152}
153