summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/android/surface_control_input_receiver.h9
-rw-r--r--include/android/system_health.h204
-rw-r--r--include/ftl/finalizer.h211
-rw-r--r--include/private/OWNERS1
4 files changed, 369 insertions, 56 deletions
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
index f0503f6324..59d3a31b30 100644
--- a/include/android/surface_control_input_receiver.h
+++ b/include/android/surface_control_input_receiver.h
@@ -39,11 +39,11 @@ __BEGIN_DECLS
*
* \param motionEvent The motion event. This must be released with AInputEvent_release.
*
+ * \return true if the event is handled by the client, false otherwise.
* Available since API level 35.
*/
typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context,
- AInputEvent *_Nonnull motionEvent)
- __INTRODUCED_IN(__ANDROID_API_V__);
+ AInputEvent *_Nonnull motionEvent);
/**
* The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives
* a key event.
@@ -53,11 +53,12 @@ typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context,
*
* \param keyEvent The key event. This must be released with AInputEvent_release.
*
+ * \return true if the event is handled by the client, false otherwise. System may generate
+ * a fallback key event if the event is not handled.
* Available since API level 35.
*/
typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context,
- AInputEvent *_Nonnull keyEvent)
- __INTRODUCED_IN(__ANDROID_API_V__);
+ AInputEvent *_Nonnull keyEvent);
typedef struct AInputReceiverCallbacks AInputReceiverCallbacks;
diff --git a/include/android/system_health.h b/include/android/system_health.h
index 352eb72b0c..6d59706490 100644
--- a/include/android/system_health.h
+++ b/include/android/system_health.h
@@ -16,6 +16,31 @@
/**
* @defgroup SystemHealth
+*
+ * SystemHealth provides access to data about how various system resources are used by applications.
+ *
+ * CPU/GPU headroom APIs are designed to be best used by applications with consistent and intense
+ * workload such as games to query the remaining capacity headroom over a short period and perform
+ * optimization accordingly. Due to the nature of the fast job scheduling and frequency scaling of
+ * CPU and GPU, the headroom by nature will have "TOCTOU" problem which makes it less suitable for
+ * apps with inconsistent or low workload to take any useful action but simply monitoring. And to
+ * avoid oscillation it's not recommended to adjust workload too frequent (on each polling request)
+ * or too aggressively. As the headroom calculation is more based on reflecting past history usage
+ * than predicting future capacity. Take game as an example, if the API returns CPU headroom of 0 in
+ * one scenario (especially if it's constant across multiple calls), or some value significantly
+ * smaller than other scenarios, then it can reason that the recent performance result is more CPU
+ * bottlenecked. Then reducing the CPU workload intensity can help reserve some headroom to handle
+ * the load variance better, which can result in less frame drops or smooth FPS value. On the other
+ * hand, if the API returns large CPU headroom constantly, the app can be more confident to increase
+ * the workload and expect higher possibility of device meeting its performance expectation.
+ * App can also use thermal APIs to read the current thermal status and headroom first, then poll
+ * the CPU and GPU headroom if the device is (about to) getting thermal throttled. If the CPU/GPU
+ * headrooms provide enough significance such as one valued at 0 while the other at 100, then it can
+ * be used to infer that reducing CPU workload could be more efficient to cool down the device.
+ * There is a caveat that the power controller may scale down the frequency of the CPU and GPU due
+ * to thermal and other reasons, which can result in a higher than usual percentage usage of the
+ * capacity.
+ *
* @{
*/
@@ -70,28 +95,14 @@ typedef struct ACpuHeadroomParams ACpuHeadroomParams;
*/
typedef struct AGpuHeadroomParams AGpuHeadroomParams;
-/**
- * Creates a new instance of ACpuHeadroomParams.
- *
- * When the client finishes using {@link ACpuHeadroomParams},
- * {@link ACpuHeadroomParams_destroy()} must be called to destroy
- * and free up the resources associated with {@link ACpuHeadroomParams}.
- *
- * Available since API level 36.
- *
- * @return A new instance of ACpuHeadroomParams.
- */
-ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void)
-__INTRODUCED_IN(36);
-
typedef enum ACpuHeadroomCalculationType : int32_t {
/**
- * Use the minimum headroom value within the calculation window.
+ * The headroom calculation type bases on minimum value over a specified window.
* Introduced in API level 36.
*/
ACPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
/**
- * Use the average headroom value within the calculation window.
+ * The headroom calculation type bases on average value over a specified window.
* Introduced in API level 36.
*/
ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
@@ -99,51 +110,55 @@ typedef enum ACpuHeadroomCalculationType : int32_t {
typedef enum AGpuHeadroomCalculationType : int32_t {
/**
- * Use the minimum headroom value within the calculation window.
+ * The headroom calculation type bases on minimum value over a specified window.
* Introduced in API level 36.
*/
AGPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
/**
- * Use the average headroom value within the calculation window.
+ * The headroom calculation type bases on average value over a specified window.
* Introduced in API level 36.
*/
AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
} AGpuHeadroomCalculationType;
/**
- * Sets the headroom calculation window size in ACpuHeadroomParams.
+ * Sets the CPU headroom calculation window size in milliseconds.
*
* Available since API level 36.
*
* @param params The params to be set.
- * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ * @param windowMillis The window size in milliseconds ranges from
+ * {@link ASystemHealth_getCpuHeadroomCalculationWindowRange}. The smaller the
* window size, the larger fluctuation in the headroom value should be expected.
* The default value can be retrieved from the
* {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device
* will try to use the closest feasible window size to this param.
*/
-void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params,
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params,
int windowMillis)
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation window size in ACpuHeadroomParams.
+ * Gets the CPU headroom calculation window size in milliseconds.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return This will return the default value chosen by the device if the params is not set.
*/
int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the headroom calculation window size in AGpuHeadroomParams.
+ * Sets the GPU headroom calculation window size in milliseconds.
*
* Available since API level 36.
*
* @param params The params to be set.
- * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ * @param windowMillis The window size in milliseconds ranges from
+ * {@link ASystemHealth_getGpuHeadroomCalculationWindowRange}. The smaller the
* window size, the larger fluctuation in the headroom value should be expected.
* The default value can be retrieved from the
* {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device
@@ -154,18 +169,20 @@ void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation window size in AGpuHeadroomParams.
+ * Gets the GPU headroom calculation window size in milliseconds.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return This will return the default value chosen by the device if the params is not set.
*/
int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the headroom calculation type in ACpuHeadroomParams.
+ * Sets the CPU headroom calculation type in {@link ACpuHeadroomParams}.
*
* Available since API level 36.
*
@@ -177,11 +194,13 @@ void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params,
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation type in ACpuHeadroomParams.
+ * Gets the CPU headroom calculation type in {@link ACpuHeadroomParams}.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return The headroom calculation type.
*/
ACpuHeadroomCalculationType
@@ -189,7 +208,7 @@ ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the headroom calculation type in AGpuHeadroomParams.
+ * Sets the GPU headroom calculation type in {@link AGpuHeadroomParams}.
*
* Available since API level 36.
*
@@ -201,11 +220,13 @@ void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params,
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation type in AGpuHeadroomParams.
+ * Gets the GPU headroom calculation type in {@link AGpuHeadroomParams}.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return The headroom calculation type.
*/
AGpuHeadroomCalculationType
@@ -213,50 +234,115 @@ AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the thread TIDs to track in ACpuHeadroomParams.
+ * Sets the thread TIDs to track in {@link ACpuHeadroomParams}.
+ *
+ * The TIDs should belong to the same of the process that will make the headroom call. And they
+ * should not have different core affinity.
+ *
+ * If not set or set to empty, the headroom will be based on the PID of the process making the call.
*
* Available since API level 36.
*
* @param params The params to be set.
- * @param tids Non-null array of TIDs, maximum 5.
+ * @param tids Non-null array of TIDs, where maximum size can be read from
+ * {@link ASystemHealth_getMaxCpuHeadroomTidsSize}.
* @param tidsSize The size of the tids array.
*/
void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
- int tidsSize)
+ size_t tidsSize)
__INTRODUCED_IN(36);
/**
- * Creates a new instance of AGpuHeadroomParams.
+ * Creates a new instance of {@link ACpuHeadroomParams}.
+ *
+ * When the client finishes using {@link ACpuHeadroomParams},
+ * {@link ACpuHeadroomParams_destroy} must be called to destroy
+ * and free up the resources associated with {@link ACpuHeadroomParams}.
+ *
+ * Available since API level 36.
+ *
+ * @return A new instance of {@link ACpuHeadroomParams}.
+ */
+ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void)
+__INTRODUCED_IN(36);
+
+/**
+ * Creates a new instance of {@link AGpuHeadroomParams}.
*
* When the client finishes using {@link AGpuHeadroomParams},
- * {@link AGpuHeadroomParams_destroy()} must be called to destroy
+ * {@link AGpuHeadroomParams_destroy} must be called to destroy
* and free up the resources associated with {@link AGpuHeadroomParams}.
*
* Available since API level 36.
*
- * @return A new instance of AGpuHeadroomParams.
+ * @return A new instance of {@link AGpuHeadroomParams}.
*/
AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create(void)
__INTRODUCED_IN(36);
/**
- * Deletes the ACpuHeadroomParams instance.
+ * Deletes the {@link ACpuHeadroomParams} instance.
*
* Available since API level 36.
*
* @param params The params to be deleted.
*/
-void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params)
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params)
__INTRODUCED_IN(36);
/**
- * Deletes the AGpuHeadroomParams instance.
+ * Deletes the {@link AGpuHeadroomParams} instance.
*
* Available since API level 36.
*
* @param params The params to be deleted.
*/
-void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params)
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the maximum number of TIDs this device supports for getting CPU headroom.
+ *
+ * See {@link ACpuHeadroomParams_setTids}.
+ *
+ * Available since API level 36.
+ *
+ * @param outSize Non-null output pointer to the max size.
+ * @return 0 on success.
+ * ENOTSUP if the CPU headroom API is unsupported.
+ */
+int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize);
+
+/**
+ * Gets the range of the calculation window size for CPU headroom.
+ *
+ * In API version 36, the range will be a superset of [50, 10000].
+ *
+ * Available since API level 36.
+ *
+ * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
+ * @param outMaxMillis Non-null output pointer to be set to the maximum window size in milliseconds.
+ * @return 0 on success.
+ * ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+ int32_t* _Nonnull outMaxMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the range of the calculation window size for GPU headroom.
+ *
+ * In API version 36, the range will be a superset of [50, 10000].
+ *
+ * Available since API level 36.
+ *
+ * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
+ * @param outMaxMillis Non-null output pointer to be set to the maximum window size in milliseconds.
+ * @return 0 on success.
+ * ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+ int32_t* _Nonnull outMaxMillis)
__INTRODUCED_IN(36);
/**
@@ -267,15 +353,21 @@ __INTRODUCED_IN(36);
* be used with the thermal status and headroom to determine if reducing the CPU bound workload can
* help reduce the device temperature to avoid thermal throttling.
*
+ * If the params are valid, each call will perform at least one synchronous binder transaction that
+ * can take more than 1ms. So it's not recommended to call or wait for this on critical threads.
+ * Some devices may implement this as an on-demand API with lazy initialization, so the caller
+ * should expect higher latency when making the first call (especially with non-default params)
+ * since app starts or after changing params, as the device may need to change its data collection.
+ *
* Available since API level 36.
*
- * @param params The params to customize the CPU headroom calculation, or nullptr to use the default
+ * @param params The params to customize the CPU headroom calculation, or nullptr to use default.
* @param outHeadroom Non-null output pointer to a single float, which will be set to the CPU
* headroom value. The value will be a single value or `Float.NaN` if it's
- * temporarily unavailable.
+ * temporarily unavailable due to server error or not enough user CPU workload.
* Each valid value ranges from [0, 100], where 0 indicates no more cpu resources
* can be granted.
- * @return 0 on success
+ * @return 0 on success.
* EPIPE if failed to get the CPU headroom.
* EPERM if the TIDs do not belong to the same process.
* ENOTSUP if API or requested params is unsupported.
@@ -292,15 +384,21 @@ __INTRODUCED_IN(36);
* be used with the thermal status and headroom to determine if reducing the GPU bound workload can
* help reduce the device temperature to avoid thermal throttling.
*
+ * If the params are valid, each call will perform at least one synchronous binder transaction that
+ * can take more than 1ms. So it's not recommended to call or wait for this on critical threads.
+ * Some devices may implement this as an on-demand API with lazy initialization, so the caller
+ * should expect higher latency when making the first call (especially with non-default params)
+ * since app starts or after changing params, as the device may need to change its data collection.
+ *
* Available since API level 36
*
- * @param params The params to customize the GPU headroom calculation, or nullptr to use the default
+ * @param params The params to customize the GPU headroom calculation, or nullptr to use default
* @param outHeadroom Non-null output pointer to a single float, which will be set to the GPU
* headroom value. The value will be a single value or `Float.NaN` if it's
* temporarily unavailable.
* Each valid value ranges from [0, 100], where 0 indicates no more gpu resources
* can be granted.
- * @return 0 on success
+ * @return 0 on success.
* EPIPE if failed to get the GPU headroom.
* ENOTSUP if API or requested params is unsupported.
*/
@@ -311,13 +409,14 @@ __INTRODUCED_IN(36);
/**
* Gets minimum polling interval for calling {@link ASystemHealth_getCpuHeadroom} in milliseconds.
*
- * The getCpuHeadroom API may return cached result if called more frequently than the interval.
+ * The {@link ASystemHealth_getCpuHeadroom} API may return cached result if called more frequently
+ * than the interval.
*
* Available since API level 36.
*
* @param outMinIntervalMillis Non-null output pointer to a int64_t, which
* will be set to the minimum polling interval in milliseconds.
- * @return 0 on success
+ * @return 0 on success.
* EPIPE if failed to get the minimum polling interval.
* ENOTSUP if API is unsupported.
*/
@@ -327,13 +426,14 @@ __INTRODUCED_IN(36);
/**
* Gets minimum polling interval for calling {@link ASystemHealth_getGpuHeadroom} in milliseconds.
*
- * The getGpuHeadroom API may return cached result if called more frequent than the interval.
+ * The {@link ASystemHealth_getGpuHeadroom} API may return cached result if called more frequently
+ * than the interval.
*
* Available since API level 36.
*
* @param outMinIntervalMillis Non-null output pointer to a int64_t, which
* will be set to the minimum polling interval in milliseconds.
- * @return 0 on success
+ * @return 0 on success.
* EPIPE if failed to get the minimum polling interval.
* ENOTSUP if API is unsupported.
*/
diff --git a/include/ftl/finalizer.h b/include/ftl/finalizer.h
new file mode 100644
index 0000000000..0251957ad0
--- /dev/null
+++ b/include/ftl/finalizer.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/function.h>
+
+namespace android::ftl {
+
+// An RAII wrapper that invokes a function object as a finalizer when destroyed.
+//
+// The function object must take no arguments, and must return void. If the function object needs
+// any context for the call, it must store it itself, for example with a lambda capture.
+//
+// The stored function object will be called once (unless canceled via the `cancel()` member
+// function) at the first of:
+//
+// - The Finalizer instance is destroyed.
+// - `operator()` is used to invoke the contained function.
+// - The Finalizer instance is move-assigned a new value. The function being replaced will be
+// invoked, and the replacement will be stored to be called later.
+//
+// The intent with this class is to keep cleanup code next to the code that requires that
+// cleanup be performed.
+//
+// bool read_file(std::string filename) {
+// FILE* f = fopen(filename.c_str(), "rb");
+// if (f == nullptr) return false;
+// const auto cleanup = ftl::Finalizer([f]() { fclose(f); });
+// // fread(...), etc
+// return true;
+// }
+//
+// The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function
+// type for storing the finalization function, such as `std::function` or `ftl::Function`.
+//
+// For convenience, this header defines a few useful aliases for using those types.
+//
+// - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>`
+// - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>`
+// - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>`
+// - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>`
+// - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>`
+//
+// Clients of this header are free to define other aliases they need.
+//
+// A Finalizer that uses a polymorphic function type can be returned from a function call and/or
+// stored as member data (to be destroyed along with the containing class).
+//
+// auto register(Observer* observer) -> ftl::FinalizerStd<void()> {
+// const auto id = observers.add(observer);
+// return ftl::Finalizer([id]() { observers.remove(id); });
+// }
+//
+// {
+// const auto _ = register(observer);
+// // do the things that required the registered observer.
+// }
+// // the observer is removed.
+//
+// Cautions:
+//
+// 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to
+// happen first, before the rest of the other member data is destroyed. For safety you should
+// assume that the finalization function will access that data directly or indirectly.
+//
+// This means that Finalizers should be defined last, after all other normal member data in a
+// class.
+//
+// class MyClass {
+// public:
+// bool initialize() {
+// ready_ = true;
+// cleanup_ = ftl::Finalizer([this]() { ready_ = false; });
+// return true;
+// }
+//
+// bool ready_ = false;
+//
+// // Finalizers should be last so other class members can be accessed before being
+// // destroyed.
+// ftl::FinalizerStd<void()> cleanup_;
+// };
+//
+// 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you
+// forget to do so, you are just creating a lambda that won't be automatically invoked!
+//
+// const auto bad = [&counter](){ ++counter; }; // Just a lambda instance
+// const auto good = ftl::Finalizer([&counter](){ ++counter; });
+//
+template <typename FinalFunction>
+class Finalizer final {
+ // requires(std::is_invocable_r_v<void, FinalFunction>)
+ static_assert(std::is_invocable_r_v<void, FinalFunction>);
+
+ public:
+ // A default constructed Finalizer does nothing when destroyed.
+ // requires(std::is_default_constructible_v<FinalFunction>)
+ constexpr Finalizer() = default;
+
+ // Constructs a Finalizer from a function object.
+ // requires(std::is_invocable_v<F>)
+ template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
+ [[nodiscard]] explicit constexpr Finalizer(F&& function)
+ : Finalizer(std::forward<F>(function), false) {}
+
+ constexpr ~Finalizer() { maybe_invoke(); }
+
+ // Disallow copying.
+ Finalizer(const Finalizer& that) = delete;
+ auto operator=(const Finalizer& that) = delete;
+
+ // Move construction
+ // requires(std::is_move_constructible_v<FinalFunction>)
+ [[nodiscard]] constexpr Finalizer(Finalizer&& that)
+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {}
+
+ // Implicit conversion move construction
+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
+ template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>>
+ // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved)
+ [[nodiscard]] constexpr Finalizer(Finalizer<F>&& that)
+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {}
+
+ // Move assignment
+ // requires(std::is_move_assignable_v<FinalFunction>)
+ constexpr auto operator=(Finalizer&& that) -> Finalizer& {
+ maybe_invoke();
+
+ function_ = std::move(that.function_);
+ canceled_ = std::exchange(that.canceled_, true);
+
+ return *this;
+ }
+
+ // Implicit conversion move assignment
+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
+ template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>>
+ // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
+ constexpr auto operator=(Finalizer<F>&& that) -> Finalizer& {
+ *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true));
+ return *this;
+ }
+
+ // Cancels the final function, preventing it from being invoked.
+ constexpr void cancel() {
+ canceled_ = true;
+ maybe_nullify_function();
+ }
+
+ // Invokes the final function now, if not already invoked.
+ constexpr void operator()() { maybe_invoke(); }
+
+ private:
+ template <typename>
+ friend class Finalizer;
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
+ [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled)
+ : function_(std::forward<F>(function)), canceled_(canceled) {}
+
+ constexpr void maybe_invoke() {
+ if (!std::exchange(canceled_, true)) {
+ std::invoke(function_);
+ maybe_nullify_function();
+ }
+ }
+
+ constexpr void maybe_nullify_function() {
+ // Sets function_ to nullptr if that is supported for the backing type.
+ if constexpr (std::is_assignable_v<FinalFunction, nullptr_t>) {
+ function_ = nullptr;
+ }
+ }
+
+ FinalFunction function_;
+ bool canceled_ = true;
+};
+
+template <typename F>
+Finalizer(F&&) -> Finalizer<std::decay_t<F>>;
+
+// A standard alias for using `std::function` as the polymorphic function type.
+using FinalizerStd = Finalizer<std::function<void()>>;
+
+// Helpful aliases for using `ftl::Function` as the polymorphic function type.
+using FinalizerFtl = Finalizer<Function<void()>>;
+using FinalizerFtl1 = Finalizer<Function<void(), 1>>;
+using FinalizerFtl2 = Finalizer<Function<void(), 2>>;
+using FinalizerFtl3 = Finalizer<Function<void(), 3>>;
+
+} // namespace android::ftl \ No newline at end of file
diff --git a/include/private/OWNERS b/include/private/OWNERS
index 37da96d488..db3ae48698 100644
--- a/include/private/OWNERS
+++ b/include/private/OWNERS
@@ -1,3 +1,4 @@
# ADPF
per-file thermal_private.h = file:platform/frameworks/base:/ADPF_OWNERS
per-file performance_hint_private.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file system_health_private.h = file:platform/frameworks/base:/ADPF_OWNERS