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 import java.util.Arrays;
30 import java.util.concurrent.atomic.AtomicReference;
31 
32 /**
33  * Performs per-state counting of multi-element values over time. The class' behavior is illustrated
34  * by this example:
35  * <pre>
36  *   // At 0 ms, the state of the tracked object is 0
37  *   counter.setState(0, 0);
38  *
39  *   // At 1000 ms, the state changes to 1
40  *   counter.setState(1, 1000);
41  *
42  *   // At 3000 ms, the tracked values are updated to {30, 300}
43  *   arrayContainer.setValues(new long[]{{30, 300}};
44  *   counter.updateValues(arrayContainer, 3000);
45  *
46  *   // The values are distributed between states 0 and 1 according to the time
47  *   // spent in those respective states. In this specific case, 1000 and 2000 ms.
48  *   counter.getValues(arrayContainer, 0);
49  *   // arrayContainer now has values {10, 100}
50  *   counter.getValues(arrayContainer, 1);
51  *   // arrayContainer now has values {20, 200}
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.LongArrayMultiStateCounter_host")
61 public final class LongArrayMultiStateCounter implements Parcelable {
62 
63     /**
64      * Container for a native equivalent of a long[].
65      */
66     @android.ravenwood.annotation.RavenwoodKeepWholeClass
67     @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
68             "com.android.platform.test.ravenwood.nativesubstitution"
69             + ".LongArrayMultiStateCounter_host$LongArrayContainer_host")
70     public static class LongArrayContainer {
71         private static NativeAllocationRegistry sRegistry;
72 
73         // Visible to other objects in this package so that it can be passed to @CriticalNative
74         // methods.
75         final long mNativeObject;
76         private final int mLength;
77 
LongArrayContainer(int length)78         public LongArrayContainer(int length) {
79             mLength = length;
80             mNativeObject = native_init(length);
81             registerNativeAllocation();
82         }
83 
84         @android.ravenwood.annotation.RavenwoodReplace
registerNativeAllocation()85         private void registerNativeAllocation() {
86             if (sRegistry == null) {
87                 synchronized (LongArrayMultiStateCounter.class) {
88                     if (sRegistry == null) {
89                         sRegistry = NativeAllocationRegistry.createMalloced(
90                                 LongArrayContainer.class.getClassLoader(), native_getReleaseFunc());
91                     }
92                 }
93             }
94             sRegistry.registerNativeAllocation(this, mNativeObject);
95         }
96 
registerNativeAllocation$ravenwood()97         private void registerNativeAllocation$ravenwood() {
98             // No-op under ravenwood
99         }
100 
101         /**
102          * Copies the supplied values into the underlying native array.
103          */
setValues(long[] array)104         public void setValues(long[] array) {
105             if (array.length != mLength) {
106                 throw new IllegalArgumentException(
107                         "Invalid array length: " + mLength + ", expected: " + mLength);
108             }
109             native_setValues(mNativeObject, array);
110         }
111 
112         /**
113          * Copies the underlying native array values to the supplied array.
114          */
getValues(long[] array)115         public void getValues(long[] array) {
116             if (array.length != mLength) {
117                 throw new IllegalArgumentException(
118                         "Invalid array length: " + mLength + ", expected: " + mLength);
119             }
120             native_getValues(mNativeObject, array);
121         }
122 
123         /**
124          * Combines contained values into a smaller array by aggregating them
125          * according to an index map.
126          */
combineValues(long[] array, int[] indexMap)127         public boolean combineValues(long[] array, int[] indexMap) {
128             if (indexMap.length != mLength) {
129                 throw new IllegalArgumentException(
130                         "Wrong index map size " + indexMap.length + ", expected " + mLength);
131             }
132             return native_combineValues(mNativeObject, array, indexMap);
133         }
134 
135         @Override
toString()136         public String toString() {
137             final long[] array = new long[mLength];
138             getValues(array);
139             return Arrays.toString(array);
140         }
141 
142         @CriticalNative
native_init(int length)143         private static native long native_init(int length);
144 
145         @CriticalNative
native_getReleaseFunc()146         private static native long native_getReleaseFunc();
147 
148         @FastNative
native_setValues(long nativeObject, long[] array)149         private static native void native_setValues(long nativeObject, long[] array);
150 
151         @FastNative
native_getValues(long nativeObject, long[] array)152         private static native void native_getValues(long nativeObject, long[] array);
153 
154         @FastNative
native_combineValues(long nativeObject, long[] array, int[] indexMap)155         private static native boolean native_combineValues(long nativeObject, long[] array,
156                 int[] indexMap);
157     }
158 
159     private static volatile NativeAllocationRegistry sRegistry;
160     private static final AtomicReference<LongArrayContainer> sTmpArrayContainer =
161             new AtomicReference<>();
162 
163     private final int mStateCount;
164     private final int mLength;
165 
166     // Visible to other objects in this package so that it can be passed to @CriticalNative
167     // methods.
168     final long mNativeObject;
169 
LongArrayMultiStateCounter(int stateCount, int arrayLength)170     public LongArrayMultiStateCounter(int stateCount, int arrayLength) {
171         Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
172         mStateCount = stateCount;
173         mLength = arrayLength;
174         mNativeObject = native_init(stateCount, arrayLength);
175         registerNativeAllocation();
176     }
177 
178     @android.ravenwood.annotation.RavenwoodReplace
registerNativeAllocation()179     private void registerNativeAllocation() {
180         if (sRegistry == null) {
181             synchronized (LongArrayMultiStateCounter.class) {
182                 if (sRegistry == null) {
183                     sRegistry = NativeAllocationRegistry.createMalloced(
184                             LongArrayMultiStateCounter.class.getClassLoader(),
185                             native_getReleaseFunc());
186                 }
187             }
188         }
189         sRegistry.registerNativeAllocation(this, mNativeObject);
190     }
191 
registerNativeAllocation$ravenwood()192     private void registerNativeAllocation$ravenwood() {
193         // No-op under ravenwood
194     }
195 
LongArrayMultiStateCounter(Parcel in)196     private LongArrayMultiStateCounter(Parcel in) {
197         mNativeObject = native_initFromParcel(in);
198         registerNativeAllocation();
199 
200         mStateCount = native_getStateCount(mNativeObject);
201         mLength = native_getArrayLength(mNativeObject);
202     }
203 
getStateCount()204     public int getStateCount() {
205         return mStateCount;
206     }
207 
getArrayLength()208     public int getArrayLength() {
209         return mLength;
210     }
211 
212     /**
213      * Enables or disables the counter.  When the counter is disabled, it does not
214      * accumulate counts supplied by the {@link #updateValues} method.
215      */
setEnabled(boolean enabled, long timestampMs)216     public void setEnabled(boolean enabled, long timestampMs) {
217         native_setEnabled(mNativeObject, enabled, timestampMs);
218     }
219 
220     /**
221      * Sets the current state to the supplied value.
222      */
setState(int state, long timestampMs)223     public void setState(int state, long timestampMs) {
224         if (state < 0 || state >= mStateCount) {
225             throw new IllegalArgumentException(
226                     "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
227         }
228         native_setState(mNativeObject, state, timestampMs);
229     }
230 
231     /**
232      * Copies time-in-state and timestamps from the supplied counter.
233      */
copyStatesFrom(LongArrayMultiStateCounter counter)234     public void copyStatesFrom(LongArrayMultiStateCounter counter) {
235         if (mStateCount != counter.mStateCount) {
236             throw new IllegalArgumentException(
237                     "State count is not the same: " + mStateCount + " vs. " + counter.mStateCount);
238         }
239         native_copyStatesFrom(mNativeObject, counter.mNativeObject);
240     }
241 
242     /**
243      * Sets the new values for the given state.
244      */
setValues(int state, long[] values)245     public void setValues(int state, long[] values) {
246         if (state < 0 || state >= mStateCount) {
247             throw new IllegalArgumentException(
248                     "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
249         }
250         if (values.length != mLength) {
251             throw new IllegalArgumentException(
252                     "Invalid array length: " + values.length + ", expected: " + mLength);
253         }
254         LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
255         if (container == null || container.mLength != values.length) {
256             container = new LongArrayContainer(values.length);
257         }
258         container.setValues(values);
259         native_setValues(mNativeObject, state, container.mNativeObject);
260         sTmpArrayContainer.set(container);
261     }
262 
263     /**
264      * Sets the new values.  The delta between the previously set values and these values
265      * is distributed among the state according to the time the object spent in those states
266      * since the previous call to updateValues.
267      */
updateValues(long[] values, long timestampMs)268     public void updateValues(long[] values, long timestampMs) {
269         LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
270         if (container == null || container.mLength != values.length) {
271             container = new LongArrayContainer(values.length);
272         }
273         container.setValues(values);
274         updateValues(container, timestampMs);
275         sTmpArrayContainer.set(container);
276     }
277 
278     /**
279      * Adds the supplied values to the current accumulated values in the counter.
280      */
incrementValues(long[] values, long timestampMs)281     public void incrementValues(long[] values, long timestampMs) {
282         LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
283         if (container == null || container.mLength != values.length) {
284             container = new LongArrayContainer(values.length);
285         }
286         container.setValues(values);
287         native_incrementValues(mNativeObject, container.mNativeObject, timestampMs);
288         sTmpArrayContainer.set(container);
289     }
290 
291     /**
292      * Sets the new values.  The delta between the previously set values and these values
293      * is distributed among the state according to the time the object spent in those states
294      * since the previous call to updateValues.
295      */
updateValues(LongArrayContainer longArrayContainer, long timestampMs)296     public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) {
297         if (longArrayContainer.mLength != mLength) {
298             throw new IllegalArgumentException(
299                     "Invalid array length: " + longArrayContainer.mLength + ", expected: "
300                             + mLength);
301         }
302         native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs);
303     }
304 
305     /**
306      * Adds the supplied values to the current accumulated values in the counter.
307      */
addCounts(LongArrayContainer counts)308     public void addCounts(LongArrayContainer counts) {
309         if (counts.mLength != mLength) {
310             throw new IllegalArgumentException(
311                     "Invalid array length: " + counts.mLength + ", expected: " + mLength);
312         }
313         native_addCounts(mNativeObject, counts.mNativeObject);
314     }
315 
316     /**
317      * Resets the accumulated counts to 0.
318      */
reset()319     public void reset() {
320         native_reset(mNativeObject);
321     }
322 
323     /**
324      * Populates the array with the accumulated counts for the specified state.
325      */
getCounts(long[] counts, int state)326     public void getCounts(long[] counts, int state) {
327         LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
328         if (container == null || container.mLength != counts.length) {
329             container = new LongArrayContainer(counts.length);
330         }
331         getCounts(container, state);
332         container.getValues(counts);
333         sTmpArrayContainer.set(container);
334     }
335 
336     /**
337      * Populates longArrayContainer with the accumulated counts for the specified state.
338      */
getCounts(LongArrayContainer longArrayContainer, int state)339     public void getCounts(LongArrayContainer longArrayContainer, int state) {
340         if (state < 0 || state >= mStateCount) {
341             throw new IllegalArgumentException(
342                     "State: " + state + ", outside the range: [0-" + mStateCount + "]");
343         }
344         native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state);
345     }
346 
347     @Override
toString()348     public String toString() {
349         return native_toString(mNativeObject);
350     }
351 
352     @Override
writeToParcel(Parcel dest, int flags)353     public void writeToParcel(Parcel dest, int flags) {
354         native_writeToParcel(mNativeObject, dest, flags);
355     }
356 
357     @Override
describeContents()358     public int describeContents() {
359         return 0;
360     }
361 
362     public static final Creator<LongArrayMultiStateCounter> CREATOR =
363             new Creator<LongArrayMultiStateCounter>() {
364                 @Override
365                 public LongArrayMultiStateCounter createFromParcel(Parcel in) {
366                     return new LongArrayMultiStateCounter(in);
367                 }
368 
369                 @Override
370                 public LongArrayMultiStateCounter[] newArray(int size) {
371                     return new LongArrayMultiStateCounter[size];
372                 }
373             };
374 
375 
376     @CriticalNative
native_init(int stateCount, int arrayLength)377     private static native long native_init(int stateCount, int arrayLength);
378 
379     @CriticalNative
native_getReleaseFunc()380     private static native long native_getReleaseFunc();
381 
382     @CriticalNative
native_setEnabled(long nativeObject, boolean enabled, long timestampMs)383     private static native void native_setEnabled(long nativeObject, boolean enabled,
384             long timestampMs);
385 
386     @CriticalNative
native_setState(long nativeObject, int state, long timestampMs)387     private static native void native_setState(long nativeObject, int state, long timestampMs);
388 
389     @CriticalNative
native_copyStatesFrom(long nativeObjectTarget, long nativeObjectSource)390     private static native void native_copyStatesFrom(long nativeObjectTarget,
391             long nativeObjectSource);
392 
393     @CriticalNative
native_setValues(long nativeObject, int state, long longArrayContainerNativeObject)394     private static native void native_setValues(long nativeObject, int state,
395             long longArrayContainerNativeObject);
396 
397     @CriticalNative
native_updateValues(long nativeObject, long longArrayContainerNativeObject, long timestampMs)398     private static native void native_updateValues(long nativeObject,
399             long longArrayContainerNativeObject, long timestampMs);
400 
401     @CriticalNative
native_incrementValues(long nativeObject, long longArrayContainerNativeObject, long timestampMs)402     private static native void native_incrementValues(long nativeObject,
403             long longArrayContainerNativeObject, long timestampMs);
404 
405     @CriticalNative
native_addCounts(long nativeObject, long longArrayContainerNativeObject)406     private static native void native_addCounts(long nativeObject,
407             long longArrayContainerNativeObject);
408 
409     @CriticalNative
native_reset(long nativeObject)410     private static native void native_reset(long nativeObject);
411 
412     @CriticalNative
native_getCounts(long nativeObject, long longArrayContainerNativeObject, int state)413     private static native void native_getCounts(long nativeObject,
414             long longArrayContainerNativeObject, int state);
415 
416     @FastNative
native_toString(long nativeObject)417     private static native String native_toString(long nativeObject);
418 
419     @FastNative
native_writeToParcel(long nativeObject, Parcel dest, int flags)420     private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
421 
422     @FastNative
native_initFromParcel(Parcel parcel)423     private static native long native_initFromParcel(Parcel parcel);
424 
425     @CriticalNative
native_getStateCount(long nativeObject)426     private static native int native_getStateCount(long nativeObject);
427 
428     @CriticalNative
native_getArrayLength(long nativeObject)429     private static native int native_getArrayLength(long nativeObject);
430 }
431