Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17a7c61a authored by Lakshman Annadorai's avatar Lakshman Annadorai Committed by Cherrypicker Worker
Browse files

Verify CPU availability against client thresholds and notify the clients.

Test: atest CpuMonitorServiceTest
Bug: 242722241
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:741709bf4a7d6087b34efd4bda82df5dba717f13)
Merged-In: I6c189b6880bf9fb5cd73e7cd31419d915bbaff61
Change-Id: I6c189b6880bf9fb5cd73e7cd31419d915bbaff61
parent ceab3a6e
Loading
Loading
Loading
Loading
+92 −5
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.os.HandlerThread;
import android.os.Process;
import android.os.SystemClock;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
@@ -51,6 +52,7 @@ import java.io.PrintWriter;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

/** Service to monitor CPU availability and usage. */
public final class CpuMonitorService extends SystemService {
@@ -92,6 +94,8 @@ public final class CpuMonitorService extends SystemService {
    @GuardedBy("mLock")
    private final SparseArray<CpusetInfo> mCpusetInfosByCpuset;
    private final Runnable mMonitorCpuStats = this::monitorCpuStats;
    private final NotifyCpuAvailabilityFunctor mNotifyCpuAvailabilityFunctor =
            new NotifyCpuAvailabilityFunctor();

    @GuardedBy("mLock")
    private long mCurrentMonitoringIntervalMillis = DEFAULT_MONITORING_INTERVAL_MILLISECONDS;
@@ -115,7 +119,7 @@ public final class CpuMonitorService extends SystemService {
                    }
                }
                CpuAvailabilityCallbackInfo callbackInfo = new CpuAvailabilityCallbackInfo(config,
                        executor);
                        callback, executor);
                mAvailabilityCallbackInfosByCallbacksByCpuset.add(config.cpuset, callback,
                        callbackInfo);
                if (DEBUG) {
@@ -254,7 +258,7 @@ public final class CpuMonitorService extends SystemService {
                CpusetInfo cpusetInfo = mCpusetInfosByCpuset.valueAt(i);
                cpusetInfo.populateLatestCpuAvailabilityInfo(uptimeMillis,
                        mLatestAvailabilityDurationMillis);
                // TODO(b/242722241): Check CPU availability against thresholds and notify clients.
                checkClientThresholdsAndNotifyLocked(cpusetInfo);
            }

            // TODO(b/267500110): Detect heavy CPU load. On detecting heavy CPU load, increase
@@ -272,6 +276,41 @@ public final class CpuMonitorService extends SystemService {
        }
    }

    @GuardedBy("mLock")
    private void checkClientThresholdsAndNotifyLocked(CpusetInfo cpusetInfo) {
        int prevAvailabilityPercent = cpusetInfo.getPrevCpuAvailabilityPercent();
        CpuAvailabilityInfo latestAvailabilityInfo = cpusetInfo.getLatestCpuAvailabilityInfo();
        ArrayMap<CpuMonitorInternal.CpuAvailabilityCallback,
                CpuAvailabilityCallbackInfo> callbackMap =
                mAvailabilityCallbackInfosByCallbacksByCpuset.get(cpusetInfo.cpuset);
        if (latestAvailabilityInfo == null || prevAvailabilityPercent < 0
                || callbackMap.isEmpty()) {
            // When either the current or the previous CPU availability percents are
            // missing, skip the current cpuset as there is not enough data to verify
            // whether the CPU availability has crossed any monitoring threshold.
            return;
        }
        for (int i = 0; i < callbackMap.size(); i++) {
            CpuAvailabilityCallbackInfo callbackInfo = callbackMap.valueAt(i);
            if (didCrossAnyThreshold(prevAvailabilityPercent,
                    latestAvailabilityInfo.latestAvgAvailabilityPercent,
                    callbackInfo.config.getThresholds())) {
                asyncNotifyCpuAvailabilityToClient(latestAvailabilityInfo, callbackInfo);
            }
        }
    }

    private void asyncNotifyCpuAvailabilityToClient(CpuAvailabilityInfo availabilityInfo,
            CpuAvailabilityCallbackInfo callbackInfo) {
        if (callbackInfo.executor == null) {
            mHandler.post(() -> mNotifyCpuAvailabilityFunctor.accept(callbackInfo.callback,
                    availabilityInfo));
        } else {
            callbackInfo.executor.execute(() -> mNotifyCpuAvailabilityFunctor.accept(
                    callbackInfo.callback, availabilityInfo));
        }
    }

    @GuardedBy("mLock")
    private boolean hasClientCallbacksLocked() {
        for (int i = 0; i < mAvailabilityCallbackInfosByCallbacksByCpuset.numMaps(); i++) {
@@ -306,21 +345,47 @@ public final class CpuMonitorService extends SystemService {
        return false;
    }

    private static boolean didCrossAnyThreshold(int prevAvailabilityPercent,
            int curAvailabilityPercent, IntArray thresholds) {
        if (prevAvailabilityPercent == curAvailabilityPercent) {
            return false;
        }
        for (int i = 0; i < thresholds.size(); i++) {
            int threshold = thresholds.get(i);
            // TODO(b/267500110): Identify whether or not the clients need to be notified when
            //  the CPU availability jumps too frequently around the provided thresholds.
            //  A. Should the client be notified twice - once when the availability reaches
            //     the threshold and once when it moves away (increase/decrease) from the threshold
            //     immediately?
            //  B. Should there be some sort of rate-limiting to avoid notifying the client too
            //     frequently? Should the client be able to config the rate-limit?
            if (prevAvailabilityPercent < threshold && curAvailabilityPercent >= threshold) {
                return true;
            }
            if (prevAvailabilityPercent >= threshold && curAvailabilityPercent < threshold) {
                return true;
            }
        }
        return false;
    }

    private static final class CpuAvailabilityCallbackInfo {
        public final CpuAvailabilityMonitoringConfig config;
        public final CpuMonitorInternal.CpuAvailabilityCallback callback;
        @Nullable
        public final Executor executor;

        CpuAvailabilityCallbackInfo(CpuAvailabilityMonitoringConfig config,
                @Nullable Executor executor) {
                CpuMonitorInternal.CpuAvailabilityCallback callback, @Nullable Executor executor) {
            this.config = config;
            this.callback = callback;
            this.executor = executor;
        }

        @Override
        public String toString() {
            return "CpuAvailabilityCallbackInfo{" + "config=" + config + ", mExecutor=" + executor
                    + '}';
            return "CpuAvailabilityCallbackInfo{config = " + config + ", callback = " + callback
                    + ", mExecutor = " + executor + '}';
        }
    }

@@ -375,6 +440,11 @@ public final class CpuMonitorService extends SystemService {
            currentSnapshot.appendCpuInfo(cpuInfo);
        }

        @Nullable
        public CpuAvailabilityInfo getLatestCpuAvailabilityInfo() {
            return mLatestCpuAvailabilityInfo;
        }

        public void populateLatestCpuAvailabilityInfo(long currentUptimeMillis,
                long latestAvailabilityDurationMillis) {
            int numSnapshots = mSnapshotsByUptime.size();
@@ -407,6 +477,14 @@ public final class CpuMonitorService extends SystemService {
                    latestAvailabilityDurationMillis);
        }

        public int getPrevCpuAvailabilityPercent() {
            int numSnapshots = mSnapshotsByUptime.size();
            if (numSnapshots < 2) {
                return -1;
            }
            return mSnapshotsByUptime.valueAt(numSnapshots - 2).getAverageAvailableCpuFreqPercent();
        }

        private int getCumulativeAvgAvailabilityPercent(long earliestUptimeMillis) {
            long totalAvailableCpuFreqKHz = 0;
            long totalOnlineMaxCpuFreqKHz = 0;
@@ -485,4 +563,13 @@ public final class CpuMonitorService extends SystemService {
            }
        }
    }

    private static final class NotifyCpuAvailabilityFunctor implements
            BiConsumer<CpuMonitorInternal.CpuAvailabilityCallback, CpuAvailabilityInfo> {
        @Override
        public void accept(CpuMonitorInternal.CpuAvailabilityCallback callback,
                CpuAvailabilityInfo availabilityInfo) {
            callback.onAvailabilityChanged(availabilityInfo);
        }
    }
}