summaryrefslogtreecommitdiff
path: root/core/java/com/android/internal/os/LongMultiStateCounter.java
blob: 33a9d547be7888ff6c4794441febc284346aba96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.os;

import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.util.Preconditions;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;

import libcore.util.NativeAllocationRegistry;

/**
 * Performs per-state counting of long integers over time.  The tracked "value" is expected
 * to increase monotonously. The counter keeps track of the current state.  When the
 * updateValue method is called, the delta from the previous invocation of this method
 * and the new value is added to the counter corresponding to the current state.  If the
 * state changed in the interim, the delta is distributed proptionally.
 *
 * The class's behavior is illustrated by this example:
 * <pre>
 *   // At 0 ms, the state of the tracked object is 0 and the initial tracked value is 100
 *   counter.setState(0, 0);
 *   counter.updateValue(100, 0);
 *
 *   // At 1000 ms, the state changes to 1
 *   counter.setState(1, 1000);
 *
 *   // At 3000 ms, the tracked value is updated to 130
 *   counter.updateValue(130, 3000);
 *
 *   // The delta (130 - 100 = 30) is distributed between states 0 and 1 according to the time
 *   // spent in those respective states; in this specific case, 1000 and 2000 ms.
 *   long countForState0 == counter.getCount(0);  // 10
 *   long countForState1 == counter.getCount(1);  // 20
 * </pre>
 *
 * The tracked values are expected to increase monotonically.
 *
 * @hide
 */
public final class LongMultiStateCounter implements Parcelable {

    private static final NativeAllocationRegistry sRegistry =
            NativeAllocationRegistry.createMalloced(
                    LongMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());

    private final int mStateCount;

    // Visible to other objects in this package so that it can be passed to @CriticalNative
    // methods.
    final long mNativeObject;

    public LongMultiStateCounter(int stateCount) {
        Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
        mStateCount = stateCount;
        mNativeObject = native_init(stateCount);
        sRegistry.registerNativeAllocation(this, mNativeObject);
    }

    private LongMultiStateCounter(Parcel in) {
        mNativeObject = native_initFromParcel(in);
        sRegistry.registerNativeAllocation(this, mNativeObject);

        mStateCount = native_getStateCount(mNativeObject);
    }

    public int getStateCount() {
        return mStateCount;
    }

    /**
     * Enables or disables the counter.  When the counter is disabled, it does not
     * accumulate counts supplied by the {@link #updateValue} method.
     */
    public void setEnabled(boolean enabled, long timestampMs) {
        native_setEnabled(mNativeObject, enabled, timestampMs);
    }

    /**
     * Sets the current state to the supplied value.
     *
     * @param state The new state
     * @param timestampMs The time when the state change occurred, e.g.
     *                    SystemClock.elapsedRealtime()
     */
    public void setState(int state, long timestampMs) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
        }
        native_setState(mNativeObject, state, timestampMs);
    }

    /**
     * Sets the new values.  The delta between the previously set value and this value
     * is distributed among the state according to the time the object spent in those states
     * since the previous call to updateValue.
     *
     * @return The delta between the previous value and the new value.
     */
    public long updateValue(long value, long timestampMs) {
        return native_updateValue(mNativeObject, value, timestampMs);
    }

    /**
     * Adds the supplied values to the current accumulated values in the counter.
     */
    public void incrementValue(long count, long timestampMs) {
        native_incrementValue(mNativeObject, count, timestampMs);
    }

    /**
     * Adds the supplied values to the current accumulated values in the counter.
     */
    public void addCount(long count) {
        native_addCount(mNativeObject, count);
    }

    /**
     * Resets the accumulated counts to 0.
     */
    public void reset() {
        native_reset(mNativeObject);
    }

    /**
     * Returns the accumulated count for the specified state.
     */
    public long getCount(int state) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
        }
        return native_getCount(mNativeObject, state);
    }

    /**
     * Returns the total accumulated count across all states.
     */
    public long getTotalCount() {
        long total = 0;
        for (int state = 0; state < mStateCount; state++) {
            total += native_getCount(mNativeObject, state);
        }
        return total;
    }

    @Override
    public String toString() {
        return native_toString(mNativeObject);
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        native_writeToParcel(mNativeObject, dest, flags);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<LongMultiStateCounter> CREATOR =
            new Creator<LongMultiStateCounter>() {
                @Override
                public LongMultiStateCounter createFromParcel(Parcel in) {
                    return new LongMultiStateCounter(in);
                }

                @Override
                public LongMultiStateCounter[] newArray(int size) {
                    return new LongMultiStateCounter[size];
                }
            };


    @CriticalNative
    private static native long native_init(int stateCount);

    @CriticalNative
    private static native long native_getReleaseFunc();

    @CriticalNative
    private static native void native_setEnabled(long nativeObject, boolean enabled,
            long timestampMs);

    @CriticalNative
    private static native void native_setState(long nativeObject, int state, long timestampMs);

    @CriticalNative
    private static native long native_updateValue(long nativeObject, long value, long timestampMs);

    @CriticalNative
    private static native void native_incrementValue(long nativeObject, long increment,
            long timestampMs);

    @CriticalNative
    private static native void native_addCount(long nativeObject, long count);

    @CriticalNative
    private static native void native_reset(long nativeObject);

    @CriticalNative
    private static native long native_getCount(long nativeObject, int state);

    @FastNative
    private native String native_toString(long nativeObject);

    @FastNative
    private native void native_writeToParcel(long nativeObject, Parcel dest, int flags);

    @FastNative
    private static native long native_initFromParcel(Parcel parcel);

    @CriticalNative
    private static native int native_getStateCount(long nativeObject);
}