diff options
4 files changed, 119 insertions, 10 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 2bad670617b9..143983ad5d11 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1655,6 +1655,8 @@ public abstract class BatteryStats { public abstract long[] getCpuFreqs(); public final static class HistoryTag { + public static final int HISTORY_TAG_POOL_OVERFLOW = -1; + public String string; public int uid; @@ -6786,10 +6788,11 @@ public abstract class BatteryStats { if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) { didWake = true; sb.append("="); - if (longNames) { + if (longNames + || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { UserHandle.formatUid(sb, wakelockTag.uid); sb.append(":\""); - sb.append(wakelockTag.string); + sb.append(wakelockTag.string.replace("\"", "\"\"")); sb.append("\""); } else { sb.append(wakelockTag.poolIdx); @@ -6809,7 +6812,7 @@ public abstract class BatteryStats { } if (!didWake && wakelockTag != null) { sb.append(longNames ? " wake_lock=" : ",w="); - if (longNames) { + if (longNames || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { UserHandle.formatUid(sb, wakelockTag.uid); sb.append(":\""); sb.append(wakelockTag.string); @@ -7070,7 +7073,14 @@ public abstract class BatteryStats { if (rec.wakeReasonTag != null) { if (checkin) { item.append(",wr="); - item.append(rec.wakeReasonTag.poolIdx); + if (rec.wakeReasonTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + item.append(sUidToString.applyAsString(rec.wakeReasonTag.uid)); + item.append(":\""); + item.append(rec.wakeReasonTag.string.replace("\"", "\"\"")); + item.append("\""); + } else { + item.append(rec.wakeReasonTag.poolIdx); + } } else { item.append(" wake_reason="); item.append(rec.wakeReasonTag.uid); @@ -7098,7 +7108,15 @@ public abstract class BatteryStats { } item.append("="); if (checkin) { - item.append(rec.eventTag.poolIdx); + if (rec.eventTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + item.append(HISTORY_EVENT_INT_FORMATTERS[idx] + .applyAsString(rec.eventTag.uid)); + item.append(":\""); + item.append(rec.eventTag.string.replace("\"", "\"\"")); + item.append("\""); + } else { + item.append(rec.eventTag.poolIdx); + } } else { item.append(HISTORY_EVENT_INT_FORMATTERS[idx] .applyAsString(rec.eventTag.uid)); diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index bbaaa472cbbb..a823c29b3f6e 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -178,7 +178,7 @@ public class BatteryStatsHistory { private boolean mHaveBatteryLevel; private boolean mRecordingHistory; - private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe; + static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe; private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024; private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>(); @@ -1848,6 +1848,7 @@ public class BatteryStatsHistory { } return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; } else { + tag.poolIdx = HistoryTag.HISTORY_TAG_POOL_OVERFLOW; // Tag pool overflow: include the tag itself in the parcel return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; } diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java index ccc3454624f8..4c2b2854df88 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -309,7 +309,11 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag(); tag.readFromParcel(src); tag.poolIdx = index & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; - mHistoryTags.put(tag.poolIdx, tag); + if (tag.poolIdx < BatteryStatsHistory.HISTORY_TAG_INDEX_LIMIT) { + mHistoryTags.put(tag.poolIdx, tag); + } else { + tag.poolIdx = BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW; + } outTag.setTo(tag); } else { diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index 1ba14623f04e..a2dc5d8cd4cc 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -37,7 +37,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BatteryStatsHistory; import com.android.internal.os.BatteryStatsHistoryIterator; -import com.android.internal.os.Clock; import org.junit.Before; import org.junit.Test; @@ -64,8 +63,9 @@ public class BatteryStatsHistoryTest { private final Parcel mHistoryBuffer = Parcel.obtain(); private File mSystemDir; private File mHistoryDir; - private final Clock mClock = new MockClock(); + private final MockClock mClock = new MockClock(); private BatteryStatsHistory mHistory; + private BatteryStats.HistoryPrinter mHistoryPrinter; @Mock private BatteryStatsHistory.TraceDelegate mTracer; @Mock @@ -89,6 +89,8 @@ public class BatteryStatsHistoryTest { when(mStepDetailsCalculator.getHistoryStepDetails()) .thenReturn(new BatteryStats.HistoryStepDetails()); + + mHistoryPrinter = new BatteryStats.HistoryPrinter(); } @Test @@ -366,11 +368,95 @@ public class BatteryStatsHistoryTest { assertThat(checkin).contains("XC,10321,400,500,600"); } + @Test + public void largeTagPool() { + // Keep the preserved part of history short - we only need to capture the very tail of + // history. + mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 1, 6000, + mStepDetailsCalculator, mClock, mTracer); + + mHistory.forceRecordAllHistory(); + + mClock.realtime = 2_000_000; + mClock.uptime = 1_000_000; + // More than 32k strings + final int tagCount = 0x7FFF + 20; + for (int tag = 0; tag < tagCount;) { + mClock.realtime += 10; + mClock.uptime += 10; + mHistory.recordEvent(mClock.realtime, mClock.uptime, HistoryItem.EVENT_ALARM_START, + "a" + (tag++), 42); + + mHistory.setBatteryState(true, BatteryManager.BATTERY_STATUS_CHARGING, tag % 50, 0); + mClock.realtime += 10; + mClock.uptime += 10; + mHistory.recordWakelockStartEvent(mClock.realtime, mClock.uptime, "w" + tag, 42); + mClock.realtime += 10; + mClock.uptime += 10; + mHistory.recordWakelockStopEvent(mClock.realtime, mClock.uptime, "w" + tag, 42); + tag++; + + mHistory.recordWakeupEvent(mClock.realtime, mClock.uptime, "wr" + (tag++)); + } + + int eventTagsPooled = 0; + int eventTagsUnpooled = 0; + int wakelockTagsPooled = 0; + int wakelockTagsUnpooled = 0; + int wakeReasonTagsPooled = 0; + int wakeReasonTagsUnpooled = 0; + for (BatteryStatsHistoryIterator iterator = mHistory.iterate(); iterator.hasNext(); ) { + HistoryItem item = iterator.next(); + if (item.cmd != HistoryItem.CMD_UPDATE) { + continue; + } + String checkinDump = toString(item, true); + if (item.eventCode == HistoryItem.EVENT_ALARM_START) { + if (item.eventTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + eventTagsPooled++; + assertThat(checkinDump).contains("+Eal=" + item.eventTag.poolIdx); + } else { + eventTagsUnpooled++; + assertThat(checkinDump).contains("+Eal=42:\"" + item.eventTag.string + "\""); + } + } + + if (item.wakelockTag != null) { + if (item.wakelockTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + wakelockTagsPooled++; + assertThat(checkinDump).contains("w=" + item.wakelockTag.poolIdx); + } else { + wakelockTagsUnpooled++; + assertThat(checkinDump).contains("w=42:\"" + item.wakelockTag.string + "\""); + } + } + + if (item.wakeReasonTag != null) { + if (item.wakeReasonTag.poolIdx + != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + wakeReasonTagsPooled++; + assertThat(checkinDump).contains("wr=" + item.wakeReasonTag.poolIdx); + } else { + wakeReasonTagsUnpooled++; + assertThat(checkinDump).contains("wr=0:\"" + item.wakeReasonTag.string + "\""); + } + } + } + + // Self-check - ensure that we have all cases represented in the test + assertThat(eventTagsPooled).isGreaterThan(0); + assertThat(eventTagsUnpooled).isGreaterThan(0); + assertThat(wakelockTagsPooled).isGreaterThan(0); + assertThat(wakelockTagsUnpooled).isGreaterThan(0); + assertThat(wakeReasonTagsPooled).isGreaterThan(0); + assertThat(wakeReasonTagsUnpooled).isGreaterThan(0); + } + private String toString(BatteryStats.HistoryItem item, boolean checkin) { BatteryStats.HistoryPrinter printer = new BatteryStats.HistoryPrinter(); StringWriter writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); - printer.printNextItem(pw, item, 0, checkin, /* verbose */ true); + mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ false); pw.flush(); return writer.toString(); } |