1 /* 2 * Copyright (C) 2024 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.BadParcelableException; 20 import android.os.Parcel; 21 import android.ravenwood.annotation.RavenwoodKeepWholeClass; 22 23 import java.util.HashMap; 24 25 /** 26 * Native implementation substitutions for the LongMultiStateCounter class. 27 */ 28 @RavenwoodKeepWholeClass 29 class LongMultiStateCounter_ravenwood { 30 31 /** 32 * A reimplementation of {@link com.android.internal.os.LongMultiStateCounter}, only in 33 * Java instead of native. The majority of the code (in C++) can be found in 34 * /frameworks/native/libs/battery/MultiStateCounter.h 35 */ 36 private static class LongMultiStateCounterRavenwood { 37 private final int mStateCount; 38 private int mCurrentState; 39 private long mLastStateChangeTimestampMs = -1; 40 private long mLastUpdateTimestampMs = -1; 41 private boolean mEnabled = true; 42 43 private static class State { 44 private long mTimeInStateSinceUpdate; 45 private long mCounter; 46 } 47 48 private final State[] mStates; 49 private long mValue; 50 LongMultiStateCounterRavenwood(int stateCount)51 LongMultiStateCounterRavenwood(int stateCount) { 52 mStateCount = stateCount; 53 mStates = new State[stateCount]; 54 for (int i = 0; i < mStateCount; i++) { 55 mStates[i] = new State(); 56 } 57 } 58 setEnabled(boolean enabled, long timestampMs)59 public void setEnabled(boolean enabled, long timestampMs) { 60 if (enabled == mEnabled) { 61 return; 62 } 63 64 if (!enabled) { 65 setState(mCurrentState, timestampMs); 66 mEnabled = false; 67 } else { 68 if (timestampMs < mLastUpdateTimestampMs) { 69 timestampMs = mLastUpdateTimestampMs; 70 } 71 72 if (mLastStateChangeTimestampMs >= 0) { 73 mLastStateChangeTimestampMs = timestampMs; 74 } 75 mEnabled = true; 76 } 77 } 78 setState(int state, long timestampMs)79 public void setState(int state, long timestampMs) { 80 if (mEnabled && mLastStateChangeTimestampMs >= 0 && mLastUpdateTimestampMs >= 0) { 81 if (timestampMs < mLastUpdateTimestampMs) { 82 timestampMs = mLastUpdateTimestampMs; 83 } 84 85 if (timestampMs >= mLastStateChangeTimestampMs) { 86 mStates[mCurrentState].mTimeInStateSinceUpdate += 87 timestampMs - mLastStateChangeTimestampMs; 88 } else { 89 for (int i = 0; i < mStateCount; i++) { 90 mStates[i].mTimeInStateSinceUpdate = 0; 91 } 92 } 93 } 94 mCurrentState = state; 95 mLastStateChangeTimestampMs = timestampMs; 96 } 97 updateValue(long value, long timestampMs)98 public long updateValue(long value, long timestampMs) { 99 long returnValue = 0; 100 if (mEnabled || mLastUpdateTimestampMs < mLastStateChangeTimestampMs) { 101 if (timestampMs < mLastStateChangeTimestampMs) { 102 timestampMs = mLastStateChangeTimestampMs; 103 } 104 105 setState(mCurrentState, timestampMs); 106 107 if (mLastUpdateTimestampMs >= 0) { 108 if (timestampMs > mLastUpdateTimestampMs) { 109 long delta = value - mValue; 110 if (delta >= 0) { 111 returnValue = delta; 112 long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs; 113 for (int i = 0; i < mStateCount; i++) { 114 long timeInState = mStates[i].mTimeInStateSinceUpdate; 115 if (timeInState > 0) { 116 mStates[i].mCounter += delta * timeInState / timeSinceUpdate; 117 mStates[i].mTimeInStateSinceUpdate = 0; 118 } 119 } 120 } else { 121 for (int i = 0; i < mStateCount; i++) { 122 mStates[i].mTimeInStateSinceUpdate = 0; 123 } 124 } 125 } else if (timestampMs < mLastUpdateTimestampMs) { 126 for (int i = 0; i < mStateCount; i++) { 127 mStates[i].mTimeInStateSinceUpdate = 0; 128 } 129 } 130 } 131 } 132 mValue = value; 133 mLastUpdateTimestampMs = timestampMs; 134 return returnValue; 135 } 136 incrementValue(long count, long timestampMs)137 public void incrementValue(long count, long timestampMs) { 138 updateValue(mValue + count, timestampMs); 139 } 140 getValue(int state)141 public long getValue(int state) { 142 return mStates[state].mCounter; 143 } 144 reset()145 public void reset() { 146 mLastStateChangeTimestampMs = -1; 147 mLastUpdateTimestampMs = -1; 148 for (int i = 0; i < mStateCount; i++) { 149 mStates[i].mTimeInStateSinceUpdate = 0; 150 mStates[i].mCounter = 0; 151 } 152 } 153 writeToParcel(Parcel parcel)154 public void writeToParcel(Parcel parcel) { 155 parcel.writeInt(mStateCount); 156 for (int i = 0; i < mStateCount; i++) { 157 parcel.writeLong(mStates[i].mCounter); 158 } 159 } 160 initFromParcel(Parcel parcel)161 public void initFromParcel(Parcel parcel) { 162 try { 163 for (int i = 0; i < mStateCount; i++) { 164 mStates[i].mCounter = parcel.readLong(); 165 } 166 } catch (Exception e) { 167 throw new BadParcelableException(e); 168 } 169 } 170 171 @Override toString()172 public String toString() { 173 StringBuilder sb = new StringBuilder(); 174 sb.append("["); 175 for (int state = 0; state < mStateCount; state++) { 176 if (state != 0) { 177 sb.append(", "); 178 } 179 sb.append(state).append(": ").append(mStates[state].mCounter); 180 } 181 sb.append("]"); 182 if (mLastUpdateTimestampMs >= 0) { 183 sb.append(" updated: ").append(mLastUpdateTimestampMs); 184 } 185 if (mLastStateChangeTimestampMs >= 0) { 186 sb.append(" currentState: ").append(mCurrentState); 187 if (mLastStateChangeTimestampMs > mLastUpdateTimestampMs) { 188 sb.append(" stateChanged: ").append(mLastStateChangeTimestampMs); 189 } 190 } else { 191 sb.append(" currentState: none"); 192 } 193 return sb.toString(); 194 } 195 } 196 197 private static final HashMap<Long, LongMultiStateCounterRavenwood> sInstances = 198 new HashMap<>(); 199 private static long sNextId = 1; 200 native_init(int stateCount)201 public static long native_init(int stateCount) { 202 LongMultiStateCounterRavenwood instance = new LongMultiStateCounterRavenwood(stateCount); 203 long instanceId = sNextId++; 204 sInstances.put(instanceId, instance); 205 return instanceId; 206 } 207 getInstance(long instanceId)208 private static LongMultiStateCounterRavenwood getInstance(long instanceId) { 209 return sInstances.get(instanceId); 210 } 211 native_setEnabled(long instanceId, boolean enabled, long timestampMs)212 public static void native_setEnabled(long instanceId, boolean enabled, 213 long timestampMs) { 214 getInstance(instanceId).setEnabled(enabled, timestampMs); 215 } 216 native_getStateCount(long instanceId)217 public static int native_getStateCount(long instanceId) { 218 return getInstance(instanceId).mStateCount; 219 } 220 native_updateValue(long instanceId, long value, long timestampMs)221 public static long native_updateValue(long instanceId, long value, long timestampMs) { 222 return getInstance(instanceId).updateValue(value, timestampMs); 223 } 224 native_setState(long instanceId, int state, long timestampMs)225 public static void native_setState(long instanceId, int state, long timestampMs) { 226 getInstance(instanceId).setState(state, timestampMs); 227 } 228 native_incrementValue(long instanceId, long count, long timestampMs)229 public static void native_incrementValue(long instanceId, long count, long timestampMs) { 230 getInstance(instanceId).incrementValue(count, timestampMs); 231 } 232 native_getCount(long instanceId, int state)233 public static long native_getCount(long instanceId, int state) { 234 return getInstance(instanceId).getValue(state); 235 } 236 native_reset(long instanceId)237 public static void native_reset(long instanceId) { 238 getInstance(instanceId).reset(); 239 } 240 native_writeToParcel(long instanceId, Parcel parcel, int flags)241 public static void native_writeToParcel(long instanceId, Parcel parcel, int flags) { 242 getInstance(instanceId).writeToParcel(parcel); 243 } 244 native_initFromParcel(Parcel parcel)245 public static long native_initFromParcel(Parcel parcel) { 246 int stateCount = parcel.readInt(); 247 if (stateCount < 0 || stateCount > 0xEFFF) { 248 throw new BadParcelableException("stateCount out of range"); 249 } 250 // LongMultiStateCounter.cpp uses AParcel, which throws on out-of-data. 251 if (parcel.dataPosition() >= parcel.dataSize()) { 252 throw new RuntimeException("Bad parcel"); 253 } 254 long instanceId = native_init(stateCount); 255 getInstance(instanceId).initFromParcel(parcel); 256 if (parcel.dataPosition() > parcel.dataSize()) { 257 throw new RuntimeException("Bad parcel"); 258 } 259 return instanceId; 260 } 261 native_toString(long instanceId)262 public static String native_toString(long instanceId) { 263 return getInstance(instanceId).toString(); 264 } 265 } 266