diff options
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | core/java/com/android/internal/os/KernelWakelockReader.java | 91 | ||||
-rw-r--r-- | core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java | 277 |
3 files changed, 354 insertions, 15 deletions
diff --git a/Android.bp b/Android.bp index 0d2caff8b42f..868886789761 100644 --- a/Android.bp +++ b/Android.bp @@ -687,6 +687,7 @@ java_defaults { static_libs: [ "apex_aidl_interface-java", + "suspend_control_aidl_interface-java", "framework-protos", "game-driver-protos", "android.hidl.base-V1.0-java", diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index 86b2a90dd64e..df8c6d021466 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -16,8 +16,13 @@ package com.android.internal.os; import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; import android.os.StrictMode; import android.os.SystemClock; +import android.system.suspend.ISuspendControlService; +import android.system.suspend.WakeLockInfo; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -58,6 +63,7 @@ public class KernelWakelockReader { private final String[] mProcWakelocksName = new String[3]; private final long[] mProcWakelocksData = new long[3]; + private ISuspendControlService mSuspendControlService = null; /** * Reads kernel wakelock stats and updates the staleStats with the new information. @@ -117,7 +123,52 @@ public class KernelWakelockReader { } } } - return parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + + updateVersion(staleStats); + + parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + + if (mSuspendControlService == null) { + try { + mSuspendControlService = ISuspendControlService.Stub.asInterface( + ServiceManager.getServiceOrThrow("suspend_control")); + } catch (ServiceNotFoundException e) { + Slog.wtf(TAG, "Required service suspend_control not available", e); + } + } + + try { + WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats(); + getNativeWakelockStats(wlStats, staleStats); + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); + } + + return removeOldStats(staleStats); + } + + /** + * Reads native wakelock stats from SystemSuspend and updates staleStats with the new + * information. + * @param staleStats Existing object to update. + * @return the updated stats. + */ + @VisibleForTesting + public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats, + final KernelWakelockStats staleStats) { + for (WakeLockInfo info : wlStats) { + if (!staleStats.containsKey(info.name)) { + staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount, + info.totalTime, sKernelWakelockUpdateVersion)); + } else { + KernelWakelockStats.Entry kwlStats = staleStats.get(info.name); + kwlStats.mCount = (int) info.activeCount; + kwlStats.mTotalTime = info.totalTime; + kwlStats.mVersion = sKernelWakelockUpdateVersion; + } + } + + return staleStats; } /** @@ -138,7 +189,6 @@ public class KernelWakelockReader { startIndex = endIndex = i + 1; synchronized(this) { - sKernelWakelockUpdateVersion++; while (endIndex < len) { for (endIndex=startIndex; endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; @@ -199,16 +249,35 @@ public class KernelWakelockReader { startIndex = endIndex + 1; } - // Don't report old data. - Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); - while (itr.hasNext()) { - if (itr.next().mVersion != sKernelWakelockUpdateVersion) { - itr.remove(); - } - } - - staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; return staleStats; } } + + /** + * Increments sKernelWakelockUpdateVersion and updates the version in staleStats. + * @param staleStats Existing object to update. + * @return the updated stats. + */ + @VisibleForTesting + public KernelWakelockStats updateVersion(KernelWakelockStats staleStats) { + sKernelWakelockUpdateVersion++; + staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; + return staleStats; + } + + /** + * Removes old stats from staleStats. + * @param staleStats Existing object to update. + * @return the updated stats. + */ + @VisibleForTesting + public KernelWakelockStats removeOldStats(final KernelWakelockStats staleStats) { + Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); + while (itr.hasNext()) { + if (itr.next().mVersion != sKernelWakelockUpdateVersion) { + itr.remove(); + } + } + return staleStats; + } } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java index 4e4bb350739d..008085e38dbf 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java @@ -13,14 +13,17 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.android.internal.os; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; import java.nio.charset.Charset; +import android.system.suspend.WakeLockInfo; + public class KernelWakelockReaderTest extends TestCase { /** * Helper class that builds the mock Kernel module file /d/wakeup_sources. @@ -57,6 +60,39 @@ public class KernelWakelockReaderTest extends TestCase { } } + /** + * Helper method to create WakeLockInfo object. + * @param totalTime is time in microseconds. + * @return the created WakeLockInfo object. + */ + private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) { + WakeLockInfo info = new WakeLockInfo(); + info.name = name; + info.pid = 1; + info.activeCount = activeCount; + info.isActive = true; + info.activeSince = 0; + info.lastChange = 0; + info.maxTime = 0; + info.totalTime = totalTime; + return info; + } + + /** + * Helper method for KernelWakeLockReader::readKernelWakelockStats(...) + * @param staleStats existing stats to update. + * @param buffer representation of mock kernel module file /d/wakeup_sources. + * @param wlStats mock WakeLockInfo list returned from ISuspendControlService. + * @return the updated stats. + */ + private KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats, + byte[] buffer, WakeLockInfo[] wlStats) { + mReader.updateVersion(staleStats); + mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats); + mReader.getNativeWakelockStats(wlStats, staleStats); + return mReader.removeOldStats(staleStats); + } + private KernelWakelockReader mReader; @Override @@ -65,18 +101,22 @@ public class KernelWakelockReaderTest extends TestCase { mReader = new KernelWakelockReader(); } +// ------------------------- Kernel Wakelock Stats Test ------------------------ @SmallTest public void testParseEmptyFile() throws Exception { KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true, new KernelWakelockStats()); + assertTrue(staleStats.isEmpty()); } @SmallTest public void testOnlyHeader() throws Exception { byte[] buffer = new ProcFileBuilder().getBytes(); + KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, new KernelWakelockStats()); + assertTrue(staleStats.isEmpty()); } @@ -85,9 +125,12 @@ public class KernelWakelockReaderTest extends TestCase { byte[] buffer = new ProcFileBuilder() .addLine("Wakelock", 34, 123) // Milliseconds .getBytes(); + KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, new KernelWakelockStats()); + assertEquals(1, staleStats.size()); + assertTrue(staleStats.containsKey("Wakelock")); KernelWakelockStats.Entry entry = staleStats.get("Wakelock"); @@ -101,9 +144,12 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("Wakelock", 1, 10) .addLine("Fakelock", 2, 20) .getBytes(); + KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, new KernelWakelockStats()); + assertEquals(2, staleStats.size()); + assertTrue(staleStats.containsKey("Wakelock")); assertTrue(staleStats.containsKey("Fakelock")); } @@ -114,8 +160,10 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("Wakelock", 1, 10) // Milliseconds .addLine("Wakelock", 1, 10) // Milliseconds .getBytes(); + KernelWakelockStats staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, new KernelWakelockStats()); + assertEquals(1, staleStats.size()); assertTrue(staleStats.containsKey("Wakelock")); @@ -126,12 +174,14 @@ public class KernelWakelockReaderTest extends TestCase { @SmallTest public void testWakelocksBecomeStale() throws Exception { + KernelWakelockStats staleStats = new KernelWakelockStats(); + byte[] buffer = new ProcFileBuilder() .addLine("Fakelock", 3, 30) .getBytes(); - KernelWakelockStats staleStats = new KernelWakelockStats(); - staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats); + readKernelWakelockStats(staleStats, buffer, new WakeLockInfo[0]); + assertEquals(1, staleStats.size()); assertTrue(staleStats.containsKey("Fakelock")); @@ -139,9 +189,228 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("Wakelock", 1, 10) .getBytes(); - staleStats = mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats); + readKernelWakelockStats(staleStats, buffer, new WakeLockInfo[0]); + assertEquals(1, staleStats.size()); assertTrue(staleStats.containsKey("Wakelock")); assertFalse(staleStats.containsKey("Fakelock")); } + +// -------------------- Native (SystemSuspend) Wakelock Stats Test ------------------- + @SmallTest + public void testEmptyWakeLockInfoList() { + KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0], + new KernelWakelockStats()); + + assertTrue(staleStats.isEmpty()); + } + + @SmallTest + public void testOneWakeLockInfo() { + WakeLockInfo[] wlStats = new WakeLockInfo[1]; + wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000); + + KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats, + new KernelWakelockStats()); + + assertEquals(1, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock")); + + KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); + assertEquals(20, entry.mCount); + assertEquals(10000, entry.mTotalTime); + } + + @SmallTest + public void testTwoWakeLockInfos() { + WakeLockInfo[] wlStats = new WakeLockInfo[2]; + wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); + wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); + + KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats, + new KernelWakelockStats()); + + assertEquals(2, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock1")); + assertTrue(staleStats.containsKey("WakeLock2")); + + KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); + assertEquals(10, entry1.mCount); + assertEquals(1000, entry1.mTotalTime); + + KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); + assertEquals(20, entry2.mCount); + assertEquals(2000, entry2.mTotalTime); + } + + @SmallTest + public void testWakeLockInfosBecomeStale() { + WakeLockInfo[] wlStats = new WakeLockInfo[1]; + wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); + + KernelWakelockStats staleStats = new KernelWakelockStats(); + + readKernelWakelockStats(staleStats, new byte[0], wlStats); + + assertEquals(1, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock1")); + KernelWakelockStats.Entry entry = staleStats.get("WakeLock1"); + assertEquals(10, entry.mCount); + assertEquals(1000, entry.mTotalTime); + + wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); + + readKernelWakelockStats(staleStats, new byte[0], wlStats); + + assertEquals(1, staleStats.size()); + + assertFalse(staleStats.containsKey("WakeLock1")); + assertTrue(staleStats.containsKey("WakeLock2")); + entry = staleStats.get("WakeLock2"); + assertEquals(20, entry.mCount); + assertEquals(2000, entry.mTotalTime); + } + +// -------------------- Aggregate Wakelock Stats Tests -------------------- + @SmallTest + public void testAggregateStatsEmpty() throws Exception { + KernelWakelockStats staleStats = new KernelWakelockStats(); + + byte[] buffer = new byte[0]; + WakeLockInfo[] wlStats = new WakeLockInfo[0]; + + readKernelWakelockStats(staleStats, buffer, wlStats); + + assertTrue(staleStats.isEmpty()); + } + + @SmallTest + public void testAggregateStatsNoNativeWakelocks() throws Exception { + KernelWakelockStats staleStats = new KernelWakelockStats(); + + byte[] buffer = new ProcFileBuilder() + .addLine("Wakelock", 34, 123) // Milliseconds + .getBytes(); + WakeLockInfo[] wlStats = new WakeLockInfo[0]; + + readKernelWakelockStats(staleStats, buffer, wlStats); + + assertEquals(1, staleStats.size()); + + assertTrue(staleStats.containsKey("Wakelock")); + + KernelWakelockStats.Entry entry = staleStats.get("Wakelock"); + assertEquals(34, entry.mCount); + assertEquals(1000 * 123, entry.mTotalTime); // Microseconds + } + + @SmallTest + public void testAggregateStatsNoKernelWakelocks() throws Exception { + KernelWakelockStats staleStats = new KernelWakelockStats(); + + byte[] buffer = new byte[0]; + WakeLockInfo[] wlStats = new WakeLockInfo[1]; + wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); + + readKernelWakelockStats(staleStats, buffer, wlStats); + + assertEquals(1, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock")); + + KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); + assertEquals(10, entry.mCount); + assertEquals(1000, entry.mTotalTime); + } + + @SmallTest + public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception { + KernelWakelockStats staleStats = new KernelWakelockStats(); + + byte[] buffer = new ProcFileBuilder() + .addLine("WakeLock1", 34, 123) // Milliseconds + .getBytes(); + WakeLockInfo[] wlStats = new WakeLockInfo[1]; + wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); + + readKernelWakelockStats(staleStats, buffer, wlStats); + + assertEquals(2, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock1")); + KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); + assertEquals(34, entry1.mCount); + assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds + + assertTrue(staleStats.containsKey("WakeLock2")); + KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); + assertEquals(10, entry2.mCount); + assertEquals(1000, entry2.mTotalTime); + } + + @SmallTest + public void testAggregateStatsUpdate() throws Exception { + KernelWakelockStats staleStats = new KernelWakelockStats(); + + byte[] buffer = new ProcFileBuilder() + .addLine("WakeLock1", 34, 123) // Milliseconds + .addLine("WakeLock2", 46, 345) // Milliseconds + .getBytes(); + WakeLockInfo[] wlStats = new WakeLockInfo[2]; + wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); + wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); + + readKernelWakelockStats(staleStats, buffer, wlStats); + + assertEquals(4, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock1")); + assertTrue(staleStats.containsKey("WakeLock2")); + assertTrue(staleStats.containsKey("WakeLock3")); + assertTrue(staleStats.containsKey("WakeLock4")); + + KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); + assertEquals(34, entry1.mCount); + assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds + + KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); + assertEquals(46, entry2.mCount); + assertEquals(345 * 1000, entry2.mTotalTime); // Microseconds + + KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3"); + assertEquals(10, entry3.mCount); + assertEquals(1000, entry3.mTotalTime); + + KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4"); + assertEquals(20, entry4.mCount); + assertEquals(2000, entry4.mTotalTime); + + buffer = new ProcFileBuilder() + .addLine("WakeLock1", 45, 789) // Milliseconds + .addLine("WakeLock1", 56, 123) // Milliseconds + .getBytes(); + wlStats = new WakeLockInfo[1]; + wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); + + readKernelWakelockStats(staleStats, buffer, wlStats); + + assertEquals(2, staleStats.size()); + + assertTrue(staleStats.containsKey("WakeLock1")); + assertTrue(staleStats.containsKey("WakeLock4")); + + assertFalse(staleStats.containsKey("WakeLock2")); + assertFalse(staleStats.containsKey("WakeLock3")); + + entry1 = staleStats.get("WakeLock1"); + assertEquals(45 + 56, entry1.mCount); + assertEquals((789 + 123) * 1000, entry1.mTotalTime); // Microseconds + + entry2 = staleStats.get("WakeLock4"); + assertEquals(40, entry2.mCount); + assertEquals(4000, entry4.mTotalTime); + } } |