Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a050bbd6 authored by Santos Cordon's avatar Santos Cordon
Browse files

Improvements to wakelock log

- Separate logs for Full and Partial wakelocks
- Reduce overall size of logs
- Use worksource UID instead of owner Uid
- Improve some logging output

Bug: 353490612
Bug: 364203600
Flag: EXEMPT bugfix
Test: atest WakeLockLogTest NotifierTest
Change-Id: I0bb8465412f2dea320801f267dc6bb2d1886b015
parent 82da37e4
Loading
Loading
Loading
Loading
+39 −13
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.power;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -36,8 +37,8 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -67,6 +68,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
@@ -147,7 +149,8 @@ public class Notifier {
    @Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
    private final TrustManager mTrustManager;
    private final Vibrator mVibrator;
    private final WakeLockLog mWakeLockLog;
    @NonNull private final WakeLockLog mPartialWakeLockLog;
    @NonNull private final WakeLockLog mFullWakeLockLog;
    private final DisplayManagerInternal mDisplayManagerInternal;

    private final NotifierHandler mHandler;
@@ -250,7 +253,9 @@ public class Notifier {
        mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
                com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);

        mWakeLockLog = mInjector.getWakeLockLog(context);
        mFullWakeLockLog = mInjector.getWakeLockLog(context);
        mPartialWakeLockLog = mInjector.getWakeLockLog(context);

        // Initialize interactive state for battery stats.
        try {
            mBatteryStats.noteInteractive(true);
@@ -324,7 +329,8 @@ public class Notifier {
                    // Ignore
                }
            }
            mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
            getWakeLockLog(flags).onWakeLockAcquired(tag,
                    getUidForWakeLockLog(ownerUid, workSource), flags, /*eventTime=*/ -1);
        }
        mWakefulnessSessionObserver.onWakeLockAcquired(flags);
    }
@@ -473,7 +479,8 @@ public class Notifier {
                    // Ignore
                }
            }
            mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
            getWakeLockLog(flags).onWakeLockReleased(tag,
                    getUidForWakeLockLog(ownerUid, workSource), /*eventTime=*/ -1);
        }
        mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
    }
