1 /*
2  * Copyright (C) 2019 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 #pragma once
17 
18 #include <gtest/gtest_prod.h>
19 #include <stdint.h>
20 
21 namespace android {
22 namespace os {
23 namespace statsd {
24 
25 /**
26  * A simple stopwatch to time the duration of condition being true.
27  *
28  * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition
29  * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps
30  * should be elapsedRealTime in nano seconds.
31  *
32  * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is
33  * responsible for thread safety.
34  */
35 class ConditionTimer {
36 public:
37     explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
38         if (initCondition) {
39             mLastConditionChangeTimestampNs = bucketStartNs;
40         }
41     };
42 
43     // Tracks how long the condition has been stayed true in the *current* bucket.
44     // When a new bucket is created, this value will be reset to 0.
45     int64_t mTimerNs = 0;
46 
47     // Last elapsed real timestamp when condition changed.
48     int64_t mLastConditionChangeTimestampNs = 0;
49 
50     bool mCondition = false;
51 
52     int64_t newBucketStart(int64_t nextBucketStartNs) {
53         if (mCondition) {
54             // Normally, the next bucket happens after the last condition
55             // change. In this case, add the time between the condition becoming
56             // true to the next bucket start time.
57             // Otherwise, the next bucket start time is before the last
58             // condition change time, this means that the condition was false at
59             // the bucket boundary before the condition became true, so the
60             // timer should not get updated and the last condition change time
61             // remains as is.
62             if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
63                 mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
64                 mLastConditionChangeTimestampNs = nextBucketStartNs;
65             }
66         } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
67             // The next bucket start time is before the last condition change
68             // time, this means that the condition was true at the bucket
69             // boundary before the condition became false, so adjust the timer
70             // to match how long the condition was true to the bucket boundary.
71             // This means remove the amount the condition stayed true in the
72             // next bucket from the current bucket.
73             mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
74         }
75 
76         int64_t temp = mTimerNs;
77         mTimerNs = 0;
78 
79         if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
80             // The next bucket start time is before the last condition change
81             // time, this means that the condition was true at the bucket
82             // boundary and remained true in the next bucket up to the condition
83             // change to false, so adjust the timer to match how long the
84             // condition stayed true in the next bucket (now the current bucket).
85             mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
86         }
87         return temp;
88     }
89 
90     void onConditionChanged(bool newCondition, int64_t timestampNs) {
91         if (newCondition == mCondition) {
92             return;
93         }
94         mCondition = newCondition;
95         if (newCondition == false) {
96             mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
97         }
98         mLastConditionChangeTimestampNs = timestampNs;
99     }
100 
101     FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
102     FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True);
103 };
104 
105 }  // namespace statsd
106 }  // namespace os
107 }  // namespace android
108