Improve wake lock logging

- ACQ events should continue to be printed until their corresponding REL event is removed from the log
- the package name will be printed

Bug: 283254680
Test: atest WakeLockLogTest
Test: atest NotifierTest
Change-Id: Ibfdedc4213d6aa055aef40ba689fb466aef6498d
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index a694e31..3ecc985 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -222,7 +222,7 @@
         mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
 
-        mWakeLockLog = new WakeLockLog();
+        mWakeLockLog = new WakeLockLog(context);
 
         // Initialize interactive state for battery stats.
         try {
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index d20c7f1..d3486a4 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -16,20 +16,26 @@
 
 package com.android.server.power;
 
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.PowerManager;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.ConcurrentModificationException;
 import java.util.Date;
 import java.util.Iterator;
+import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 /**
  * Simple Log for wake lock events. Optimized to reduce memory usage.
@@ -117,7 +123,7 @@
     private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
     /**
-     * Lock protects WakeLockLock.dump (binder thread) from conflicting with changes to the log
+     * Lock protects WakeLockLog.dump (binder thread) from conflicting with changes to the log
      * happening on the background thread.
      */
     private final Object mLock = new Object();
@@ -126,18 +132,20 @@
     private final TheLog mLog;
     private final TagDatabase mTagDatabase;
     private final SimpleDateFormat mDumpsysDateFormat;
+    private final Context mContext;
 
-    WakeLockLog() {
-        this(new Injector());
+    WakeLockLog(Context context) {
+        this(new Injector(), context);
     }
 
     @VisibleForTesting
-    WakeLockLog(Injector injector) {
+    WakeLockLog(Injector injector, Context context) {
         mInjector = injector;
         mTagDatabase = new TagDatabase(injector);
         EntryByteTranslator translator = new EntryByteTranslator(mTagDatabase);
         mLog = new TheLog(injector, translator, mTagDatabase);
         mDumpsysDateFormat = injector.getDateFormat();
+        mContext = context;
     }
 
     /**
@@ -176,10 +184,24 @@
         try {
             synchronized (mLock) {
                 pw.println("Wake Lock Log");
-                LogEntry tempEntry = new LogEntry();  // Temporary entry for the iterator to reuse.
-                final Iterator<LogEntry> iterator = mLog.getAllItems(tempEntry);
                 int numEvents = 0;
                 int numResets = 0;
+                SparseArray<String[]> uidToPackagesCache = new SparseArray();
+
+                for (int i = 0; i < mLog.mSavedAcquisitions.size(); i++) {
+                    numEvents++;
+                    LogEntry entry = mLog.mSavedAcquisitions.get(i);
+
+                    entry.updatePackageName(uidToPackagesCache, mContext.getPackageManager());
+
+                    if (DEBUG) {
+                        pw.print("Saved acquisition no. " + i);
+                    }
+                    entry.dump(pw, mDumpsysDateFormat);
+                }
+
+                LogEntry tempEntry = new LogEntry();  // Temporary entry for the iterator to reuse.
+                final Iterator<LogEntry> iterator = mLog.getAllItems(tempEntry);
                 while (iterator.hasNext()) {
                     String address = null;
                     if (DEBUG) {
@@ -192,6 +214,8 @@
                             numResets++;
                         } else {
                             numEvents++;
+                            entry.updatePackageName(uidToPackagesCache,
+                                    mContext.getPackageManager());
                             if (DEBUG) {
                                 pw.print(address);
                             }
@@ -381,6 +405,11 @@
          */
         public int flags;
 
+        /**
+         * The name of the package that acquired the wake lock
+         */
+        public String packageName;
+
         LogEntry() {}
 
         LogEntry(long time, int type, TagData tag, int flags) {
@@ -438,8 +467,13 @@
             }
             sb.append(dateFormat.format(new Date(time)))
                     .append(" - ")
-                    .append(tag == null ? "---" : tag.ownerUid)
-                    .append(" - ")
+                    .append(tag == null ? "---" : tag.ownerUid);
+            if (packageName != null) {
+                sb.append(" (");
+                sb.append(packageName);
+                sb.append(")");
+            }
+            sb.append(" - ")
                     .append(type == TYPE_ACQUIRE ? "ACQ" : "REL")
                     .append(" ")
                     .append(tag == null ? "UNKNOWN" : tag.tag);
@@ -463,6 +497,36 @@
                 sb.append(",system-wakelock");
             }
         }
+
+        /**
+         * Update the package name using the cache if available or the package manager.
+         * @param uidToPackagesCache The cache of package names
+         * @param packageManager The package manager
+         */
+        public void updatePackageName(SparseArray<String[]> uidToPackagesCache,
+                PackageManager packageManager) {
+            if (tag == null) {
+                return;
+            }
+
+            String[] packages;
+            if (uidToPackagesCache.contains(tag.ownerUid)) {
+                packages = uidToPackagesCache.get(tag.ownerUid);
+            } else {
+                packages = packageManager.getPackagesForUid(tag.ownerUid);
+                uidToPackagesCache.put(tag.ownerUid, packages);
+            }
+
+            if (packages != null && packages.length > 0) {
+                packageName = packages[0];
+                if (packages.length > 1) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append(packageName)
+                            .append(",...");
+                    packageName = sb.toString();
+                }
+            }
+        }
     }
 
     /**
@@ -744,6 +808,12 @@
 
         private final TagDatabase mTagDatabase;
 
+        /**
+         * Wake lock acquisition events should continue to be printed until their corresponding
+         * release event is removed from the log.
+         */
+        private final List<LogEntry> mSavedAcquisitions;
+
         TheLog(Injector injector, EntryByteTranslator translator, TagDatabase tagDatabase) {
             final int logSize = Math.max(injector.getLogSize(), LOG_SIZE_MIN);
             mBuffer = new byte[logSize];
@@ -758,6 +828,8 @@
                     removeTagIndex(index);
                 }
             });
