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