@@ -960,11 +967,18 @@ public class Notifier {
     * @param pw The stream to print to.
     */
    public void dump(PrintWriter pw) {
        if (mWakeLockLog != null) {
            mWakeLockLog.dump(pw);
        }
        pw.println("Notifier:");

        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
        ipw.println("Partial Wakelock Log:");
        mPartialWakeLockLog.dump(ipw);

        ipw.println("");
        ipw.println("Full Wakelock Log:");
        mFullWakeLockLog.dump(ipw);

        mWakefulnessSessionObserver.dump(pw);
        ipw.println("");
        mWakefulnessSessionObserver.dump(ipw);
    }

    private void updatePendingBroadcastLocked() {
@@ -1232,7 +1246,9 @@ public class Notifier {
                // Do Nothing
            }
        }
        mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);

        getWakeLockLog(flags).onWakeLockAcquired(tag, getUidForWakeLockLog(ownerUid, workSource),
                flags, currentTime);
    }

    @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1253,7 +1269,8 @@ public class Notifier {
                // Ignore
            }
        }
        mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
        getWakeLockLog(flags).onWakeLockReleased(tag, getUidForWakeLockLog(ownerUid, workSource),
                currentTime);
    }

    @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1419,6 +1436,15 @@ public class Notifier {
        }
    }

    private @NonNull WakeLockLog getWakeLockLog(int flags) {
        return PowerManagerService.isScreenLock(flags) ? mFullWakeLockLog : mPartialWakeLockLog;
    }

    private int getUidForWakeLockLog(int ownerUid, WorkSource workSource) {
        int attributionUid = workSource != null ? workSource.getAttributionUid() : -1;
        return attributionUid != -1 ? attributionUid : ownerUid;
    }

    private final class NotifierHandler extends Handler {

        public NotifierHandler(Looper looper) {
@@ -1501,7 +1527,7 @@ public class Notifier {
        /**
         * Gets the WakeLockLog object
         */
        WakeLockLog getWakeLockLog(Context context);
        @NonNull WakeLockLog getWakeLockLog(Context context);

        /**
         * Gets the AppOpsManager system service
@@ -1522,7 +1548,7 @@ public class Notifier {
        }

        @Override
        public WakeLockLog getWakeLockLog(Context context) {
        public @NonNull WakeLockLog getWakeLockLog(Context context) {
            return new WakeLockLog(context);
        }

+9 −2
Original line number Diff line number Diff line
@@ -1723,9 +1723,16 @@ public final class PowerManagerService extends SystemService
        }
    }

    @SuppressWarnings("deprecation")
    private static boolean isScreenLock(final WakeLock wakeLock) {
        switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
        return isScreenLock(wakeLock.mFlags);
    }

    /**
     * Returns if a wakelock flag corresponds to a screen wake lock.
     */
    @SuppressWarnings("deprecation")
    public static boolean isScreenLock(int flags) {
        switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
            case PowerManager.FULL_WAKE_LOCK:
            case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
            case PowerManager.SCREEN_DIM_WAKE_LOCK:
+31 −11
Original line number Diff line number Diff line
@@ -81,11 +81,12 @@ final class WakeLockLog {
    private static final int TYPE_ACQUIRE = 0x1;
    private static final int TYPE_RELEASE = 0x2;
    private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
    private static final int LOG_SIZE = 1024 * 10;
    private static final int LOG_SIZE = 1024 * 3;
    private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;

    private static final int TAG_DATABASE_SIZE = 128;
    private static final int TAG_DATABASE_SIZE = 64;
    private static final int TAG_DATABASE_SIZE_MAX = 128;
    private static final int TAG_DATABASE_STARTING_SIZE = 16;

    private static final int LEVEL_SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK = 0;
    private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
@@ -182,7 +183,7 @@ final class WakeLockLog {
     * @param pw The {@code PrintWriter} to write to.
     */
    public void dump(PrintWriter pw) {
        dump(pw, false);
        dump(pw, /* includeTagDb= */ true);
    }

    @VisibleForTesting
@@ -1161,15 +1162,16 @@ final class WakeLockLog {
     */
    static class TagDatabase {
        private final int mInvalidIndex;
        private final TagData[] mArray;
        private final int mMaxArraySize;
        private TagData[] mArray;
        private Callback mCallback;

        TagDatabase(Injector injector) {
            int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);

            // Largest possible index used as "INVALID", hence the (size - 1) sizing.
            mArray = new TagData[size - 1];
            mInvalidIndex = size - 1;
            // Largest possible index used as "INVALID", hence the (size - 1) sizing
            mMaxArraySize = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX - 1);
            int startingSize = Math.min(mMaxArraySize, injector.getTagDatabaseStartingSize());
            mArray = new TagData[startingSize];
            mInvalidIndex = mMaxArraySize;
        }

        @Override
@@ -1195,8 +1197,10 @@ final class WakeLockLog {
            sb.append(", entries: ").append(entries);
            sb.append(", Bytes used: ").append(byteEstimate);
            if (DEBUG) {
                sb.append(", Avg tag size: ").append(tagSize / tags);
                sb.append("\n    ").append(Arrays.toString(mArray));
                sb.append(", Avg tag size: ").append(tags == 0 ? 0 : (tagSize / tags));
                for (int i = 0; i < mArray.length; i++) {
                    sb.append("\n  [").append(i).append("] ").append(mArray[i]);
                }
            }
            return sb.toString();
        }
@@ -1284,6 +1288,18 @@ final class WakeLockLog {
                return null;
            }

            // We don't have a spot available, see if we can still increase the array size
            if (firstAvailable == -1) {
                if (mArray.length < mMaxArraySize) {
                    int oldSize = mArray.length;
                    int newSize = Math.min(oldSize * 2, mMaxArraySize);
                    TagData[] newArray = new TagData[newSize];
                    System.arraycopy(mArray, 0, newArray, 0, oldSize);
                    mArray = newArray;
                    firstAvailable = oldSize;
                }
            }

            // If we need to remove an index, report to listeners that we are removing an index.
            boolean useOldest = firstAvailable == -1;
            if (useOldest && mCallback != null) {
@@ -1402,6 +1418,10 @@ final class WakeLockLog {
            return TAG_DATABASE_SIZE;
        }

        public int getTagDatabaseStartingSize() {
            return TAG_DATABASE_STARTING_SIZE;
        }

        public int getLogSize() {
            return LOG_SIZE;
        }
+64 −5
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import android.annotation.NonNull;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.content.Context;
@@ -53,8 +54,8 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -689,6 +690,7 @@ public class NotifierTest {

        final int uid = 1234;
        final int pid = 5678;

        mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
                exceptingCallback);
@@ -753,6 +755,55 @@ public class NotifierTest {
        verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
    }

    @Test
    public void test_wakeLockLogUsesWorkSource() {
        createNotifier();
        clearInvocations(mWakeLockLog);
        IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
            @Override public void onStateChanged(boolean enabled) throws RemoteException {
                throw new RemoteException("Just testing");
            }
        };

        final int uid = 1234;
        final int pid = 5678;
        WorkSource worksource = new WorkSource(1212);
        WorkSource worksource2 = new WorkSource(3131);

        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                "my.package.name", uid, pid, worksource, /* historyTag= */ null,
                exceptingCallback);
        verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
                PowerManager.PARTIAL_WAKE_LOCK, -1);

        // Release the wakelock
        mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
                "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
                exceptingCallback);
        verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, -1);

        // clear the handler
        mTestLooper.dispatchAll();

        // Now test with improveWakelockLatency flag true
        clearInvocations(mWakeLockLog);
        when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);

        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                "my.package.name", uid, pid, worksource, /* historyTag= */ null,
                exceptingCallback);
        mTestLooper.dispatchAll();
        verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
                PowerManager.PARTIAL_WAKE_LOCK, 1);

        // Release the wakelock
        mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
                "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
                exceptingCallback);
        mTestLooper.dispatchAll();
        verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, 1);
    }

    @Test
    public void
            test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