+
+            mSavedAcquisitions = new ArrayList();
         }
 
         /**
@@ -976,6 +1048,19 @@
 
             // Copy the contents of the start of the buffer to our temporary buffer.
             LogEntry entry = readEntryAt(mStart, mStartTime, null);
+            if (entry.type == TYPE_ACQUIRE) {
+                // We'll continue to print the event until the corresponding release event is also
+                // removed from the log.
+                mSavedAcquisitions.add(entry);
+            } else if (entry.type == TYPE_RELEASE) {
+                // We no longer need to print the corresponding acquire event.
+                for (int i = 0; i < mSavedAcquisitions.size(); i++) {
+                    if (Objects.equals(mSavedAcquisitions.get(i).tag, entry.tag)) {
+                        mSavedAcquisitions.remove(i);
+                        break;
+                    }
+                }
+            }
             if (DEBUG) {
                 Slog.d(TAG, "Removing oldest item at @ " + mStart + ", found: " + entry);
             }
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
index a73fcb8..7af4b3d 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
@@ -18,11 +18,18 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.PowerManager;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -34,12 +41,27 @@
  */
 public class WakeLockLogTest {
 
+    @Mock
+    private Context mContext;
+
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+        when(mPackageManager.getPackagesForUid(101)).thenReturn(new String[]{ "some.package1" });
+        when(mPackageManager.getPackagesForUid(102)).thenReturn(new String[]{ "some.package2" });
+    }
+
     @Test
     public void testAddTwoItems() {
         final int tagDatabaseSize = 128;
         final int logSize = 20;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101,
@@ -50,8 +72,10 @@
                 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
 
         assertEquals("Wake Lock Log\n"
-                + "  01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,on-after-release)\n"
-                + "  01-01 00:00:01.150 - 102 - ACQ TagFull (full,acq-causes-wake)\n"
+                + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+                        + "(partial,on-after-release)\n"
+                + "  01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull "
+                        + "(full,acq-causes-wake)\n"
                 + "  -\n"
                 + "  Events: 2, Time-Resets: 0\n"
                 + "  Buffer, Bytes used: 6\n",
@@ -63,7 +87,7 @@
         final int tagDatabaseSize = 128;
         final int logSize = 20;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -72,8 +96,8 @@
         log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
 
         assertEquals("Wake Lock Log\n"
-                + "  01-01 00:00:01.000 - 101 - ACQ TagPartial (partial)\n"
-                + "  01-01 00:00:01.350 - 102 - ACQ TagFull (full)\n"
+                + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial (partial)\n"
+                + "  01-01 00:00:01.350 - 102 (some.package2) - ACQ TagFull (full)\n"
                 + "  -\n"
                 + "  Events: 2, Time-Resets: 1\n"
                 + "  Buffer, Bytes used: 15\n",
@@ -85,7 +109,7 @@
         final int tagDatabaseSize = 2;
         final int logSize = 20;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -95,7 +119,7 @@
 
         assertEquals("Wake Lock Log\n"
                 + "  01-01 00:00:01.000 - --- - ACQ UNKNOWN (partial)\n"
-                + "  01-01 00:00:01.150 - 102 - ACQ TagFull (full)\n"
+                + "  01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull (full)\n"
                 + "  -\n"
                 + "  Events: 2, Time-Resets: 0\n"
                 + "  Buffer, Bytes used: 6\n",
@@ -107,26 +131,55 @@
         final int tagDatabaseSize = 6;
         final int logSize = 10;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
-        // This first item will get deleted when ring buffer loops around
+        // Wake lock 1 acquired - log size = 3
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
 
+        // Wake lock 2 acquired - log size = 3 + 3 = 6
         when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
         log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+
+        // Wake lock 3 acquired - log size = 6 + 3 = 9
         when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
         log.onWakeLockAcquired("TagThree", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+        // We need more space - wake lock 1 acquisition is removed from the log and saved in the
+        // list. Log size = 9 - 3 + 2 = 8
         when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
+        log.onWakeLockReleased("TagThree", 101);
+
+        // We need more space - wake lock 2 acquisition is removed from the log and saved in the
+        // list. Log size = 8 - 3 + 2 = 7
+        when(injectorSpy.currentTimeMillis()).thenReturn(1153L);
+        log.onWakeLockReleased("TagPartial", 101);
+
+        // We need more space - wake lock 3 acquisition is removed from the log and saved in the
+        // list. Log size = 7 - 3 + 3 = 7
+        when(injectorSpy.currentTimeMillis()).thenReturn(1154L);
         log.onWakeLockAcquired("TagFour", 101, PowerManager.PARTIAL_WAKE_LOCK);
 
+        // We need more space - wake lock 3 release is removed from the log and wake lock 3
+        // acquisition is removed from the list. Log size = 7 - 2 + 3 = 8
+        when(injectorSpy.currentTimeMillis()).thenReturn(1155L);
+        log.onWakeLockAcquired("TagFive", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+        // We need more space - wake lock 1 release is removed from the log and wake lock 1
+        // acquisition is removed from the list. Log size = 8 - 2 + 2 = 8
+        when(injectorSpy.currentTimeMillis()).thenReturn(1156L);
+        log.onWakeLockReleased("TagFull", 102);
+
+        // Wake lock 2 acquisition is still printed because its release have not rolled off the log
+        // yet.
         assertEquals("Wake Lock Log\n"
-                + "  01-01 00:00:01.150 - 102 - ACQ TagFull (full)\n"
-                + "  01-01 00:00:01.151 - 101 - ACQ TagThree (partial)\n"
-                + "  01-01 00:00:01.152 - 101 - ACQ TagFour (partial)\n"
+                + "  01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull (full)\n"
+                + "  01-01 00:00:01.154 - 101 (some.package1) - ACQ TagFour (partial)\n"
+                + "  01-01 00:00:01.155 - 101 (some.package1) - ACQ TagFive (partial)\n"
+                + "  01-01 00:00:01.156 - 102 (some.package2) - REL TagFull\n"
                 + "  -\n"
-                + "  Events: 3, Time-Resets: 0\n"
-                + "  Buffer, Bytes used: 9\n",
+                + "  Events: 4, Time-Resets: 0\n"
+                + "  Buffer, Bytes used: 8\n",
                 dumpLog(log, false));
     }
 
@@ -135,7 +188,7 @@
         final int tagDatabaseSize = 6;
         final int logSize = 10;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         // Bad tag means it wont get written
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
@@ -153,14 +206,15 @@
         final int tagDatabaseSize = 6;
         final int logSize = 10;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
                 PowerManager.PARTIAL_WAKE_LOCK);
 
         assertEquals("Wake Lock Log\n"
-                + "  01-01 00:00:01.000 - 101 - ACQ *job*/c.o.t.3/.o..Last (partial)\n"
+                + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ "
+                        + "*job*/c.o.t.3/.o..Last (partial)\n"
                 + "  -\n"
                 + "  Events: 1, Time-Resets: 0\n"
                 + "  Buffer, Bytes used: 3\n",
@@ -172,7 +226,7 @@
         final int tagDatabaseSize = 6;
         final int logSize = 10;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -180,8 +234,8 @@
         log.onWakeLockReleased("HowdyTag", 101);
 
         assertEquals("Wake Lock Log\n"
-                + "  01-01 00:00:01.000 - 101 - ACQ HowdyTag (partial)\n"
-                + "  01-01 00:00:01.001 - 101 - REL HowdyTag\n"
+                + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ HowdyTag (partial)\n"
+                + "  01-01 00:00:01.001 - 101 (some.package1) - REL HowdyTag\n"
                 + "  -\n"
                 + "  Events: 2, Time-Resets: 0\n"
                 + "  Buffer, Bytes used: 5\n"
@@ -194,7 +248,7 @@
         final int tagDatabaseSize = 6;
         final int logSize = 10;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1100L);
         log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -204,7 +258,7 @@
         log.onWakeLockReleased("HowdyTag", 101);
 
         assertEquals("Wake Lock Log\n"
-                + "  01-01 00:00:01.100 - 101 - ACQ HowdyTag (partial)\n"
+                + "  01-01 00:00:01.100 - 101 (some.package1) - ACQ HowdyTag (partial)\n"
                 + "  -\n"
                 + "  Events: 1, Time-Resets: 0\n"
                 + "  Buffer, Bytes used: 3\n",
@@ -216,20 +270,153 @@
         final int tagDatabaseSize = 6;
         final int logSize = 10;
         TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
-        WakeLockLog log = new WakeLockLog(injectorSpy);
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101,
                 PowerManager.PARTIAL_WAKE_LOCK | PowerManager.SYSTEM_WAKELOCK);
 
         assertEquals("Wake Lock Log\n"
-                        + "  01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,system-wakelock)\n"
+                        + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+                                + "(partial,system-wakelock)\n"
                         + "  -\n"
                         + "  Events: 1, Time-Resets: 0\n"
                         + "  Buffer, Bytes used: 3\n",
                 dumpLog(log, false));
     }
 
+    @Test
+    public void testAddItemWithNoPackageName() {
+        final int tagDatabaseSize = 128;
+        final int logSize = 20;
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+        when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
+        when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+        log.onWakeLockAcquired("TagPartial", 101,
+                PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+        assertEquals("Wake Lock Log\n"
+                        + "  01-01 00:00:01.000 - 101 - ACQ TagPartial "
+                                + "(partial,on-after-release)\n"
+                        + "  -\n"
+                        + "  Events: 1, Time-Resets: 0\n"
+                        + "  Buffer, Bytes used: 3\n",
+                dumpLog(log, false));
+    }
+
+    @Test
+    public void testAddItemWithMultiplePackageNames() {
+        final int tagDatabaseSize = 128;
+        final int logSize = 20;
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+        when(mPackageManager.getPackagesForUid(101)).thenReturn(
+                new String[]{ "some.package1", "some.package2", "some.package3" });
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+        log.onWakeLockAcquired("TagPartial", 101,
+                PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+        assertEquals("Wake Lock Log\n"
+                        + "  01-01 00:00:01.000 - 101 (some.package1,...) - ACQ TagPartial "
+                                + "(partial,on-after-release)\n"
+                        + "  -\n"
+                        + "  Events: 1, Time-Resets: 0\n"
+                        + "  Buffer, Bytes used: 3\n",
+                dumpLog(log, false));
+    }
+
+    @Test
+    public void testAddItemsWithRepeatOwnerUid_UsesCache() {
+        final int tagDatabaseSize = 128;
+        final int logSize = 20;
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+        log.onWakeLockAcquired("TagPartial", 101,
+                PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+        log.onWakeLockAcquired("TagFull", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
+        log.onWakeLockAcquired("TagFull2", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        assertEquals("Wake Lock Log\n"
+                        + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+                        + "(partial,on-after-release)\n"
+                        + "  01-01 00:00:01.150 - 101 (some.package1) - ACQ TagFull "
+                        + "(full,acq-causes-wake)\n"
+                        + "  01-01 00:00:01.151 - 101 (some.package1) - ACQ TagFull2 "
+                        + "(full,acq-causes-wake)\n"
+                        + "  -\n"
+                        + "  Events: 3, Time-Resets: 0\n"
+                        + "  Buffer, Bytes used: 9\n",
+                dumpLog(log, false));
+
+        verify(mPackageManager, times(1)).getPackagesForUid(101);
+    }
+
+    @Test
+    public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
+        final int tagDatabaseSize = 128;
+        final int logSize = 10;
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+        log.onWakeLockAcquired("TagPartial", 101,
+                PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+        log.onWakeLockAcquired("TagFull", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
+        log.onWakeLockAcquired("TagFull2", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
+        log.onWakeLockAcquired("TagFull3", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1153L);
+        log.onWakeLockAcquired("TagFull4", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1154L);
+        log.onWakeLockAcquired("TagFull5", 101,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+        // The first 3 events have been removed from the log and they exist in the saved
+        // acquisitions list. They should also use the cache when fetching the package names.
+        assertEquals("Wake Lock Log\n"
+                        + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+                        + "(partial,on-after-release)\n"
+                        + "  01-01 00:00:01.150 - 101 (some.package1) - ACQ TagFull "
+                        + "(full,acq-causes-wake)\n"
+                        + "  01-01 00:00:01.151 - 101 (some.package1) - ACQ TagFull2 "
+                        + "(full,acq-causes-wake)\n"
+                        + "  01-01 00:00:01.152 - 101 (some.package1) - ACQ TagFull3 "
+                        + "(full,acq-causes-wake)\n"
+                        + "  01-01 00:00:01.153 - 101 (some.package1) - ACQ TagFull4 "
+                        + "(full,acq-causes-wake)\n"
+                        + "  01-01 00:00:01.154 - 101 (some.package1) - ACQ TagFull5 "
+                        + "(full,acq-causes-wake)\n"
+                        + "  -\n"
+                        + "  Events: 6, Time-Resets: 0\n"
+                        + "  Buffer, Bytes used: 9\n",
+                dumpLog(log, false));
+
+        verify(mPackageManager, times(1)).getPackagesForUid(101);
+    }
+
     private String dumpLog(WakeLockLog log, boolean includeTagDb) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);