• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.annotation.Nullable;
20 import android.os.BadParcelableException;
21 import android.os.Parcel;
22 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
23 
24 import java.util.Arrays;
25 import java.util.HashMap;
26 
27 /**
28  * Native implementation substitutions for the LongArrayMultiStateCounter class.
29  */
30 @RavenwoodKeepWholeClass
31 class LongArrayMultiStateCounter_ravenwood {
32 
33     /**
34      * A reimplementation of {@link LongArrayMultiStateCounter}, only in
35      * Java instead of native.  The majority of the code (in C++) can be found in
36      * /frameworks/native/libs/battery/MultiStateCounter.h
37      */
38     private static class LongArrayMultiStateCounterRavenwood {
39         private final int mStateCount;
40         private final int mArrayLength;
41         private int mCurrentState;
42         private long mLastStateChangeTimestampMs = -1;
43         private long mLastUpdateTimestampMs = -1;
44         private boolean mEnabled = true;
45 
46         private static class State {
47             private long mTimeInStateSinceUpdate;
48             private long[] mCounter;
49         }
50 
51         private final State[] mStates;
52         private final long[] mValues;
53         private final long[] mDelta;
54 
LongArrayMultiStateCounterRavenwood(int stateCount, int arrayLength)55         LongArrayMultiStateCounterRavenwood(int stateCount, int arrayLength) {
56             mStateCount = stateCount;
57             mArrayLength = arrayLength;
58             mStates = new State[stateCount];
59             for (int i = 0; i < mStateCount; i++) {
60                 mStates[i] = new State();
61                 mStates[i].mCounter = new long[mArrayLength];
62             }
63             mValues = new long[mArrayLength];
64             mDelta = new long[mArrayLength];
65         }
66 
setEnabled(boolean enabled, long timestampMs)67         public void setEnabled(boolean enabled, long timestampMs) {
68             if (enabled == mEnabled) {
69                 return;
70             }
71 
72             if (!enabled) {
73                 setState(mCurrentState, timestampMs);
74                 mEnabled = false;
75             } else {
76                 if (timestampMs < mLastUpdateTimestampMs) {
77                     timestampMs = mLastUpdateTimestampMs;
78                 }
79 
80                 if (mLastStateChangeTimestampMs >= 0) {
81                     mLastStateChangeTimestampMs = timestampMs;
82                 }
83                 mEnabled = true;
84             }
85         }
86 
setState(int state, long timestampMs)87         public void setState(int state, long timestampMs) {
88             if (mEnabled && mLastStateChangeTimestampMs >= 0 && mLastUpdateTimestampMs >= 0) {
89                 if (timestampMs < mLastUpdateTimestampMs) {
90                     timestampMs = mLastUpdateTimestampMs;
91                 }
92 
93                 if (timestampMs >= mLastStateChangeTimestampMs) {
94                     mStates[mCurrentState].mTimeInStateSinceUpdate +=
95                             timestampMs - mLastStateChangeTimestampMs;
96                 } else {
97                     for (int i = 0; i < mStateCount; i++) {
98                         mStates[i].mTimeInStateSinceUpdate = 0;
99                     }
100                 }
101             }
102             mCurrentState = state;
103             mLastStateChangeTimestampMs = timestampMs;
104         }
105 
copyStatesFrom(LongArrayMultiStateCounterRavenwood source)106         public void copyStatesFrom(LongArrayMultiStateCounterRavenwood source) {
107             for (int i = 0; i < mStateCount; i++) {
108                 mStates[i].mTimeInStateSinceUpdate = source.mStates[i].mTimeInStateSinceUpdate;
109                 Arrays.fill(mStates[i].mCounter, 0);
110             }
111             mCurrentState = source.mCurrentState;
112             mLastStateChangeTimestampMs = source.mLastStateChangeTimestampMs;
113             mLastUpdateTimestampMs = source.mLastUpdateTimestampMs;
114         }
115 
setValue(int state, long[] values)116         public void setValue(int state, long[] values) {
117             System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength);
118         }
119 
updateValue(long[] values, long timestampMs)120         public void updateValue(long[] values, long timestampMs) {
121             if (mEnabled || mLastUpdateTimestampMs < mLastStateChangeTimestampMs) {
122                 if (timestampMs < mLastStateChangeTimestampMs) {
123                     timestampMs = mLastStateChangeTimestampMs;
124                 }
125 
126                 setState(mCurrentState, timestampMs);
127 
128                 if (mLastUpdateTimestampMs >= 0) {
129                     if (timestampMs > mLastUpdateTimestampMs) {
130                         if (delta(mValues, values, mDelta)) {
131                             long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs;
132                             for (int i = 0; i < mStateCount; i++) {
133                                 long timeInState = mStates[i].mTimeInStateSinceUpdate;
134                                 if (timeInState > 0) {
135                                     add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate);
136                                     mStates[i].mTimeInStateSinceUpdate = 0;
137                                 }
138                             }
139                         } else {
140                             throw new RuntimeException();
141                         }
142                     } else if (timestampMs < mLastUpdateTimestampMs) {
143                         throw new RuntimeException();
144                     }
145                 }
146             }
147             System.arraycopy(values, 0, mValues, 0, mArrayLength);
148             mLastUpdateTimestampMs = timestampMs;
149         }
150 
incrementValues(@ullable long[] delta, long timestampMs)151         public void incrementValues(@Nullable long[] delta, long timestampMs) {
152             long[] values = Arrays.copyOf(mValues, mValues.length);
153             if (delta != null) {
154                 for (int i = 0; i < mArrayLength; i++) {
155                     values[i] += delta[i];
156                 }
157             }
158             updateValue(values, timestampMs);
159         }
160 
addCounts(long[] delta)161         public void addCounts(long[] delta) {
162             if (!mEnabled) {
163                 return;
164             }
165 
166             for (int i = 0; i < mArrayLength; i++) {
167                 mStates[mCurrentState].mCounter[i] += delta[i];
168             }
169         }
170 
getValues(long[] values, int state)171         public boolean getValues(long[] values, int state) {
172             long[] counts = mStates[state].mCounter;
173             boolean allZeros = true;
174             for (int i = 0; i < counts.length; i++) {
175                 if (counts[i] != 0) {
176                     allZeros = false;
177                     break;
178                 }
179             }
180             if (allZeros) {
181                 return false;
182             }
183             System.arraycopy(counts, 0, values, 0, mArrayLength);
184             return true;
185         }
186 
reset()187         public void reset() {
188             mLastStateChangeTimestampMs = -1;
189             mLastUpdateTimestampMs = -1;
190             for (int i = 0; i < mStateCount; i++) {
191                 mStates[i].mTimeInStateSinceUpdate = 0;
192                 Arrays.fill(mStates[i].mCounter, 0);
193             }
194         }
195 
writeToParcel(Parcel parcel)196         public void writeToParcel(Parcel parcel) {
197             parcel.writeInt(mStateCount);
198             parcel.writeInt(mArrayLength);
199             for (int i = 0; i < mStateCount; i++) {
200                 parcel.writeLongArray(mStates[i].mCounter);
201             }
202         }
203 
initFromParcel(Parcel parcel)204         public void initFromParcel(Parcel parcel) {
205             try {
206                 for (int i = 0; i < mStateCount; i++) {
207                     parcel.readLongArray(mStates[i].mCounter);
208                 }
209             } catch (Exception e) {
210                 throw new BadParcelableException(e);
211             }
212         }
213 
214         @Override
toString()215         public String toString() {
216             StringBuilder sb = new StringBuilder();
217             sb.append("[");
218             for (int state = 0; state < mStateCount; state++) {
219                 if (state != 0) {
220                     sb.append(", ");
221                 }
222                 sb.append(state).append(": {");
223                 for (int i = 0; i < mStates[state].mCounter.length; i++) {
224                     if (i != 0) {
225                         sb.append(", ");
226                     }
227                     sb.append(mStates[state].mCounter[i]);
228                 }
229                 sb.append("}");
230             }
231             sb.append("]");
232             if (mLastUpdateTimestampMs >= 0) {
233                 sb.append(" updated: ").append(mLastUpdateTimestampMs);
234             }
235             if (mLastStateChangeTimestampMs >= 0) {
236                 sb.append(" currentState: ").append(mCurrentState);
237                 if (mLastStateChangeTimestampMs > mLastUpdateTimestampMs) {
238                     sb.append(" stateChanged: ").append(mLastStateChangeTimestampMs);
239                 }
240             } else {
241                 sb.append(" currentState: none");
242             }
243             return sb.toString();
244         }
245 
delta(long[] values1, long[] values2, long[] delta)246         private boolean delta(long[] values1, long[] values2, long[] delta) {
247             if (delta.length != mArrayLength) {
248                 throw new RuntimeException();
249             }
250 
251             boolean is_delta_valid = true;
252             for (int i = 0; i < mArrayLength; i++) {
253                 if (values2[i] >= values1[i]) {
254                     delta[i] = values2[i] - values1[i];
255                 } else {
256                     delta[i] = 0;
257                     is_delta_valid = false;
258                 }
259             }
260 
261             return is_delta_valid;
262         }
263 
add(long[] counter, long[] delta, long numerator, long denominator)264         private void add(long[] counter, long[] delta, long numerator, long denominator) {
265             if (numerator != denominator) {
266                 for (int i = 0; i < mArrayLength; i++) {
267                     counter[i] += delta[i] * numerator / denominator;
268                 }
269             } else {
270                 for (int i = 0; i < mArrayLength; i++) {
271                     counter[i] += delta[i];
272                 }
273             }
274         }
275     }
276 
277     private static final HashMap<Long, LongArrayMultiStateCounterRavenwood> sInstances =
278             new HashMap<>();
279     private static long sNextId = 1;
280 
native_init(int stateCount, int arrayLength)281     public static long native_init(int stateCount, int arrayLength) {
282         LongArrayMultiStateCounterRavenwood instance = new LongArrayMultiStateCounterRavenwood(
283                 stateCount, arrayLength);
284         long instanceId = sNextId++;
285         sInstances.put(instanceId, instance);
286         return instanceId;
287     }
288 
getInstance(long instanceId)289     private static LongArrayMultiStateCounterRavenwood getInstance(long instanceId) {
290         return sInstances.get(instanceId);
291     }
292 
native_setEnabled(long instanceId, boolean enabled, long timestampMs)293     public static void native_setEnabled(long instanceId, boolean enabled,
294             long timestampMs) {
295         getInstance(instanceId).setEnabled(enabled, timestampMs);
296     }
297 
native_getStateCount(long instanceId)298     public static int native_getStateCount(long instanceId) {
299         return getInstance(instanceId).mStateCount;
300     }
301 
native_getArrayLength(long instanceId)302     public static int native_getArrayLength(long instanceId) {
303         return getInstance(instanceId).mArrayLength;
304     }
305 
native_setValues(long instanceId, int state, long[] values)306     public static void native_setValues(long instanceId, int state, long[] values) {
307         getInstance(instanceId).setValue(state, values);
308     }
309 
native_updateValues(long instanceId, long[] values, long timestampMs)310     public static void native_updateValues(long instanceId, long[] values, long timestampMs) {
311         getInstance(instanceId).updateValue(values, timestampMs);
312     }
313 
native_setState(long instanceId, int state, long timestampMs)314     public static void native_setState(long instanceId, int state, long timestampMs) {
315         getInstance(instanceId).setState(state, timestampMs);
316     }
317 
native_copyStatesFrom(long targetInstanceId, long sourceInstanceId)318     public static void native_copyStatesFrom(long targetInstanceId, long sourceInstanceId) {
319         getInstance(targetInstanceId).copyStatesFrom(getInstance(sourceInstanceId));
320     }
321 
native_incrementValues(long instanceId, @Nullable long[] delta, long timestampMs)322     public static void native_incrementValues(long instanceId, @Nullable long[] delta,
323             long timestampMs) {
324         getInstance(instanceId).incrementValues(delta, timestampMs);
325     }
326 
native_addCounts(long instanceId, long[] counts)327     public static void native_addCounts(long instanceId, long[] counts) {
328         getInstance(instanceId).addCounts(counts);
329     }
330 
native_getCounts(long instanceId, long[] counts, int state)331     public static boolean native_getCounts(long instanceId, long[] counts, int state) {
332         return getInstance(instanceId).getValues(counts, state);
333     }
334 
native_reset(long instanceId)335     public static void native_reset(long instanceId) {
336         getInstance(instanceId).reset();
337     }
338 
native_writeToParcel(long instanceId, Parcel parcel, int flags)339     public static void native_writeToParcel(long instanceId, Parcel parcel, int flags) {
340         getInstance(instanceId).writeToParcel(parcel);
341     }
342 
native_initFromParcel(Parcel parcel)343     public static long native_initFromParcel(Parcel parcel) {
344         int stateCount = parcel.readInt();
345         if (stateCount < 0 || stateCount > 0xEFFF) {
346             throw new BadParcelableException("stateCount out of range");
347         }
348         // LongArrayMultiStateCounter.cpp uses AParcel, which throws on out-of-data.
349         if (parcel.dataPosition() >= parcel.dataSize()) {
350             throw new RuntimeException("Bad parcel");
351         }
352         int arrayLength = parcel.readInt();
353         if (parcel.dataPosition() >= parcel.dataSize()) {
354             throw new RuntimeException("Bad parcel");
355         }
356         long instanceId = native_init(stateCount, arrayLength);
357         getInstance(instanceId).initFromParcel(parcel);
358         if (parcel.dataPosition() > parcel.dataSize()) {
359             throw new RuntimeException("Bad parcel");
360         }
361         return instanceId;
362     }
363 
native_toString(long instanceId)364     public static String native_toString(long instanceId) {
365         return getInstance(instanceId).toString();
366     }
367 }
368