@@ -907,16 +958,24 @@ public class NotifierTest {
                PowerManager.DRAW_WAKE_LOCK);
        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
                PowerManager.PARTIAL_WAKE_LOCK);
        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                        PowerManager.DOZE_WAKE_LOCK), -1);
    }

    @Test
    public void getWakelockMonitorTypeForLogging_evaluateProximityLevel() {
        // How proximity wakelock is evaluated depends on boolean configuration. Test both.
        when(mResourcesSpy.getBoolean(
                com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
                .thenReturn(false);
        createNotifier();
        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                        PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
                PowerManager.PARTIAL_WAKE_LOCK);
        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                        PowerManager.DOZE_WAKE_LOCK), -1);

        when(mResourcesSpy.getBoolean(
                com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
                .thenReturn(true);

        createNotifier();
        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                        PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
@@ -1203,7 +1262,7 @@ public class NotifierTest {
                    }

                    @Override
                    public WakeLockLog getWakeLockLog(Context context) {
                    public @NonNull WakeLockLog getWakeLockLog(Context context) {
                        return mWakeLockLog;
                    }

+68 −17
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.os.Process;

import com.android.server.power.WakeLockLog.TagData;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -62,8 +64,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddTwoItems_withNoEventTimeSupplied() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
        when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
        log.onWakeLockAcquired("TagPartial", 101,
@@ -93,8 +96,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddTwoItems() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("TagPartial", 101,
@@ -117,8 +121,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddTwoItemsWithTimeReset() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -136,9 +141,10 @@ public class WakeLockLogTest {

    @Test
    public void testAddTwoItemsWithTagOverwrite() {
        final int tagDatabaseSize = 2;
        final int tagDatabaseSize = 1;
        final int tagStartingSize = 1;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -157,8 +163,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddFourItemsWithRingBufferOverflow() {
        final int tagDatabaseSize = 6;
        final int tagStartingSize = 2;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        // Wake lock 1 acquired - log size = 3
@@ -206,8 +213,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddItemWithBadTag() {
        final int tagDatabaseSize = 6;
        final int tagStartingSize = 2;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        // Bad tag means it wont get written
@@ -224,8 +232,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddItemWithReducedTagName() {
        final int tagDatabaseSize = 6;
        final int tagStartingSize = 2;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
@@ -242,9 +251,10 @@ public class WakeLockLogTest {

    @Test
    public void testAddAcquireAndReleaseWithRepeatTagName() {
        final int tagDatabaseSize = 6;
        final int tagDatabaseSize = 5;
        final int tagStartingSize = 5;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -263,8 +273,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddAcquireAndReleaseWithTimeTravel() {
        final int tagDatabaseSize = 6;
        final int tagStartingSize = 2;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
@@ -283,8 +294,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddSystemWakelock() {
        final int tagDatabaseSize = 6;
        final int tagStartingSize = 2;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("TagPartial", 101,
@@ -302,8 +314,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddItemWithNoPackageName() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
@@ -322,8 +335,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddItemWithMultiplePackageNames() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        when(mPackageManager.getPackagesForUid(101)).thenReturn(
@@ -344,8 +358,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddItemsWithRepeatOwnerUid_UsesCache() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 20;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("TagPartial", 101,
@@ -375,8 +390,9 @@ public class WakeLockLogTest {
    @Test
    public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
        final int tagDatabaseSize = 128;
        final int tagStartingSize = 16;
        final int logSize = 10;
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);

        log.onWakeLockAcquired("TagPartial", 101,
@@ -420,6 +436,34 @@ public class WakeLockLogTest {
        verify(mPackageManager, times(1)).getPackagesForUid(101);
    }

    @Test
    public void testTagDatabaseGrowsBeyondStartingSize() {
        final int tagDatabaseSize = 3;
        final int tagStartingSize = 1;
        final int logSize = 10;
        // start with size = 1 and max size
        TestInjector injector = new TestInjector(tagDatabaseSize, tagStartingSize, logSize);
        WakeLockLog.TagDatabase td = new WakeLockLog.TagDatabase(injector);

        // Add one
        TagData data1 = td.findOrCreateTag("Tagname1", 1001, /* shouldCreate= */ true);
        assertEquals(0, td.getTagIndex(data1));

        // Check that it grows by adding 1 more
        TagData data2 = td.findOrCreateTag("Tagname2", 1001, /* shouldCreate= */ true);
        assertEquals(1, td.getTagIndex(data2));

        // Lets add the last one to fill up the DB to maxSize
        TagData data3 = td.findOrCreateTag("Tagname3", 1001, /* shouldCreate= */ true);
        assertEquals(2, td.getTagIndex(data3));

        // Adding a fourth one should replace the oldest one (Tagname1)
        TagData data4 = td.findOrCreateTag("Tagname4", 1001, /* shouldCreate= */ true);
        assertEquals(0, td.getTagIndex(data4));
        assertEquals(tagDatabaseSize, td.getTagIndex(data1));

    }

    private String dumpLog(WakeLockLog log, boolean includeTagDb) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
@@ -429,10 +473,12 @@ public class WakeLockLogTest {

    public static class TestInjector extends WakeLockLog.Injector {
        private final int mTagDatabaseSize;
        private final int mTagStartingSize;
        private final int mLogSize;

        public TestInjector(int tagDatabaseSize, int logSize) {
        public TestInjector(int tagDatabaseSize, int tagStartingSize, int logSize) {
            mTagDatabaseSize = tagDatabaseSize;
            mTagStartingSize = tagStartingSize;
            mLogSize = logSize;
        }

@@ -441,6 +487,11 @@ public class WakeLockLogTest {
            return mTagDatabaseSize;
        }

        @Override
        public int getTagDatabaseStartingSize() {
            return mTagStartingSize;
        }

        @Override
        public int getLogSize() {
            return mLogSize;