blob: 3851ce6d9cbdc70993cba4c676ac2dd3278d87f6 [file] [log] [blame]
Misha Wagner4b32c9f2019-01-25 15:30:14 +00001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import android.annotation.Nullable;
20import android.content.Context;
21import android.database.ContentObserver;
22import android.net.Uri;
23import android.os.UserHandle;
24import android.provider.Settings;
25import android.util.KeyValueListParser;
26import android.util.Range;
27import android.util.Slog;
28
29import com.android.internal.annotations.VisibleForTesting;
30
31import java.util.ArrayList;
32import java.util.List;
33import java.util.function.Predicate;
34import java.util.regex.Matcher;
35import java.util.regex.Pattern;
36
37/**
38 * Service that handles settings for {@link KernelCpuThreadReader}
39 *
40 * <p>N.B.: The `collected_uids` setting takes a string representation of what UIDs to collect data
41 * for. A string representation is used as we will want to express UID ranges, therefore an integer
42 * array could not be used. The format of the string representation is detailed here: {@link
43 * UidPredicate#fromString}.
44 *
45 * @hide Only for use within the system server
46 */
47public class KernelCpuThreadReaderSettingsObserver extends ContentObserver {
48 private static final String TAG = "KernelCpuThreadReaderSettingsObserver";
49
Misha Wagner3989eb02019-03-19 11:05:37 +000050 /** The number of frequency buckets to report */
Misha Wagner4b32c9f2019-01-25 15:30:14 +000051 private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets";
Misha Wagner3989eb02019-03-19 11:05:37 +000052
Misha Wagner4b32c9f2019-01-25 15:30:14 +000053 private static final int NUM_BUCKETS_DEFAULT = 8;
54
Misha Wagner3989eb02019-03-19 11:05:37 +000055 /** List of UIDs to report data for */
Misha Wagner4b32c9f2019-01-25 15:30:14 +000056 private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids";
Misha Wagner3989eb02019-03-19 11:05:37 +000057
Misha Wagnerd250dd82019-03-01 16:06:28 +000058 private static final String COLLECTED_UIDS_DEFAULT = "0-0;1000-1000";
Misha Wagner4b32c9f2019-01-25 15:30:14 +000059
Misha Wagner3989eb02019-03-19 11:05:37 +000060 /** Minimum total CPU usage to report */
Misha Wagner648d2032019-02-11 10:43:27 +000061 private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY =
62 "minimum_total_cpu_usage_millis";
Misha Wagner3989eb02019-03-19 11:05:37 +000063
Misha Wagnerd250dd82019-03-01 16:06:28 +000064 private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 10000;
Misha Wagner648d2032019-02-11 10:43:27 +000065
Misha Wagner4b32c9f2019-01-25 15:30:14 +000066 private final Context mContext;
67
Misha Wagner3989eb02019-03-19 11:05:37 +000068 @Nullable private final KernelCpuThreadReader mKernelCpuThreadReader;
Misha Wagner4b32c9f2019-01-25 15:30:14 +000069
70 /**
Misha Wagner3989eb02019-03-19 11:05:37 +000071 * @return returns a created {@link KernelCpuThreadReader} that will be modified by any change
72 * in settings, returns null if creation failed
Misha Wagner4b32c9f2019-01-25 15:30:14 +000073 */
74 @Nullable
75 public static KernelCpuThreadReader getSettingsModifiedReader(Context context) {
76 // Create the observer
77 KernelCpuThreadReaderSettingsObserver settingsObserver =
78 new KernelCpuThreadReaderSettingsObserver(context);
79 // Register the observer to listen for setting changes
Misha Wagner3989eb02019-03-19 11:05:37 +000080 Uri settingsUri = Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER);
81 context.getContentResolver()
82 .registerContentObserver(
83 settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM);
Misha Wagner4b32c9f2019-01-25 15:30:14 +000084 // Return the observer's reader
85 return settingsObserver.mKernelCpuThreadReader;
86 }
87
88 private KernelCpuThreadReaderSettingsObserver(Context context) {
89 super(BackgroundThread.getHandler());
90 mContext = context;
Misha Wagner3989eb02019-03-19 11:05:37 +000091 mKernelCpuThreadReader =
92 KernelCpuThreadReader.create(
93 NUM_BUCKETS_DEFAULT,
94 UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
95 MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
Misha Wagner4b32c9f2019-01-25 15:30:14 +000096 }
97
98 @Override
99 public void onChange(boolean selfChange, Uri uri, int userId) {
100 updateReader();
101 }
102
Misha Wagner3989eb02019-03-19 11:05:37 +0000103 /** Update the reader with new settings */
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000104 private void updateReader() {
105 if (mKernelCpuThreadReader == null) {
106 return;
107 }
108
109 final KeyValueListParser parser = new KeyValueListParser(',');
110 try {
Misha Wagner3989eb02019-03-19 11:05:37 +0000111 parser.setString(
112 Settings.Global.getString(
113 mContext.getContentResolver(),
114 Settings.Global.KERNEL_CPU_THREAD_READER));
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000115 } catch (IllegalArgumentException e) {
116 Slog.e(TAG, "Bad settings", e);
117 return;
118 }
119
120 final UidPredicate uidPredicate;
121 try {
Misha Wagner3989eb02019-03-19 11:05:37 +0000122 uidPredicate =
123 UidPredicate.fromString(
124 parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT));
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000125 } catch (NumberFormatException e) {
126 Slog.w(TAG, "Failed to get UID predicate", e);
127 return;
128 }
129
130 mKernelCpuThreadReader.setNumBuckets(
131 parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT));
132 mKernelCpuThreadReader.setUidPredicate(uidPredicate);
Misha Wagner3989eb02019-03-19 11:05:37 +0000133 mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(
134 parser.getInt(
135 MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
136 MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000137 }
138
Misha Wagner3989eb02019-03-19 11:05:37 +0000139 /** Check whether a UID belongs to a set of UIDs */
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000140 @VisibleForTesting
141 public static class UidPredicate implements Predicate<Integer> {
142 private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)");
143 private static final String UID_SPECIFIER_DELIMITER = ";";
144 private final List<Range<Integer>> mAcceptedUidRanges;
145
146 /**
147 * Create a UID predicate from a string representing a list of UID ranges
148 *
149 * <p>UID ranges are a pair of integers separated by a '-'. If you want to specify a single
Misha Wagner3989eb02019-03-19 11:05:37 +0000150 * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by a
151 * single ';'. For example, this would be a valid string representation: {@code
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000152 * "1000-1999;2003-2003;2004-2004;2050-2060"}.
153 *
154 * <p>We do not use ',' to delimit as it is already used in separating different setting
155 * arguments.
156 *
Misha Wagner3989eb02019-03-19 11:05:37 +0000157 * @throws NumberFormatException if the input string is incorrectly formatted
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000158 * @throws IllegalArgumentException if an UID range has a lower end than start
159 */
160 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
161 public static UidPredicate fromString(String predicateString) throws NumberFormatException {
162 final List<Range<Integer>> acceptedUidRanges = new ArrayList<>();
163 for (String uidSpecifier : predicateString.split(UID_SPECIFIER_DELIMITER)) {
164 final Matcher uidRangeMatcher = UID_RANGE_PATTERN.matcher(uidSpecifier);
165 if (!uidRangeMatcher.matches()) {
166 throw new NumberFormatException(
167 "Failed to recognize as number range: " + uidSpecifier);
168 }
Misha Wagner3989eb02019-03-19 11:05:37 +0000169 acceptedUidRanges.add(
170 Range.create(
171 Integer.parseInt(uidRangeMatcher.group(1)),
172 Integer.parseInt(uidRangeMatcher.group(2))));
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000173 }
174 return new UidPredicate(acceptedUidRanges);
175 }
176
177 private UidPredicate(List<Range<Integer>> acceptedUidRanges) {
178 mAcceptedUidRanges = acceptedUidRanges;
179 }
180
181 @Override
Misha Wagner3989eb02019-03-19 11:05:37 +0000182 @SuppressWarnings("ForLoopReplaceableByForEach")
Misha Wagner4b32c9f2019-01-25 15:30:14 +0000183 public boolean test(Integer uid) {
184 for (int i = 0; i < mAcceptedUidRanges.size(); i++) {
185 if (mAcceptedUidRanges.get(i).contains(uid)) {
186 return true;
187 }
188 }
189 return false;
190 }
191 }
192}