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