diff options
8 files changed, 379 insertions, 54 deletions
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java index 11c7599a508a..92348497101e 100644 --- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java +++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java @@ -39,7 +39,8 @@ public class KernelCpuThreadReaderPerfTest { @Rule public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - private final KernelCpuThreadReader mKernelCpuThreadReader = KernelCpuThreadReader.create(); + private final KernelCpuThreadReader mKernelCpuThreadReader = + KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000); @Test public void timeReadCurrentProcessCpuUsage() { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a0a59215c10b..e14bb66828a4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14328,6 +14328,19 @@ public final class Settings { public static final String LOOPER_STATS = "looper_stats"; /** + * Settings for collecting statistics on CPU usage per thread + * + * The following strings are supported as keys: + * <pre> + * num_buckets (int) + * collected_uids (string) + * </pre> + * + * @hide + */ + public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader"; + + /** * Default user id to boot into. They map to user ids, for example, 10, 11, 12. * * @hide diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 2742b7cdf872..b1328e862273 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -37,9 +37,10 @@ import java.util.function.Predicate; * frequency band. * * <p>Frequencies are bucketed together to reduce the amount of data created. This means that we - * return {@link #NUM_BUCKETS} frequencies instead of the full number. Frequencies are reported as - * the lowest frequency in that range. Frequencies are spread as evenly as possible across the - * buckets. The buckets do not cross over the little/big frequencies reported. + * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of + * frequencies is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest + * frequency in that range. Frequencies are spread as evenly as possible across the buckets. The + * buckets do not cross over the little/big frequencies reported. * * <p>N.B.: In order to bucket across little/big frequencies correctly, we assume that the {@code * time_in_state} file contains every little core frequency in ascending order, followed by every @@ -97,20 +98,15 @@ public class KernelCpuThreadReader { DEFAULT_PROC_PATH.resolve("self/time_in_state"); /** - * Number of frequency buckets - */ - private static final int NUM_BUCKETS = 8; - - /** - * Default predicate for what UIDs to check for when getting processes. This filters to only - * select UID 1000 (the {@code system} user) + * Value returned when there was an error getting an integer ID value (e.g. PID, UID) */ - private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000; + private static final int ID_ERROR = -1; /** - * Value returned when there was an error getting an integer ID value (e.g. PID, UID) + * When checking whether to report data for a thread, we check the UID of the thread's owner + * against this predicate */ - private static final int ID_ERROR = -1; + private Predicate<Integer> mUidPredicate; /** * Where the proc filesystem is mounted @@ -121,7 +117,7 @@ public class KernelCpuThreadReader { * Frequencies read from the {@code time_in_state} file. Read from {@link * #mProcTimeInStateReader#getCpuFrequenciesKhz()} and cast to {@code int[]} */ - private final int[] mFrequenciesKhz; + private int[] mFrequenciesKhz; /** * Used to read and parse {@code time_in_state} files @@ -131,17 +127,10 @@ public class KernelCpuThreadReader { /** * Used to sort frequencies and usage times into buckets */ - private final FrequencyBucketCreator mFrequencyBucketCreator; + private FrequencyBucketCreator mFrequencyBucketCreator; private final Injector mInjector; - private KernelCpuThreadReader() throws IOException { - this( - DEFAULT_PROC_PATH, - DEFAULT_INITIAL_TIME_IN_STATE_PATH, - new Injector()); - } - /** * Create with a path where `proc` is mounted. Used primarily for testing * @@ -151,17 +140,16 @@ public class KernelCpuThreadReader { */ @VisibleForTesting public KernelCpuThreadReader( + int numBuckets, + Predicate<Integer> uidPredicate, Path procPath, Path initialTimeInStatePath, Injector injector) throws IOException { + mUidPredicate = uidPredicate; mProcPath = procPath; mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath); mInjector = injector; - - // Copy mProcTimeInState's frequencies and initialize bucketing - final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz(); - mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, NUM_BUCKETS); - mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies(frequenciesKhz); + setNumBuckets(numBuckets); } /** @@ -170,9 +158,14 @@ public class KernelCpuThreadReader { * @return the reader, null if an exception was thrown during creation */ @Nullable - public static KernelCpuThreadReader create() { + public static KernelCpuThreadReader create(int numBuckets, Predicate<Integer> uidPredicate) { try { - return new KernelCpuThreadReader(); + return new KernelCpuThreadReader( + numBuckets, + uidPredicate, + DEFAULT_PROC_PATH, + DEFAULT_INITIAL_TIME_IN_STATE_PATH, + new Injector()); } catch (IOException e) { Slog.e(TAG, "Failed to initialize KernelCpuThreadReader", e); return null; @@ -180,14 +173,6 @@ public class KernelCpuThreadReader { } /** - * Get the per-thread CPU usage of all processes belonging to UIDs between {@code [1000, 2000)} - */ - @Nullable - public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() { - return getProcessCpuUsageByUids(DEFAULT_UID_PREDICATE); - } - - /** * Get the per-thread CPU usage of all processes belonging to a set of UIDs * * <p>This function will crawl through all process {@code proc} directories found by the pattern @@ -195,10 +180,11 @@ public class KernelCpuThreadReader { * approximately 500ms on a Pixel 2. Therefore, this method can be computationally expensive, * and should not be called more than once an hour. * - * @param uidPredicate only get usage from processes owned by UIDs that match this predicate + * <p>Data is only collected for UIDs passing the predicate supplied in {@link + * #setUidPredicate}. */ @Nullable - public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids(Predicate<Integer> uidPredicate) { + public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() { if (DEBUG) { Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs"); } @@ -213,7 +199,7 @@ public class KernelCpuThreadReader { if (uid == ID_ERROR || processId == ID_ERROR) { continue; } - if (!uidPredicate.test(uid)) { + if (!mUidPredicate.test(uid)) { continue; } @@ -247,10 +233,7 @@ public class KernelCpuThreadReader { */ @Nullable public ProcessCpuUsage getCurrentProcessCpuUsage() { - return getProcessCpuUsage( - mProcPath.resolve("self"), - mInjector.myPid(), - mInjector.myUid()); + return getProcessCpuUsage(mProcPath.resolve("self"), mInjector.myPid(), mInjector.myUid()); } /** @@ -300,6 +283,31 @@ public class KernelCpuThreadReader { } /** + * Set the number of frequency buckets to use + */ + void setNumBuckets(int numBuckets) { + if (numBuckets < 1) { + Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); + return; + } + // If `numBuckets` hasn't changed since the last set, do nothing + if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { + return; + } + mFrequencyBucketCreator = new FrequencyBucketCreator( + mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); + mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies( + mProcTimeInStateReader.getFrequenciesKhz()); + } + + /** + * Set the UID predicate for {@link #getProcessCpuUsageByUids} + */ + void setUidPredicate(Predicate<Integer> uidPredicate) { + mUidPredicate = uidPredicate; + } + + /** * Get the CPU frequencies that correspond to the times reported in * {@link ThreadCpuUsage#usageTimesMillis} */ diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java new file mode 100644 index 000000000000..77f6a17acdc9 --- /dev/null +++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.annotation.Nullable; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.KeyValueListParser; +import android.util.Range; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Service that handles settings for {@link KernelCpuThreadReader} + * + * <p>N.B.: The `collected_uids` setting takes a string representation of what UIDs to collect data + * for. A string representation is used as we will want to express UID ranges, therefore an integer + * array could not be used. The format of the string representation is detailed here: {@link + * UidPredicate#fromString}. + * + * @hide Only for use within the system server + */ +public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { + private static final String TAG = "KernelCpuThreadReaderSettingsObserver"; + + /** + * The number of frequency buckets to report + */ + private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets"; + private static final int NUM_BUCKETS_DEFAULT = 8; + + /** + * List of UIDs to report data for + */ + private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids"; + private static final String COLLECTED_UIDS_DEFAULT = "1000-1000"; + + private final Context mContext; + + @Nullable + private final KernelCpuThreadReader mKernelCpuThreadReader; + + /** + * @return returns a created {@link KernelCpuThreadReader} that will be modified by any + * change in settings, returns null if creation failed + */ + @Nullable + public static KernelCpuThreadReader getSettingsModifiedReader(Context context) { + // Create the observer + KernelCpuThreadReaderSettingsObserver settingsObserver = + new KernelCpuThreadReaderSettingsObserver(context); + // Register the observer to listen for setting changes + Uri settingsUri = + Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER); + context.getContentResolver().registerContentObserver( + settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM); + // Return the observer's reader + return settingsObserver.mKernelCpuThreadReader; + } + + private KernelCpuThreadReaderSettingsObserver(Context context) { + super(BackgroundThread.getHandler()); + mContext = context; + mKernelCpuThreadReader = KernelCpuThreadReader.create( + NUM_BUCKETS_DEFAULT, + UidPredicate.fromString(COLLECTED_UIDS_DEFAULT)); + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + updateReader(); + } + + /** + * Update the reader with new settings + */ + private void updateReader() { + if (mKernelCpuThreadReader == null) { + return; + } + + final KeyValueListParser parser = new KeyValueListParser(','); + try { + parser.setString(Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.KERNEL_CPU_THREAD_READER)); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Bad settings", e); + return; + } + + final UidPredicate uidPredicate; + try { + uidPredicate = UidPredicate.fromString( + parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT)); + } catch (NumberFormatException e) { + Slog.w(TAG, "Failed to get UID predicate", e); + return; + } + + mKernelCpuThreadReader.setNumBuckets( + parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT)); + mKernelCpuThreadReader.setUidPredicate(uidPredicate); + } + + /** + * Check whether a UID belongs to a set of UIDs + */ + @VisibleForTesting + public static class UidPredicate implements Predicate<Integer> { + private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)"); + private static final String UID_SPECIFIER_DELIMITER = ";"; + private final List<Range<Integer>> mAcceptedUidRanges; + + /** + * Create a UID predicate from a string representing a list of UID ranges + * + * <p>UID ranges are a pair of integers separated by a '-'. If you want to specify a single + * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by + * a single ';'. For example, this would be a valid string representation: {@code + * "1000-1999;2003-2003;2004-2004;2050-2060"}. + * + * <p>We do not use ',' to delimit as it is already used in separating different setting + * arguments. + * + * @throws NumberFormatException if the input string is incorrectly formatted + * @throws IllegalArgumentException if an UID range has a lower end than start + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public static UidPredicate fromString(String predicateString) throws NumberFormatException { + final List<Range<Integer>> acceptedUidRanges = new ArrayList<>(); + for (String uidSpecifier : predicateString.split(UID_SPECIFIER_DELIMITER)) { + final Matcher uidRangeMatcher = UID_RANGE_PATTERN.matcher(uidSpecifier); + if (!uidRangeMatcher.matches()) { + throw new NumberFormatException( + "Failed to recognize as number range: " + uidSpecifier); + } + acceptedUidRanges.add(Range.create( + Integer.parseInt(uidRangeMatcher.group(1)), + Integer.parseInt(uidRangeMatcher.group(2)))); + } + return new UidPredicate(acceptedUidRanges); + } + + private UidPredicate(List<Range<Integer>> acceptedUidRanges) { + mAcceptedUidRanges = acceptedUidRanges; + } + + @Override + public boolean test(Integer uid) { + for (int i = 0; i < mAcceptedUidRanges.size(); i++) { + if (mAcceptedUidRanges.get(i).contains(uid)) { + return true; + } + } + return false; + } + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index f42003372703..2cb925aa9549 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -297,6 +297,7 @@ public class SettingsBackupTest { Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL, Settings.Global.JOB_SCHEDULER_CONSTANTS, Settings.Global.KEEP_PROFILE_IN_BACKGROUND, + Settings.Global.KERNEL_CPU_THREAD_READER, Settings.Global.LANG_ID_UPDATE_CONTENT_URL, Settings.Global.LANG_ID_UPDATE_METADATA_URL, Settings.Global.LAST_ACTIVE_USER_ID, diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java index c03d1f37ffa7..3ddd8aaeba2c 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.os.Process; import android.os.SystemClock; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; @@ -121,7 +122,8 @@ public class KernelCpuThreadReaderEndToEndTest { workFinishedLatch.await(); // Get thread data from KernelCpuThreadReader - final KernelCpuThreadReader kernelCpuThreadReader = KernelCpuThreadReader.create(); + final KernelCpuThreadReader kernelCpuThreadReader = + KernelCpuThreadReader.create(8, uid -> uid == Process.myUid()); assertNotNull(kernelCpuThreadReader); final ProcessCpuUsage currentProcessCpuUsage = kernelCpuThreadReader.getCurrentProcessCpuUsage(); diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java index 0c56b8a3e8aa..b9744f599a9a 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java @@ -18,8 +18,10 @@ package com.android.internal.os; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.testng.Assert.assertThrows; import android.content.Context; import android.os.FileUtils; @@ -99,6 +101,8 @@ public class KernelCpuThreadReaderTest { THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES); final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( + 8, + uid -> 1000 <= uid && uid < 2000, mProcDirectory.toPath(), mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"), processUtils); @@ -138,11 +142,13 @@ public class KernelCpuThreadReaderTest { new int[][]{{uid}}); } final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( + 8, + uidPredicate, mProcDirectory.toPath(), mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"), processUtils); ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids = - kernelCpuThreadReader.getProcessCpuUsageByUids(uidPredicate); + kernelCpuThreadReader.getProcessCpuUsageByUids(); processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId)); assertEquals(expectedUids.length, processCpuUsageByUids.size()); @@ -350,4 +356,106 @@ public class KernelCpuThreadReaderTest { 4, KernelCpuThreadReader.FrequencyBucketCreator.getBigFrequenciesStartIndex( new long[]{1, 2, 3, 4})); } + + @Test + public void testUidPredicate_singleRange() { + KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = + KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1000-1999"); + assertTrue(uidPredicate.test(1000)); + assertTrue(uidPredicate.test(1050)); + assertTrue(uidPredicate.test(1999)); + assertFalse(uidPredicate.test(2000)); + assertFalse(uidPredicate.test(0)); + assertFalse(uidPredicate.test(10000)); + assertFalse(uidPredicate.test(-100)); + } + + @Test + public void testUidPredicate_singleUid() { + KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = + KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1234-1234"); + assertTrue(uidPredicate.test(1234)); + assertFalse(uidPredicate.test(1235)); + assertFalse(uidPredicate.test(1232)); + assertFalse(uidPredicate.test(0)); + assertFalse(uidPredicate.test(-1234)); + } + + @Test + public void testUidPredicate_uidAndRange() { + KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = + KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString( + "1000-1000;1050-1060"); + assertTrue(uidPredicate.test(1000)); + assertTrue(uidPredicate.test(1050)); + assertTrue(uidPredicate.test(1054)); + assertTrue(uidPredicate.test(1060)); + assertFalse(uidPredicate.test(1040)); + assertFalse(uidPredicate.test(1001)); + assertFalse(uidPredicate.test(0)); + assertFalse(uidPredicate.test(-1000)); + } + + @Test + public void testUidPredicate_multiple() { + KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = + KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString( + "1000-1000;1050-1060;1001-1001;2000-3000"); + assertTrue(uidPredicate.test(1000)); + assertTrue(uidPredicate.test(1001)); + assertTrue(uidPredicate.test(1050)); + assertTrue(uidPredicate.test(1054)); + assertTrue(uidPredicate.test(1060)); + assertTrue(uidPredicate.test(1001)); + assertTrue(uidPredicate.test(2000)); + assertTrue(uidPredicate.test(2444)); + assertTrue(uidPredicate.test(3000)); + assertFalse(uidPredicate.test(0)); + assertFalse(uidPredicate.test(1040)); + assertFalse(uidPredicate.test(3001)); + assertFalse(uidPredicate.test(1999)); + } + + @Test + public void testUidPredicate_emptyRangeString() { + assertThrows( + NumberFormatException.class, + () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("")); + } + + @Test + public void testUidPredicate_singleNumber() { + assertThrows( + NumberFormatException.class, + () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1000")); + } + + @Test + public void testUidPredicate_lettersInRange() { + assertThrows( + NumberFormatException.class, + () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString( + "0-0;1-1;a;3-3")); + } + + @Test + public void testUidPredicate_onlyLetters() { + assertThrows( + NumberFormatException.class, + () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("abc")); + } + + @Test + public void testUidPredicate_backwardsRange() { + assertThrows( + IllegalArgumentException.class, + () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("20-10")); + } + + @Test + public void testUidPredicate_comma() { + assertThrows( + NumberFormatException.class, + () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1-1,2-2,3-3")); + } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 2be78fe32f68..2378c57ff8c9 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -98,6 +98,7 @@ import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BinderCallsStats.ExportedCallStat; import com.android.internal.os.KernelCpuSpeedReader; import com.android.internal.os.KernelCpuThreadReader; +import com.android.internal.os.KernelCpuThreadReaderSettingsObserver; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; @@ -203,7 +204,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { "zygote64", }; - private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8; + private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8; static final class CompanionHandler extends Handler { CompanionHandler(Looper looper) { @@ -332,7 +333,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { handlerThread.start(); mHandler = new CompanionHandler(handlerThread.getLooper()); - mKernelCpuThreadReader = KernelCpuThreadReader.create(); + mKernelCpuThreadReader = + KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext); } @Override @@ -1678,8 +1680,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return; } int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz(); - if (cpuFrequencies.length != CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES) { - Slog.w(TAG, "Expected " + CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES + if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) { + Slog.w(TAG, "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES + " frequencies, but got " + cpuFrequencies.length); return; } @@ -1703,9 +1705,17 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeInt(threadCpuUsage.threadId); e.writeString(processCpuUsage.processName); e.writeString(threadCpuUsage.threadName); - for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES; k++) { - e.writeInt(cpuFrequencies[k]); - e.writeInt(threadCpuUsage.usageTimesMillis[k]); + for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) { + if (k < cpuFrequencies.length) { + e.writeInt(cpuFrequencies[k]); + e.writeInt(threadCpuUsage.usageTimesMillis[k]); + } else { + // If we have no more frequencies to write, we still must write empty data. + // We know that this data is empty (and not just zero) because all + // frequencies are expected to be greater than zero + e.writeInt(0); + e.writeInt(0); + } } pulledData.add(e); } |