diff --git a/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java b/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java new file mode 100644 index 0000000000000..06b45bf0fb4b8 --- /dev/null +++ b/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 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.server.cpu; + +import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL; +import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_BACKGROUND; + +import com.android.internal.util.Preconditions; + +/** CPU availability information. */ +public final class CpuAvailabilityInfo { + /** Constant to indicate missing CPU availability percent. */ + public static final int MISSING_CPU_AVAILABILITY_PERCENT = -1; + + /** + * The CPUSET whose availability info is recorded in this object. + * + *
The contained value is one of the CPUSET_* constants from the + * {@link CpuAvailabilityMonitoringConfig}. + */ + @CpuAvailabilityMonitoringConfig.Cpuset + public final int cpuset; + + /** The latest average CPU availability percent. */ + public final int latestAvgAvailabilityPercent; + + /** The past N-second average CPU availability percent. */ + public final int pastNSecAvgAvailabilityPercent; + + /** The duration over which the {@link pastNSecAvgAvailabilityPercent} was calculated. */ + public final int avgAvailabilityDurationSec; + + @Override + public String toString() { + return "CpuAvailabilityInfo{" + "cpuset=" + cpuset + ", latestAvgAvailabilityPercent=" + + latestAvgAvailabilityPercent + ", pastNSecAvgAvailabilityPercent=" + + pastNSecAvgAvailabilityPercent + ", avgAvailabilityDurationSec=" + + avgAvailabilityDurationSec + '}'; + } + + CpuAvailabilityInfo(int cpuset, int latestAvgAvailabilityPercent, + int pastNSecAvgAvailabilityPercent, int avgAvailabilityDurationSec) { + this.cpuset = Preconditions.checkArgumentInRange(cpuset, CPUSET_ALL, CPUSET_BACKGROUND, + "cpuset"); + this.latestAvgAvailabilityPercent = latestAvgAvailabilityPercent; + this.pastNSecAvgAvailabilityPercent = pastNSecAvgAvailabilityPercent; + this.avgAvailabilityDurationSec = avgAvailabilityDurationSec; + } +} diff --git a/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java b/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java new file mode 100644 index 0000000000000..a3c4c9e828b4f --- /dev/null +++ b/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 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.server.cpu; + +import android.annotation.IntDef; +import android.util.IntArray; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** CPU availability monitoring config. */ +public final class CpuAvailabilityMonitoringConfig { + /** Constant to monitor all cpusets. */ + public static final int CPUSET_ALL = 1; + + /** Constant to monitor background cpusets. */ + public static final int CPUSET_BACKGROUND = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CPUSET_"}, value = { + CPUSET_ALL, + CPUSET_BACKGROUND + }) + public @interface Cpuset { + } + + /** + * The CPUSET to monitor. + * + *
The value must be one of the {@code CPUSET_*} constants. + */ + @Cpuset + public final int cpuset; + + /** + * CPU availability percent thresholds. + * + *
CPU availability change notifications are sent when the latest or last N seconds average + * CPU availability percent crosses any of these thresholds since the last notification. + */ + private final IntArray mThresholds; + + public IntArray getThresholds() { + return mThresholds; + } + + /** + * Builder for the construction of {@link CpuAvailabilityMonitoringConfig} objects. + * + *
The builder must contain at least one threshold before calling {@link build}. + */ + public static final class Builder { + private final int mCpuset; + private final IntArray mThresholds = new IntArray(); + + public Builder(int cpuset, int... thresholds) { + mCpuset = cpuset; + for (int threshold : thresholds) { + addThreshold(threshold); + } + } + + /** Adds the given threshold to the builder object. */ + public Builder addThreshold(int threshold) { + if (mThresholds.indexOf(threshold) == -1) { + mThresholds.add(threshold); + } + return this; + } + + /** Returns the {@link CpuAvailabilityMonitoringConfig} object. */ + public CpuAvailabilityMonitoringConfig build() { + return new CpuAvailabilityMonitoringConfig(this); + } + } + + @Override + public String toString() { + return "CpuAvailabilityMonitoringConfig{cpuset=" + cpuset + ", mThresholds=" + mThresholds + + ')'; + } + + private CpuAvailabilityMonitoringConfig(Builder builder) { + if (builder.mCpuset != CPUSET_ALL && builder.mCpuset != CPUSET_BACKGROUND) { + throw new IllegalStateException("Cpuset must be either CPUSET_ALL (" + CPUSET_ALL + + ") or CPUSET_BACKGROUND (" + CPUSET_BACKGROUND + "). Builder contains " + + builder.mCpuset); + } + if (builder.mThresholds.size() == 0) { + throw new IllegalStateException("Must provide at least one threshold"); + } + this.cpuset = builder.mCpuset; + this.mThresholds = builder.mThresholds.clone(); + } +} diff --git a/services/core/java/com/android/server/cpu/CpuMonitorInternal.java b/services/core/java/com/android/server/cpu/CpuMonitorInternal.java new file mode 100644 index 0000000000000..849a20be0cf85 --- /dev/null +++ b/services/core/java/com/android/server/cpu/CpuMonitorInternal.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 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.server.cpu; + +import android.annotation.CallbackExecutor; + +import java.util.concurrent.Executor; + +/** CpuMonitorInternal hosts internal APIs to monitor CPU. */ +public abstract class CpuMonitorInternal { + /** Callback to get CPU availability change notifications. */ + public interface CpuAvailabilityCallback { + /** + * Called when the CPU availability crosses the provided thresholds. + * + *
Called when the latest or past N-second (which will be specified in the + * {@link CpuAvailabilityInfo}) average CPU availability percent has crossed + * (either goes above or drop below) the {@link CpuAvailabilityMonitoringConfig#thresholds} + * since the last notification. Also called when a callback is added to the service. + * + *
The callback is called at the executor which is specified in + * {@link addCpuAvailabilityCallback} or at the service handler thread. + * + * @param info CPU availability information. + */ + void onAvailabilityChanged(CpuAvailabilityInfo info); + + /** + * Called when the CPU monitoring interval changes. + * + *
Also called when a callback is added to the service. + * + * @param intervalMilliseconds CPU monitoring interval in milliseconds. + */ + void onMonitoringIntervalChanged(long intervalMilliseconds); + } + + /** + * Adds the {@link CpuAvailabilityCallback} for the caller. + * + *
When the callback is added, the callback will be called to notify the current CPU + * availability and monitoring interval. + * + *
When the client needs to update the {@link config} for a previously added callback,
+ * the client has to remove the callback and add the callback with a new {@link config}.
+ *
+ * @param executor Executor to execute the callback. If an executor is not provided,
+ * the callback will be executed on the service handler thread.
+ * @param config CPU availability monitoring config.
+ * @param callback Callback implementing {@link CpuAvailabilityCallback}
+ * interface.
+ *
+ * @throws IllegalStateException if {@code callback} is already added.
+ */
+ public abstract void addCpuAvailabilityCallback(@CallbackExecutor Executor executor,
+ CpuAvailabilityMonitoringConfig config, CpuAvailabilityCallback callback);
+
+ /**
+ * Removes the {@link CpuAvailabilityCallback} for the caller.
+ *
+ * @param callback Callback implementing {@link CpuAvailabilityCallback}
+ * interface.
+ *
+ * @throws IllegalArgumentException if {@code callback} is not previously added.
+ */
+ public abstract void removeCpuAvailabilityCallback(CpuAvailabilityCallback callback);
+}
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorService.java b/services/core/java/com/android/server/cpu/CpuMonitorService.java
new file mode 100644
index 0000000000000..b0dfb8467fa76
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuMonitorService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 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.server.cpu;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+
+import android.content.Context;
+import android.os.Binder;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import com.android.server.utils.PriorityDump;
+import com.android.server.utils.Slogf;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** Service to monitor CPU availability and usage. */
+public final class CpuMonitorService extends SystemService {
+ static final String TAG = CpuMonitorService.class.getSimpleName();
+ static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
+ // TODO(b/242722241): Make this a resource overlay property.
+ // Maintain 3 monitoring intervals:
+ // * One to poll very frequently when mCpuAvailabilityCallbackInfoByCallbacks are available and
+ // CPU availability is above a threshold (such as at least 10% of CPU is available).
+ // * One to poll less frequently when mCpuAvailabilityCallbackInfoByCallbacks are available
+ // and CPU availability is below a threshold (such as less than 10% of CPU is available).
+ // * One to poll very less frequently when no callbacks are available and the build is either
+ // user-debug or eng. This will be useful for debugging in development environment.
+ static final int DEFAULT_CPU_MONITORING_INTERVAL_MILLISECONDS = 5_000;
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final ArrayMap