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 package com.android.server.power.stats; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertTrue; 21 22 import android.os.nano.OsProtoEnums; 23 24 import androidx.test.filters.SmallTest; 25 import androidx.test.runner.AndroidJUnit4; 26 27 import org.junit.Before; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 31 import java.util.ArrayList; 32 33 @RunWith(AndroidJUnit4.class) 34 @SmallTest 35 public class WakelockStatsFrameworkEventsTest { 36 private WakelockStatsFrameworkEvents mEvents; 37 38 @Before setup()39 public void setup() { 40 mEvents = new WakelockStatsFrameworkEvents(); 41 } 42 43 private static final int UID_1 = 1; 44 private static final int UID_2 = 2; 45 46 private static final String TAG_1 = "TAG1"; 47 private static final String TAG_2 = "TAG2"; 48 49 private static final int WAKELOCK_TYPE_1 = OsProtoEnums.PARTIAL_WAKE_LOCK; 50 private static final int WAKELOCK_TYPE_2 = OsProtoEnums.DOZE_WAKE_LOCK; 51 52 private static final long TS_1 = 1000; 53 private static final long TS_2 = 2000; 54 private static final long TS_3 = 3000; 55 private static final long TS_4 = 4000; 56 private static final long TS_5 = 5000; 57 58 // Mirrors com.android.os.framework.FrameworkWakelockInfo proto. 59 private static class WakelockInfo { 60 public int uid; 61 public String tag; 62 public int type; 63 public long uptimeMillis; 64 public long completedCount; 65 WakelockInfo(int uid, String tag, int type, long uptimeMillis, long completedCount)66 WakelockInfo(int uid, String tag, int type, long uptimeMillis, long completedCount) { 67 this.uid = uid; 68 this.tag = tag; 69 this.type = type; 70 this.uptimeMillis = uptimeMillis; 71 this.completedCount = completedCount; 72 } 73 } 74 75 // Assumes that mEvents is empty. 76 @SuppressWarnings("GuardedBy") makeMetricsAlmostOverflow()77 private void makeMetricsAlmostOverflow() throws Exception { 78 for (int i = 0; i < mEvents.SUMMARY_THRESHOLD - 1; i++) { 79 String tag = "forceOverflow" + i; 80 mEvents.noteStartWakeLock(UID_1, tag, WAKELOCK_TYPE_1, TS_1); 81 mEvents.noteStopWakeLock(UID_1, tag, WAKELOCK_TYPE_1, TS_2); 82 } 83 84 assertFalse("not overflow", mEvents.inOverflow()); 85 ArrayList<WakelockInfo> info = pullResults(TS_4); 86 WakelockInfo notOverflowInfo = 87 info.stream() 88 .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG)) 89 .findFirst() 90 .orElse(null); 91 92 assertEquals("not overflow", notOverflowInfo, null); 93 94 // Add one more to hit an overflow state. 95 String lastTag = "forceOverflowLast"; 96 mEvents.noteStartWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2, TS_1); 97 mEvents.noteStopWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2, TS_2); 98 99 assertTrue("overflow", mEvents.inOverflow()); 100 info = pullResults(TS_4); 101 102 WakelockInfo tag1Info = 103 info.stream().filter(i -> i.tag.equals(lastTag)).findFirst().orElse(null); 104 105 assertTrue("lastTag found", tag1Info != null); 106 assertEquals("uid", UID_1, tag1Info.uid); 107 assertEquals("tag", lastTag, tag1Info.tag); 108 assertEquals("type", WAKELOCK_TYPE_2, tag1Info.type); 109 assertEquals("duration", TS_2 - TS_1, tag1Info.uptimeMillis); 110 assertEquals("count", 1, tag1Info.completedCount); 111 } 112 113 // Assumes that mEvents is empty. 114 @SuppressWarnings("GuardedBy") makeMetricsAlmostHardCap()115 private void makeMetricsAlmostHardCap() throws Exception { 116 for (int i = 0; i < mEvents.MAX_WAKELOCK_DIMENSIONS - 1; i++) { 117 mEvents.noteStartWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1, TS_1); 118 mEvents.noteStopWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1, TS_2); 119 } 120 121 assertFalse("not hard capped", mEvents.inHardCap()); 122 ArrayList<WakelockInfo> info = pullResults(TS_4); 123 WakelockInfo notOverflowInfo = 124 info.stream() 125 .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG)) 126 .findFirst() 127 .orElse(null); 128 129 assertEquals("not overflow", notOverflowInfo, null); 130 131 // Add one more to hit an hardcap state. 132 int hardCapUid = mEvents.MAX_WAKELOCK_DIMENSIONS; 133 mEvents.noteStartWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2, TS_1); 134 mEvents.noteStopWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2, TS_2); 135 136 assertTrue("hard capped", mEvents.inHardCap()); 137 info = pullResults(TS_4); 138 139 WakelockInfo tag2Info = 140 info.stream().filter(i -> i.uid == hardCapUid).findFirst().orElse(null); 141 142 assertTrue("hardCapUid found", tag2Info != null); 143 assertEquals("uid", hardCapUid, tag2Info.uid); 144 assertEquals("tag", mEvents.OVERFLOW_TAG, tag2Info.tag); 145 assertEquals("type", mEvents.OVERFLOW_LEVEL, tag2Info.type); 146 assertEquals("duration", TS_2 - TS_1, tag2Info.uptimeMillis); 147 assertEquals("count", 1, tag2Info.completedCount); 148 } 149 pullResults(long timestamp)150 private ArrayList<WakelockInfo> pullResults(long timestamp) { 151 ArrayList<WakelockInfo> results = new ArrayList<>(); 152 WakelockStatsFrameworkEvents.EventLogger logger = 153 new WakelockStatsFrameworkEvents.EventLogger() { 154 public void logResult( 155 int uid, 156 String tag, 157 int wakeLockLevel, 158 long uptimeMillis, 159 long completedCount) { 160 WakelockInfo info = 161 new WakelockInfo( 162 uid, tag, wakeLockLevel, uptimeMillis, completedCount); 163 results.add(info); 164 } 165 }; 166 mEvents.pullFrameworkWakelockInfoAtoms(timestamp, logger); 167 return results; 168 } 169 170 @Test singleWakelock()171 public void singleWakelock() { 172 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 173 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2); 174 175 ArrayList<WakelockInfo> info = pullResults(TS_3); 176 177 assertEquals("size", 1, info.size()); 178 assertEquals("uid", UID_1, info.get(0).uid); 179 assertEquals("tag", TAG_1, info.get(0).tag); 180 assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); 181 assertEquals("duration", TS_2 - TS_1, info.get(0).uptimeMillis); 182 assertEquals("count", 1, info.get(0).completedCount); 183 } 184 185 @Test wakelockOpen()186 public void wakelockOpen() throws Exception { 187 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 188 189 ArrayList<WakelockInfo> info = pullResults(TS_3); 190 191 assertEquals("size", 1, info.size()); 192 assertEquals("uid", UID_1, info.get(0).uid); 193 assertEquals("tag", TAG_1, info.get(0).tag); 194 assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); 195 assertEquals("duration", TS_3 - TS_1, info.get(0).uptimeMillis); 196 assertEquals("count", 0, info.get(0).completedCount); 197 } 198 199 @Test wakelockOpenOverlap()200 public void wakelockOpenOverlap() throws Exception { 201 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 202 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2); 203 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_3); 204 205 ArrayList<WakelockInfo> info = pullResults(TS_4); 206 207 assertEquals("size", 1, info.size()); 208 assertEquals("uid", UID_1, info.get(0).uid); 209 assertEquals("tag", TAG_1, info.get(0).tag); 210 assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); 211 assertEquals("duration", TS_4 - TS_1, info.get(0).uptimeMillis); 212 assertEquals("count", 0, info.get(0).completedCount); 213 } 214 215 @Test testOverflow()216 public void testOverflow() throws Exception { 217 makeMetricsAlmostOverflow(); 218 219 // This one gets tagged as an overflow. 220 mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); 221 mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_2); 222 223 ArrayList<WakelockInfo> info = pullResults(TS_4); 224 WakelockInfo overflowInfo = 225 info.stream() 226 .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG)) 227 .findFirst() 228 .orElse(null); 229 230 assertEquals("uid", UID_1, overflowInfo.uid); 231 assertEquals("type", mEvents.OVERFLOW_LEVEL, overflowInfo.type); 232 assertEquals("duration", TS_2 - TS_1, overflowInfo.uptimeMillis); 233 assertEquals("count", 1, overflowInfo.completedCount); 234 } 235 236 @Test testOverflowOpen()237 public void testOverflowOpen() throws Exception { 238 makeMetricsAlmostOverflow(); 239 240 // This is the open wakelock that overflows. 241 mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); 242 243 ArrayList<WakelockInfo> info = pullResults(TS_4); 244 WakelockInfo overflowInfo = 245 info.stream() 246 .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG)) 247 .findFirst() 248 .orElse(null); 249 250 assertEquals("uid", UID_1, overflowInfo.uid); 251 assertEquals("type", mEvents.OVERFLOW_LEVEL, overflowInfo.type); 252 assertEquals("duration", (TS_4 - TS_1), overflowInfo.uptimeMillis); 253 assertEquals("count", 0, overflowInfo.completedCount); 254 } 255 256 @Test testHardCap()257 public void testHardCap() throws Exception { 258 makeMetricsAlmostHardCap(); 259 260 // This one gets tagged as a hard cap. 261 mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); 262 mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_2); 263 264 ArrayList<WakelockInfo> info = pullResults(TS_4); 265 WakelockInfo hardCapInfo = 266 info.stream() 267 .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG)) 268 .findFirst() 269 .orElse(null); 270 271 assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.uid); 272 assertEquals("type", mEvents.OVERFLOW_LEVEL, hardCapInfo.type); 273 assertEquals("duration", TS_2 - TS_1, hardCapInfo.uptimeMillis); 274 assertEquals("count", 1, hardCapInfo.completedCount); 275 } 276 277 @Test testHardCapOpen()278 public void testHardCapOpen() throws Exception { 279 makeMetricsAlmostHardCap(); 280 281 // This is the open wakelock that overflows. 282 mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); 283 284 ArrayList<WakelockInfo> info = pullResults(TS_4); 285 WakelockInfo hardCapInfo = 286 info.stream() 287 .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG)) 288 .findFirst() 289 .orElse(null); 290 291 assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.uid); 292 assertEquals("type", mEvents.OVERFLOW_LEVEL, hardCapInfo.type); 293 assertEquals("duration", (TS_4 - TS_1), hardCapInfo.uptimeMillis); 294 assertEquals("count", 0, hardCapInfo.completedCount); 295 } 296 297 @Test overlappingWakelocks()298 public void overlappingWakelocks() throws Exception { 299 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 300 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2); 301 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_3); 302 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); 303 304 ArrayList<WakelockInfo> info = pullResults(TS_5); 305 306 assertEquals("size", 1, info.size()); 307 assertEquals("uid", UID_1, info.get(0).uid); 308 assertEquals("tag", TAG_1, info.get(0).tag); 309 assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); 310 assertEquals("duration", TS_4 - TS_1, info.get(0).uptimeMillis); 311 assertEquals("count", 1, info.get(0).completedCount); 312 } 313 314 @Test diffUid()315 public void diffUid() throws Exception { 316 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 317 mEvents.noteStartWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1, TS_2); 318 mEvents.noteStopWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1, TS_3); 319 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); 320 321 ArrayList<WakelockInfo> info = pullResults(TS_5); 322 assertEquals("size", 2, info.size()); 323 324 WakelockInfo uid1Info = info.stream().filter(i -> i.uid == UID_1).findFirst().orElse(null); 325 326 assertTrue("UID_1 found", uid1Info != null); 327 assertEquals("uid", UID_1, uid1Info.uid); 328 assertEquals("tag", TAG_1, uid1Info.tag); 329 assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type); 330 assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis); 331 assertEquals("count", 1, uid1Info.completedCount); 332 333 WakelockInfo uid2Info = info.stream().filter(i -> i.uid == UID_2).findFirst().orElse(null); 334 assertTrue("UID_2 found", uid2Info != null); 335 assertEquals("uid", UID_2, uid2Info.uid); 336 assertEquals("tag", TAG_1, uid2Info.tag); 337 assertEquals("type", WAKELOCK_TYPE_1, uid2Info.type); 338 assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis); 339 assertEquals("count", 1, uid2Info.completedCount); 340 } 341 342 @Test diffTag()343 public void diffTag() throws Exception { 344 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 345 mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1, TS_2); 346 mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1, TS_3); 347 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); 348 349 ArrayList<WakelockInfo> info = pullResults(TS_5); 350 assertEquals("size", 2, info.size()); 351 352 WakelockInfo uid1Info = 353 info.stream().filter(i -> i.tag.equals(TAG_1)).findFirst().orElse(null); 354 355 assertTrue("TAG_1 found", uid1Info != null); 356 assertEquals("uid", UID_1, uid1Info.uid); 357 assertEquals("tag", TAG_1, uid1Info.tag); 358 assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type); 359 assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis); 360 assertEquals("count", 1, uid1Info.completedCount); 361 362 WakelockInfo uid2Info = 363 info.stream().filter(i -> i.tag.equals(TAG_2)).findFirst().orElse(null); 364 assertTrue("TAG_2 found", uid2Info != null); 365 assertEquals("uid", UID_1, uid2Info.uid); 366 assertEquals("tag", TAG_2, uid2Info.tag); 367 assertEquals("type", WAKELOCK_TYPE_1, uid2Info.type); 368 assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis); 369 assertEquals("count", 1, uid2Info.completedCount); 370 } 371 372 @Test diffType()373 public void diffType() throws Exception { 374 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); 375 mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2, TS_2); 376 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2, TS_3); 377 mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); 378 379 ArrayList<WakelockInfo> info = pullResults(TS_5); 380 assertEquals("size", 2, info.size()); 381 382 WakelockInfo uid1Info = 383 info.stream().filter(i -> i.type == WAKELOCK_TYPE_1).findFirst().orElse(null); 384 385 assertTrue("WAKELOCK_TYPE_1 found", uid1Info != null); 386 assertEquals("uid", UID_1, uid1Info.uid); 387 assertEquals("tag", TAG_1, uid1Info.tag); 388 assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type); 389 assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis); 390 assertEquals("count", 1, uid1Info.completedCount); 391 392 WakelockInfo uid2Info = 393 info.stream().filter(i -> i.type == WAKELOCK_TYPE_2).findFirst().orElse(null); 394 assertTrue("WAKELOCK_TYPE_2 found", uid2Info != null); 395 assertEquals("uid", UID_1, uid2Info.uid); 396 assertEquals("tag", TAG_1, uid2Info.tag); 397 assertEquals("type", WAKELOCK_TYPE_2, uid2Info.type); 398 assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis); 399 assertEquals("count", 1, uid2Info.completedCount); 400 } 401 } 402