1 /*
2  * Copyright (C) 2021 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.internal.os;
18 
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 
22 import com.android.internal.util.Preconditions;
23 
24 import dalvik.annotation.optimization.CriticalNative;
25 import dalvik.annotation.optimization.FastNative;
26 
27 import libcore.util.NativeAllocationRegistry;
28 
29 /**
30  * Performs per-state counting of long integers over time.  The tracked "value" is expected
31  * to increase monotonously. The counter keeps track of the current state.  When the
32  * updateValue method is called, the delta from the previous invocation of this method
33  * and the new value is added to the counter corresponding to the current state.  If the
34  * state changed in the interim, the delta is distributed proptionally.
35  *
36  * The class's behavior is illustrated by this example:
37  * <pre>
38  *   // At 0 ms, the state of the tracked object is 0 and the initial tracked value is 100
39  *   counter.setState(0, 0);
40  *   counter.updateValue(100, 0);
41  *
42  *   // At 1000 ms, the state changes to 1
43  *   counter.setState(1, 1000);
44  *
45  *   // At 3000 ms, the tracked value is updated to 130
46  *   counter.updateValue(130, 3000);
47  *
48  *   // The delta (130 - 100 = 30) is distributed between states 0 and 1 according to the time
49  *   // spent in those respective states; in this specific case, 1000 and 2000 ms.
50  *   long countForState0 == counter.getCount(0);  // 10
51  *   long countForState1 == counter.getCount(1);  // 20
52  * </pre>
53  *
54  * The tracked values are expected to increase monotonically.
55  *
56  * @hide
57  */
58 public final class LongMultiStateCounter implements Parcelable {
59 
60     private static final NativeAllocationRegistry sRegistry =
61             NativeAllocationRegistry.createMalloced(
62                     LongMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());
63 
64     private final int mStateCount;
65 
66     // Visible to other objects in this package so that it can be passed to @CriticalNative
67     // methods.
68     final long mNativeObject;
69 
LongMultiStateCounter(int stateCount)70     public LongMultiStateCounter(int stateCount) {
71         Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
72         mStateCount = stateCount;
73         mNativeObject = native_init(stateCount);
74         sRegistry.registerNativeAllocation(this, mNativeObject);
75     }
76 
LongMultiStateCounter(Parcel in)77     private LongMultiStateCounter(Parcel in) {
78         mNativeObject = native_initFromParcel(in);
79         sRegistry.registerNativeAllocation(this, mNativeObject);
80 
81         mStateCount = native_getStateCount(mNativeObject);
82     }
83 
getStateCount()84     public int getStateCount() {
85         return mStateCount;
86     }
87 
88     /**
89      * Enables or disables the counter.  When the counter is disabled, it does not
90      * accumulate counts supplied by the {@link #updateValue} method.
91      */
setEnabled(boolean enabled, long timestampMs)92     public void setEnabled(boolean enabled, long timestampMs) {
93         native_setEnabled(mNativeObject, enabled, timestampMs);
94     }
95 
96     /**
97      * Sets the current state to the supplied value.
98      *
99      * @param state The new state
100      * @param timestampMs The time when the state change occurred, e.g.
101      *                    SystemClock.elapsedRealtime()
102      */
setState(int state, long timestampMs)103     public void setState(int state, long timestampMs) {
104         if (state < 0 || state >= mStateCount) {
105             throw new IllegalArgumentException(
106                     "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
107         }
108         native_setState(mNativeObject, state, timestampMs);
109     }
110 
111     /**
112      * Sets the new values.  The delta between the previously set value and this value
113      * is distributed among the state according to the time the object spent in those states
114      * since the previous call to updateValue.
115      *
116      * @return The delta between the previous value and the new value.
117      */
updateValue(long value, long timestampMs)118     public long updateValue(long value, long timestampMs) {
119         return native_updateValue(mNativeObject, value, timestampMs);
120     }
121 
122     /**
123      * Adds the supplied values to the current accumulated values in the counter.
124      */
incrementValue(long count, long timestampMs)125     public void incrementValue(long count, long timestampMs) {
126         native_incrementValue(mNativeObject, count, timestampMs);
127     }
128 
129     /**
130      * Adds the supplied values to the current accumulated values in the counter.
131      */
addCount(long count)132     public void addCount(long count) {
133         native_addCount(mNativeObject, count);
134     }
135 
136     /**
137      * Resets the accumulated counts to 0.
138      */
reset()139     public void reset() {
140         native_reset(mNativeObject);
141     }
142 
143     /**
144      * Returns the accumulated count for the specified state.
145      */
getCount(int state)146     public long getCount(int state) {
147         if (state < 0 || state >= mStateCount) {
148             throw new IllegalArgumentException(
149                     "State: " + state + ", outside the range: [0-" + mStateCount + "]");
150         }
151         return native_getCount(mNativeObject, state);
152     }
153 
154     /**
155      * Returns the total accumulated count across all states.
156      */
getTotalCount()157     public long getTotalCount() {
158         long total = 0;
159         for (int state = 0; state < mStateCount; state++) {
160             total += native_getCount(mNativeObject, state);
161         }
162         return total;
163     }
164 
165     @Override
toString()166     public String toString() {
167         return native_toString(mNativeObject);
168     }
169 
170     @Override
writeToParcel(Parcel dest, int flags)171     public void writeToParcel(Parcel dest, int flags) {
172         native_writeToParcel(mNativeObject, dest, flags);
173     }
174 
175     @Override
describeContents()176     public int describeContents() {
177         return 0;
178     }
179 
180     public static final Creator<LongMultiStateCounter> CREATOR =
181             new Creator<LongMultiStateCounter>() {
182                 @Override
183                 public LongMultiStateCounter createFromParcel(Parcel in) {
184                     return new LongMultiStateCounter(in);
185                 }
186 
187                 @Override
188                 public LongMultiStateCounter[] newArray(int size) {
189                     return new LongMultiStateCounter[size];
190                 }
191             };
192 
193 
194     @CriticalNative
native_init(int stateCount)195     private static native long native_init(int stateCount);
196 
197     @CriticalNative
native_getReleaseFunc()198     private static native long native_getReleaseFunc();
199 
200     @CriticalNative
native_setEnabled(long nativeObject, boolean enabled, long timestampMs)201     private static native void native_setEnabled(long nativeObject, boolean enabled,
202             long timestampMs);
203 
204     @CriticalNative
native_setState(long nativeObject, int state, long timestampMs)205     private static native void native_setState(long nativeObject, int state, long timestampMs);
206 
207     @CriticalNative
native_updateValue(long nativeObject, long value, long timestampMs)208     private static native long native_updateValue(long nativeObject, long value, long timestampMs);
209 
210     @CriticalNative
native_incrementValue(long nativeObject, long increment, long timestampMs)211     private static native void native_incrementValue(long nativeObject, long increment,
212             long timestampMs);
213 
214     @CriticalNative
native_addCount(long nativeObject, long count)215     private static native void native_addCount(long nativeObject, long count);
216 
217     @CriticalNative
native_reset(long nativeObject)218     private static native void native_reset(long nativeObject);
219 
220     @CriticalNative
native_getCount(long nativeObject, int state)221     private static native long native_getCount(long nativeObject, int state);
222 
223     @FastNative
native_toString(long nativeObject)224     private native String native_toString(long nativeObject);
225 
226     @FastNative
native_writeToParcel(long nativeObject, Parcel dest, int flags)227     private native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
228 
229     @FastNative
native_initFromParcel(Parcel parcel)230     private static native long native_initFromParcel(Parcel parcel);
231 
232     @CriticalNative
native_getStateCount(long nativeObject)233     private static native int native_getStateCount(long nativeObject);
234 }
235