199 lines
8.0 KiB
Java
199 lines
8.0 KiB
Java
/*
|
|
* 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.Collection;
|
|
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 = "0-0;1000-1000";
|
|
|
|
/** Minimum total CPU usage to report */
|
|
private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY =
|
|
"minimum_total_cpu_usage_millis";
|
|
|
|
private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 10000;
|
|
|
|
private final Context mContext;
|
|
|
|
@Nullable private final KernelCpuThreadReader mKernelCpuThreadReader;
|
|
|
|
@Nullable private final KernelCpuThreadReaderDiff mKernelCpuThreadReaderDiff;
|
|
|
|
/**
|
|
* @return returns a created {@link KernelCpuThreadReader} that will be modified by any change
|
|
* in settings, returns null if creation failed
|
|
*/
|
|
@Nullable
|
|
public static KernelCpuThreadReaderDiff 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.mKernelCpuThreadReaderDiff;
|
|
}
|
|
|
|
private KernelCpuThreadReaderSettingsObserver(Context context) {
|
|
super(BackgroundThread.getHandler());
|
|
mContext = context;
|
|
mKernelCpuThreadReader =
|
|
KernelCpuThreadReader.create(
|
|
NUM_BUCKETS_DEFAULT, UidPredicate.fromString(COLLECTED_UIDS_DEFAULT));
|
|
mKernelCpuThreadReaderDiff =
|
|
mKernelCpuThreadReader == null
|
|
? null
|
|
: new KernelCpuThreadReaderDiff(
|
|
mKernelCpuThreadReader, MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
|
|
}
|
|
|
|
@Override
|
|
public void onChange(boolean selfChange, Collection<Uri> uris, int flags, 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);
|
|
mKernelCpuThreadReaderDiff.setMinimumTotalCpuUsageMillis(
|
|
parser.getInt(
|
|
MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
|
|
MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
|
|
}
|
|
|
|
/** 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
|
|
@SuppressWarnings("ForLoopReplaceableByForEach")
|
|
public boolean test(Integer uid) {
|
|
for (int i = 0; i < mAcceptedUidRanges.size(); i++) {
|
|
if (mAcceptedUidRanges.get(i).contains(uid)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|