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 @android.ravenwood.annotation.RavenwoodKeepWholeClass
59 @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
60         "com.android.platform.test.ravenwood.nativesubstitution.LongMultiStateCounter_host")
61 public final class LongMultiStateCounter implements Parcelable {
62 
63     private static NativeAllocationRegistry sRegistry;
64 
65     private final int mStateCount;
66 
67     // Visible to other objects in this package so that it can be passed to @CriticalNative
68     // methods.
69     final long mNativeObject;
70 
LongMultiStateCounter(int stateCount)71     public LongMultiStateCounter(int stateCount) {
72         Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
73         mStateCount = stateCount;
74         mNativeObject = native_init(stateCount);
75         registerNativeAllocation();
76     }
77 
LongMultiStateCounter(Parcel in)78     private LongMultiStateCounter(Parcel in) {
79         mNativeObject = native_initFromParcel(in);
80         registerNativeAllocation();
81 
82         mStateCount = native_getStateCount(mNativeObject);
83     }
84 
85     @android.ravenwood.annotation.RavenwoodReplace
registerNativeAllocation()86     private void registerNativeAllocation() {
87         if (sRegistry == null) {
88             synchronized (LongMultiStateCounter.class) {
89                 if (sRegistry == null) {
90                     sRegistry = NativeAllocationRegistry.createMalloced(
91                             LongMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());
92                 }
93             }
94         }
95         sRegistry.registerNativeAllocation(this, mNativeObject);
96     }
97 
registerNativeAllocation$ravenwood()98     private void registerNativeAllocation$ravenwood() {
99         // No-op under ravenwood
100     }
101 
getStateCount()102     public int getStateCount() {
103         return mStateCount;
104     }
105 
106     /**
107      * Enables or disables the counter.  When the counter is disabled, it does not
108      * accumulate counts supplied by the {@link #updateValue} method.
109      */
setEnabled(boolean enabled, long timestampMs)110     public void setEnabled(boolean enabled, long timestampMs) {
111         native_setEnabled(mNativeObject, enabled, timestampMs);
112     }
113 
114     /**
115      * Sets the current state to the supplied value.
116      *
117      * @param state The new state
118      * @param timestampMs The time when the state change occurred, e.g.
119      *                    SystemClock.elapsedRealtime()
120      */
setState(int state, long timestampMs)121     public void setState(int state, long timestampMs) {
122         if (state < 0 || state >= mStateCount) {
123             throw new IllegalArgumentException(
124                     "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
125         }
126         native_setState(mNativeObject, state, timestampMs);
127     }
128 
129     /**
130      * Sets the new values.  The delta between the previously set value and this value
131      * is distributed among the state according to the time the object spent in those states
132      * since the previous call to updateValue.
133      *
134      * @return The delta between the previous value and the new value.
135      */
updateValue(long value, long timestampMs)136     public long updateValue(long value, long timestampMs) {
137         return native_updateValue(mNativeObject, value, timestampMs);
138     }
139 
140     /**
141      * Adds the supplied values to the current accumulated values in the counter.
142      */
incrementValue(long count, long timestampMs)143     public void incrementValue(long count, long timestampMs) {
144         native_incrementValue(mNativeObject, count, timestampMs);
145     }
146 
147     /**
148      * Adds the supplied values to the current accumulated values in the counter.
149      */
addCount(long count)150     public void addCount(long count) {
151         native_addCount(mNativeObject, count);
152     }
153 
154     /**
155      * Resets the accumulated counts to 0.
156      */
reset()157     public void reset() {
158         native_reset(mNativeObject);
159     }
160 
161     /**
162      * Returns the accumulated count for the specified state.
163      */
getCount(int state)164     public long getCount(int state) {
165         if (state < 0 || state >= mStateCount) {
166             throw new IllegalArgumentException(
167                     "State: " + state + ", outside the range: [0-" + mStateCount + "]");
168         }
169         return native_getCount(mNativeObject, state);
170     }
171 
172     /**
173      * Returns the total accumulated count across all states.
174      */
getTotalCount()175     public long getTotalCount() {
176         long total = 0;
177         for (int state = 0; state < mStateCount; state++) {
178             total += native_getCount(mNativeObject, state);
179         }
180         return total;
181     }
182 
183     @Override
toString()184     public String toString() {
185         return native_toString(mNativeObject);
186     }
187 
188     @Override
writeToParcel(Parcel dest, int flags)189     public void writeToParcel(Parcel dest, int flags) {
190         native_writeToParcel(mNativeObject, dest, flags);
191     }
192 
193     @Override
describeContents()194     public int describeContents() {
195         return 0;
196     }
197 
198     public static final Creator<LongMultiStateCounter> CREATOR =
199             new Creator<LongMultiStateCounter>() {
200                 @Override
201                 public LongMultiStateCounter createFromParcel(Parcel in) {
202                     return new LongMultiStateCounter(in);
203                 }
204 
205                 @Override
206                 public LongMultiStateCounter[] newArray(int size) {
207                     return new LongMultiStateCounter[size];
208                 }
209             };
210 
211 
212     @CriticalNative
native_init(int stateCount)213     private static native long native_init(int stateCount);
214 
215     @CriticalNative
native_getReleaseFunc()216     private static native long native_getReleaseFunc();
217 
218     @CriticalNative
native_setEnabled(long nativeObject, boolean enabled, long timestampMs)219     private static native void native_setEnabled(long nativeObject, boolean enabled,
220             long timestampMs);
221 
222     @CriticalNative
native_setState(long nativeObject, int state, long timestampMs)223     private static native void native_setState(long nativeObject, int state, long timestampMs);
224 
225     @CriticalNative
native_updateValue(long nativeObject, long value, long timestampMs)226     private static native long native_updateValue(long nativeObject, long value, long timestampMs);
227 
228     @CriticalNative
native_incrementValue(long nativeObject, long increment, long timestampMs)229     private static native void native_incrementValue(long nativeObject, long increment,
230             long timestampMs);
231 
232     @CriticalNative
native_addCount(long nativeObject, long count)233     private static native void native_addCount(long nativeObject, long count);
234 
235     @CriticalNative
native_reset(long nativeObject)236     private static native void native_reset(long nativeObject);
237 
238     @CriticalNative
native_getCount(long nativeObject, int state)239     private static native long native_getCount(long nativeObject, int state);
240 
241     @FastNative
native_toString(long nativeObject)242     private static native String native_toString(long nativeObject);
243 
244     @FastNative
native_writeToParcel(long nativeObject, Parcel dest, int flags)245     private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
246 
247     @FastNative
native_initFromParcel(Parcel parcel)248     private static native long native_initFromParcel(Parcel parcel);
249 
250     @CriticalNative
native_getStateCount(long nativeObject)251     private static native int native_getStateCount(long nativeObject);
252 }
253