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 public final class LongArrayMultiStateCounter implements Parcelable { 59 60 /** 61 * Container for a native equivalent of a long[]. 62 */ 63 public static class LongArrayContainer { 64 private static final NativeAllocationRegistry sRegistry = 65 NativeAllocationRegistry.createMalloced( 66 LongArrayContainer.class.getClassLoader(), native_getReleaseFunc()); 67 68 // Visible to other objects in this package so that it can be passed to @CriticalNative 69 // methods. 70 final long mNativeObject; 71 private final int mLength; 72 LongArrayContainer(int length)73 public LongArrayContainer(int length) { 74 mLength = length; 75 mNativeObject = native_init(length); 76 sRegistry.registerNativeAllocation(this, mNativeObject); 77 } 78 79 /** 80 * Copies the supplied values into the underlying native array. 81 */ setValues(long[] array)82 public void setValues(long[] array) { 83 if (array.length != mLength) { 84 throw new IllegalArgumentException( 85 "Invalid array length: " + mLength + ", expected: " + mLength); 86 } 87 native_setValues(mNativeObject, array); 88 } 89 90 /** 91 * Copies the underlying native array values to the supplied array. 92 */ getValues(long[] array)93 public void getValues(long[] array) { 94 if (array.length != mLength) { 95 throw new IllegalArgumentException( 96 "Invalid array length: " + mLength + ", expected: " + mLength); 97 } 98 native_getValues(mNativeObject, array); 99 } 100 101 /** 102 * Combines contained values into a smaller array by aggregating them 103 * according to an index map. 104 */ combineValues(long[] array, int[] indexMap)105 public boolean combineValues(long[] array, int[] indexMap) { 106 if (indexMap.length != mLength) { 107 throw new IllegalArgumentException( 108 "Wrong index map size " + indexMap.length + ", expected " + mLength); 109 } 110 return native_combineValues(mNativeObject, array, indexMap); 111 } 112 113 @Override toString()114 public String toString() { 115 final long[] array = new long[mLength]; 116 getValues(array); 117 return Arrays.toString(array); 118 } 119 120 @CriticalNative native_init(int length)121 private static native long native_init(int length); 122 123 @CriticalNative native_getReleaseFunc()124 private static native long native_getReleaseFunc(); 125 126 @FastNative native_setValues(long nativeObject, long[] array)127 private native void native_setValues(long nativeObject, long[] array); 128 129 @FastNative native_getValues(long nativeObject, long[] array)130 private native void native_getValues(long nativeObject, long[] array); 131 132 @FastNative native_combineValues(long nativeObject, long[] array, int[] indexMap)133 private native boolean native_combineValues(long nativeObject, long[] array, 134 int[] indexMap); 135 } 136 137 private static final NativeAllocationRegistry sRegistry = 138 NativeAllocationRegistry.createMalloced( 139 LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc()); 140 private static final AtomicReference<LongArrayContainer> sTmpArrayContainer = 141 new AtomicReference<>(); 142 143 private final int mStateCount; 144 private final int mLength; 145 146 // Visible to other objects in this package so that it can be passed to @CriticalNative 147 // methods. 148 final long mNativeObject; 149 LongArrayMultiStateCounter(int stateCount, int arrayLength)150 public LongArrayMultiStateCounter(int stateCount, int arrayLength) { 151 Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0"); 152 mStateCount = stateCount; 153 mLength = arrayLength; 154 mNativeObject = native_init(stateCount, arrayLength); 155 sRegistry.registerNativeAllocation(this, mNativeObject); 156 } 157 LongArrayMultiStateCounter(Parcel in)158 private LongArrayMultiStateCounter(Parcel in) { 159 mNativeObject = native_initFromParcel(in); 160 sRegistry.registerNativeAllocation(this, mNativeObject); 161 162 mStateCount = native_getStateCount(mNativeObject); 163 mLength = native_getArrayLength(mNativeObject); 164 } 165 getStateCount()166 public int getStateCount() { 167 return mStateCount; 168 } 169 getArrayLength()170 public int getArrayLength() { 171 return mLength; 172 } 173 174 /** 175 * Enables or disables the counter. When the counter is disabled, it does not 176 * accumulate counts supplied by the {@link #updateValues} method. 177 */ setEnabled(boolean enabled, long timestampMs)178 public void setEnabled(boolean enabled, long timestampMs) { 179 native_setEnabled(mNativeObject, enabled, timestampMs); 180 } 181 182 /** 183 * Sets the current state to the supplied value. 184 */ setState(int state, long timestampMs)185 public void setState(int state, long timestampMs) { 186 if (state < 0 || state >= mStateCount) { 187 throw new IllegalArgumentException( 188 "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]"); 189 } 190 native_setState(mNativeObject, state, timestampMs); 191 } 192 193 /** 194 * Sets the new values. The delta between the previously set values and these values 195 * is distributed among the state according to the time the object spent in those states 196 * since the previous call to updateValues. 197 */ updateValues(LongArrayContainer longArrayContainer, long timestampMs)198 public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) { 199 if (longArrayContainer.mLength != mLength) { 200 throw new IllegalArgumentException( 201 "Invalid array length: " + longArrayContainer.mLength + ", expected: " 202 + mLength); 203 } 204 native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs); 205 } 206 207 /** 208 * Adds the supplied values to the current accumulated values in the counter. 209 */ addCounts(LongArrayContainer counts)210 public void addCounts(LongArrayContainer counts) { 211 if (counts.mLength != mLength) { 212 throw new IllegalArgumentException( 213 "Invalid array length: " + counts.mLength + ", expected: " + mLength); 214 } 215 native_addCounts(mNativeObject, counts.mNativeObject); 216 } 217 218 /** 219 * Resets the accumulated counts to 0. 220 */ reset()221 public void reset() { 222 native_reset(mNativeObject); 223 } 224 225 /** 226 * Populates the array with the accumulated counts for the specified state. 227 */ getCounts(long[] counts, int state)228 public void getCounts(long[] counts, int state) { 229 LongArrayContainer container = sTmpArrayContainer.getAndSet(null); 230 if (container == null || container.mLength != counts.length) { 231 container = new LongArrayContainer(counts.length); 232 } 233 getCounts(container, state); 234 container.getValues(counts); 235 sTmpArrayContainer.set(container); 236 } 237 238 /** 239 * Populates longArrayContainer with the accumulated counts for the specified state. 240 */ getCounts(LongArrayContainer longArrayContainer, int state)241 public void getCounts(LongArrayContainer longArrayContainer, int state) { 242 if (state < 0 || state >= mStateCount) { 243 throw new IllegalArgumentException( 244 "State: " + state + ", outside the range: [0-" + mStateCount + "]"); 245 } 246 native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state); 247 } 248 249 @Override toString()250 public String toString() { 251 return native_toString(mNativeObject); 252 } 253 254 @Override writeToParcel(Parcel dest, int flags)255 public void writeToParcel(Parcel dest, int flags) { 256 native_writeToParcel(mNativeObject, dest, flags); 257 } 258 259 @Override describeContents()260 public int describeContents() { 261 return 0; 262 } 263 264 public static final Creator<LongArrayMultiStateCounter> CREATOR = 265 new Creator<LongArrayMultiStateCounter>() { 266 @Override 267 public LongArrayMultiStateCounter createFromParcel(Parcel in) { 268 return new LongArrayMultiStateCounter(in); 269 } 270 271 @Override 272 public LongArrayMultiStateCounter[] newArray(int size) { 273 return new LongArrayMultiStateCounter[size]; 274 } 275 }; 276 277 278 @CriticalNative native_init(int stateCount, int arrayLength)279 private static native long native_init(int stateCount, int arrayLength); 280 281 @CriticalNative native_getReleaseFunc()282 private static native long native_getReleaseFunc(); 283 284 @CriticalNative native_setEnabled(long nativeObject, boolean enabled, long timestampMs)285 private static native void native_setEnabled(long nativeObject, boolean enabled, 286 long timestampMs); 287 288 @CriticalNative native_setState(long nativeObject, int state, long timestampMs)289 private static native void native_setState(long nativeObject, int state, long timestampMs); 290 291 @CriticalNative native_updateValues(long nativeObject, long longArrayContainerNativeObject, long timestampMs)292 private static native void native_updateValues(long nativeObject, 293 long longArrayContainerNativeObject, long timestampMs); 294 295 @CriticalNative native_addCounts(long nativeObject, long longArrayContainerNativeObject)296 private static native void native_addCounts(long nativeObject, 297 long longArrayContainerNativeObject); 298 299 @CriticalNative native_reset(long nativeObject)300 private static native void native_reset(long nativeObject); 301 302 @CriticalNative native_getCounts(long nativeObject, long longArrayContainerNativeObject, int state)303 private static native void native_getCounts(long nativeObject, 304 long longArrayContainerNativeObject, int state); 305 306 @FastNative native_toString(long nativeObject)307 private native String native_toString(long nativeObject); 308 309 @FastNative native_writeToParcel(long nativeObject, Parcel dest, int flags)310 private native void native_writeToParcel(long nativeObject, Parcel dest, int flags); 311 312 @FastNative native_initFromParcel(Parcel parcel)313 private static native long native_initFromParcel(Parcel parcel); 314 315 @CriticalNative native_getStateCount(long nativeObject)316 private static native int native_getStateCount(long nativeObject); 317 318 @CriticalNative native_getArrayLength(long nativeObject)319 private static native int native_getArrayLength(long nativeObject); 320 } 321