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