Merge "Add remaining tone mapping support in Skia-RenderEngine"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 560459b..a8c0500 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -14,6 +14,7 @@
libs/graphicsenv/
libs/gui/
libs/input/
+ libs/nativedisplay/
libs/renderengine/
libs/ui/
libs/vr/
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index a5e6c68..85e6969 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -72,5 +72,5 @@
/**
* Called when ui intensive bugreport dumps are finished.
*/
- oneway void onUiIntensiveBugreportDumpsFinished(String callingPackage);
+ oneway void onUiIntensiveBugreportDumpsFinished();
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a81cdaf..8bdde62 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1472,6 +1472,8 @@
RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"},
CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS CONNECTIVITY REQUESTS", {"connectivity", "requests"},
+ CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -2892,17 +2894,17 @@
// TODO(b/158737089) reduce code repetition in if branches
if (options_->telephony_only) {
MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+ onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly(calling_package);
} else if (options_->wifi_only) {
MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+ onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateWifiOnly();
} else if (options_->limited_only) {
MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+ onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateLimitedOnly();
} else {
@@ -2911,7 +2913,7 @@
// Take screenshot and get consent only after critical dumpsys has finished.
MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+ onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
// Dump state for the default case. This also drops root.
@@ -3001,16 +3003,14 @@
TakeScreenshot();
}
-void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid,
- const std::string& calling_package) {
+void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
if (calling_uid == AID_SHELL || !CalledByApi()) {
return;
}
if (listener_ != nullptr) {
// Let listener know ui intensive bugreport dumps are finished, then it can do event
// handling if required.
- android::String16 package(calling_package.c_str());
- listener_->onUiIntensiveBugreportDumpsFinished(package);
+ listener_->onUiIntensiveBugreportDumpsFinished();
}
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 9582c9d..3b9b1b7 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -549,8 +549,7 @@
void MaybeTakeEarlyScreenshot();
- void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid,
- const std::string& calling_package);
+ void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 1c6583e..70bdbcc 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -173,12 +173,9 @@
return binder::Status::ok();
}
- binder::Status onUiIntensiveBugreportDumpsFinished(const android::String16& callingpackage)
- override {
+ binder::Status onUiIntensiveBugreportDumpsFinished() override {
std::lock_guard <std::mutex> lock(lock_);
- std::string callingpackageUtf8 = std::string(String8(callingpackage).string());
- dprintf(out_fd_, "\rCalling package of ui intensive bugreport dumps finished: %s",
- callingpackageUtf8.c_str());
+ dprintf(out_fd_, "\rUi intensive bugreport dumps finished");
return binder::Status::ok();
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 6b93692..fdeea24 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -68,8 +68,7 @@
MOCK_METHOD1(onError, binder::Status(int32_t error_code));
MOCK_METHOD0(onFinished, binder::Status());
MOCK_METHOD1(onScreenshotTaken, binder::Status(bool success));
- MOCK_METHOD1(onUiIntensiveBugreportDumpsFinished,
- binder::Status(const android::String16& callingpackage));
+ MOCK_METHOD0(onUiIntensiveBugreportDumpsFinished, binder::Status());
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f583c9b..594880a 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -450,16 +450,22 @@
AddArg("--boot-image-merge");
}
+ // The percent won't exceed 100, otherwise, don't set it and use the
+ // default one set in profman.
uint32_t min_new_classes_percent_change = ::android::base::GetUintProperty<uint32_t>(
- "dalvik.vm.bgdexopt.new-classes-percent", /*default*/-1);
- if (min_new_classes_percent_change >= 0 && min_new_classes_percent_change <= 100) {
+ "dalvik.vm.bgdexopt.new-classes-percent",
+ /*default*/std::numeric_limits<uint32_t>::max());
+ if (min_new_classes_percent_change <= 100) {
AddArg("--min-new-classes-percent-change=" +
std::to_string(min_new_classes_percent_change));
}
+ // The percent won't exceed 100, otherwise, don't set it and use the
+ // default one set in profman.
uint32_t min_new_methods_percent_change = ::android::base::GetUintProperty<uint32_t>(
- "dalvik.vm.bgdexopt.new-methods-percent", /*default*/-1);
- if (min_new_methods_percent_change >=0 && min_new_methods_percent_change <= 100) {
+ "dalvik.vm.bgdexopt.new-methods-percent",
+ /*default*/std::numeric_limits<uint32_t>::max());
+ if (min_new_methods_percent_change <= 100) {
AddArg("--min-new-methods-percent-change=" +
std::to_string(min_new_methods_percent_change));
}
diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml
index 5defb2f..480e0ec 100644
--- a/data/etc/cec_config.xml
+++ b/data/etc/cec_config.xml
@@ -30,7 +30,7 @@
</setting>
<setting name="power_state_change_on_active_source_lost"
value-type="string"
- user-configurable="false">
+ user-configurable="true">
<allowed-values>
<value string-value="none" />
<value string-value="standby_now" />
@@ -39,7 +39,7 @@
</setting>
<setting name="system_audio_mode_muting"
value-type="int"
- user-configurable="false">
+ user-configurable="true">
<allowed-values>
<value int-value="0" />
<value int-value="1" />
diff --git a/include/android/input.h b/include/android/input.h
index b04775b..38af89a 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -163,6 +163,9 @@
/** Focus event */
AINPUT_EVENT_TYPE_FOCUS = 3,
+
+ /** Capture event */
+ AINPUT_EVENT_TYPE_CAPTURE = 4,
};
/**
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index cbcf6ec..7a74248 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -48,7 +48,7 @@
/**
* Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
- * |debug_name| is a debug name associated with this surface. It can be used to
+ * \a debug_name is a debug name associated with this surface. It can be used to
* identify this surface in the SurfaceFlinger's layer tree. It must not be
* null.
*
@@ -69,7 +69,7 @@
__INTRODUCED_IN(29);
/**
- * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
+ * Releases the \a surface_control object. After releasing the ASurfaceControl the caller no longer
* has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
* as their parent remains on display.
*
@@ -87,21 +87,21 @@
/**
* The caller takes ownership of the transaction and must release it using
- * ASurfaceControl_delete below.
+ * ASurfaceTransaction_delete() below.
*
* Available since API level 29.
*/
ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
/**
- * Destroys the |transaction| object.
+ * Destroys the \a transaction object.
*
* Available since API level 29.
*/
void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
/**
- * Applies the updates accumulated in |transaction|.
+ * Applies the updates accumulated in \a transaction.
*
* Note that the transaction is guaranteed to be applied atomically. The
* transactions which are applied on the same thread are also guaranteed to be
@@ -123,10 +123,10 @@
* ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
* including the updates in a transaction was presented.
*
- * |context| is the optional context provided by the client that is passed into
+ * \param context Optional context provided by the client that is passed into
* the callback.
*
- * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
* information about the transaction. The handle is only valid during the callback.
*
* THREADING
@@ -157,14 +157,14 @@
__INTRODUCED_IN(29);
/**
- * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * \a outASurfaceControls returns an array of ASurfaceControl pointers that were updated during the
* transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
* When the client is done using the array, it must release it by calling
* ASurfaceTransactionStats_releaseASurfaceControls.
*
* Available since API level 29.
*
- * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ * \a outASurfaceControlsSize returns the size of the ASurfaceControls array.
*/
void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
ASurfaceControl*** outASurfaceControls,
@@ -172,7 +172,7 @@
__INTRODUCED_IN(29);
/**
* Releases the array of ASurfaceControls that were returned by
- * ASurfaceTransactionStats_getASurfaceControls.
+ * ASurfaceTransactionStats_getASurfaceControls().
*
* Available since API level 29.
*/
@@ -197,8 +197,8 @@
* buffer is already released. The recipient of the callback takes ownership of the
* previousReleaseFenceFd and is responsible for closing it.
*
- * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
- * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer() on a transaction
+ * which is applied, the framework takes a ref on this buffer. The framework treats the
* addition of a buffer to a particular surface as a unique ref. When a transaction updates or
* removes a buffer from a surface, or removes the surface itself from the tree, this ref is
* guaranteed to be released in the OnComplete callback for this transaction. The
@@ -226,10 +226,10 @@
ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
/**
- * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
- * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ * Reparents the \a surface_control from its old parent to the \a new_parent surface control.
+ * Any children of the reparented \a surface_control will remain children of the \a surface_control.
*
- * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ * The \a new_parent can be null. Surface controls with a null parent do not appear on the display.
*
* Available since API level 29.
*/
@@ -237,14 +237,16 @@
ASurfaceControl* surface_control, ASurfaceControl* new_parent)
__INTRODUCED_IN(29);
-/* Parameter for ASurfaceTransaction_setVisibility */
+/**
+ * Parameter for ASurfaceTransaction_setVisibility().
+ */
enum {
ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
};
/**
- * Updates the visibility of |surface_control|. If show is set to
- * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
+ * Updates the visibility of \a surface_control. If show is set to
+ * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the \a surface_control and all surfaces in its subtree will
* be hidden.
*
* Available since API level 29.
@@ -254,7 +256,7 @@
__INTRODUCED_IN(29);
/**
- * Updates the z order index for |surface_control|. Note that the z order for a surface
+ * Updates the z order index for \a surface_control. Note that the z order for a surface
* is relative to other surfaces which are siblings of this surface. The behavior of sibilings with
* the same z order is undefined.
*
@@ -267,11 +269,11 @@
__INTRODUCED_IN(29);
/**
- * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
+ * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the
* acquire_fence_fd should be a file descriptor that is signaled when all pending work
* for the buffer is complete and the buffer can be safely read.
*
- * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
+ * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
* for closing it.
*
* Available since API level 29.
@@ -281,9 +283,9 @@
int acquire_fence_fd = -1) __INTRODUCED_IN(29);
/**
- * Updates the color for |surface_control|. This will make the background color for the
- * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|,
- * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha|
+ * Updates the color for \a surface_control. This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g,
+ * and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha
* will be the dataspace and alpha set for the background color layer.
*
* Available since API level 29.
@@ -294,15 +296,15 @@
__INTRODUCED_IN(29);
/**
- * |source| the sub-rect within the buffer's content to be rendered inside the surface's area
+ * \param source The sub-rect within the buffer's content to be rendered inside the surface's area
* The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
* and height must be > 0.
*
- * |destination| specifies the rect in the parent's space where this surface will be drawn. The post
+ * \param destination Specifies the rect in the parent's space where this surface will be drawn. The post
* source rect bounds are scaled to fit the destination rect. The surface's destination rect is
* clipped by the bounds of its parent. The destination rect's width and height must be > 0.
*
- * |transform| the transform applied after the source rect is applied to the buffer. This parameter
+ * \param transform The transform applied after the source rect is applied to the buffer. This parameter
* should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
* enum.
*
@@ -314,7 +316,9 @@
__INTRODUCED_IN(29);
-/* Parameter for ASurfaceTransaction_setBufferTransparency */
+/**
+ * Parameter for ASurfaceTransaction_setBufferTransparency().
+ */
enum {
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
@@ -360,7 +364,7 @@
/**
* Sets the alpha for the buffer. It uses a premultiplied blending.
*
- * The |alpha| must be between 0.0 and 1.0.
+ * The \a alpha must be between 0.0 and 1.0.
*
* Available since API level 29.
*/
@@ -379,10 +383,10 @@
ASurfaceControl* surface_control, ADataSpace data_space)
__INTRODUCED_IN(29);
-/*
+/**
* SMPTE ST 2086 "Mastering Display Color Volume" static metadata
*
- * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * When \a metadata is set to null, the framework does not use any smpte2086 metadata when rendering
* the surface's buffer.
*
* Available since API level 29.
@@ -392,10 +396,10 @@
struct AHdrMetadata_smpte2086* metadata)
__INTRODUCED_IN(29);
-/*
+/**
* Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
*
- * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * When \a metadata is set to null, the framework does not use any cta861.3 metadata when rendering
* the surface's buffer.
*
* Available since API level 29.
@@ -410,24 +414,10 @@
#if __ANDROID_API__ >= 30
/**
- * Sets the intended frame rate for |surface_control|.
+ * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control,
+ * frameRate, compatibility, true).
*
- * On devices that are capable of running the display at different refresh rates, the system may
- * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
- * directly affect the application's frame production pipeline. However, because the system may
- * change the display refresh rate, calls to this function may result in changes to Choreographer
- * callback timings, and changes to the time interval at which the system releases buffers back to
- * the application.
- *
- * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
- * value that indicates the app will accept the system's choice for the display frame rate, which is
- * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
- * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
- * that can only run the display at 60fps.
- *
- * |compatibility| The frame rate compatibility of this surface. The compatibility value may
- * influence the system's choice of display frame rate. To specify a compatibility use the
- * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ * See ASurfaceTransaction_setFrameRateWithSeamlessness().
*
* Available since API level 30.
*/
@@ -437,6 +427,42 @@
#endif // __ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Sets the intended frame rate for \a surface_control.
+ *
+ * On devices that are capable of running the display at different refresh rates, the system may
+ * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
+ * directly affect the application's frame production pipeline. However, because the system may
+ * change the display refresh rate, calls to this function may result in changes to Choreographer
+ * callback timings, and changes to the time interval at which the system releases buffers back to
+ * the application.
+ *
+ * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
+ * value that indicates the app will accept the system's choice for the display frame rate, which is
+ * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
+ * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
+ * that can only run the display at 60fps.
+ *
+ * \param compatibility The frame rate compatibility of this surface. The compatibility value may
+ * influence the system's choice of display frame rate. To specify a compatibility use the
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ *
+ * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
+ * seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. True indicates that any frame rate changes caused by this
+ * request should be seamless. False indicates that non-seamless refresh rates are also
+ * acceptable.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float frameRate,
+ int8_t compatibility, bool shouldBeSeamless)
+ __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
index 28f717a..ff685c5 100644
--- a/include/ftl/ArrayTraits.h
+++ b/include/ftl/ArrayTraits.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <iterator>
#include <new>
+#include <type_traits>
#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
@@ -40,11 +41,16 @@
using const_iterator = const_pointer;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
- // TODO: Replace with std::construct_at in C++20.
template <typename... Args>
static pointer construct_at(const_iterator it, Args&&... args) {
void* const ptr = const_cast<void*>(static_cast<const void*>(it));
- return new (ptr) value_type{std::forward<Args>(args)...};
+ if constexpr (std::is_constructible_v<value_type, Args...>) {
+ // TODO: Replace with std::construct_at in C++20.
+ return new (ptr) value_type(std::forward<Args>(args)...);
+ } else {
+ // Fall back to list initialization.
+ return new (ptr) value_type{std::forward<Args>(args)...};
+ }
}
};
diff --git a/include/ftl/InitializerList.h b/include/ftl/InitializerList.h
new file mode 100644
index 0000000..bb99280
--- /dev/null
+++ b/include/ftl/InitializerList.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 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 <tuple>
+#include <utility>
+
+namespace android::ftl {
+
+// Compile-time counterpart of std::initializer_list<T> that stores per-element constructor
+// arguments with heterogeneous types. For a container with elements of type T, given Sizes
+// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the
+// first S0 arguments, the second element is initialized with the next S1 arguments, and so
+// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes.
+//
+// The InitializerList is created using ftl::init::list, and is consumed by constructors of
+// containers. The function call operator is overloaded such that arguments are accumulated
+// in a tuple with each successive call. For instance, the following calls initialize three
+// strings using different constructors, i.e. string literal, default, and count/character:
+//
+// ... = ftl::init::list<std::string>("abc")()(3u, '?');
+//
+// The following syntax is a shorthand for key-value pairs, where the first argument is the
+// key, and the rest construct the value. The types of the key and value are deduced if the
+// first pair contains exactly two arguments:
+//
+// ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?');
+//
+// ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+//
+// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed
+// immediately, since temporary arguments are destroyed after the full expression. Storing
+// an InitializerList results in dangling references.
+//
+template <typename T, typename Sizes = std::index_sequence<>, typename... Types>
+struct InitializerList;
+
+template <typename T, size_t... Sizes, typename... Types>
+struct InitializerList<T, std::index_sequence<Sizes...>, Types...> {
+ // Creates a superset InitializerList by appending the number of arguments to Sizes, and
+ // expanding Types with forwarding references for each argument.
+ template <typename... Args>
+ [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList<
+ T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> {
+ return {std::tuple_cat(std::move(tuple),
+ std::forward_as_tuple(std::forward<Args>(args)...))};
+ }
+
+ // The temporary InitializerList returned by operator() is bound to an rvalue reference in
+ // container constructors, which extends the lifetime of any temporary arguments that this
+ // tuple refers to until the completion of the full expression containing the construction.
+ std::tuple<Types...> tuple;
+};
+
+template <typename K, typename V>
+struct KeyValue {};
+
+// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
+// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
+// with the latter.
+template <typename K, typename V, size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+ // Accumulate the three arguments to std::pair's piecewise constructor.
+ template <typename... Args>
+ [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
+ KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+ std::tuple<K&&>, std::tuple<Args&&...>> {
+ return {std::tuple_cat(std::move(tuple),
+ std::forward_as_tuple(std::piecewise_construct,
+ std::forward_as_tuple(std::forward<K>(k)),
+ std::forward_as_tuple(
+ std::forward<Args>(args)...)))};
+ }
+
+ std::tuple<Types...> tuple;
+};
+
+namespace init {
+
+template <typename T, typename... Args>
+[[nodiscard]] constexpr auto list(Args&&... args) {
+ return InitializerList<T>{}(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V, typename... Args>
+[[nodiscard]] constexpr auto map(Args&&... args) {
+ return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V>
+[[nodiscard]] constexpr auto map(K&& k, V&& v) {
+ return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v));
+}
+
+} // namespace init
+} // namespace android::ftl
diff --git a/include/ftl/SmallMap.h b/include/ftl/SmallMap.h
new file mode 100644
index 0000000..87ae99c
--- /dev/null
+++ b/include/ftl/SmallMap.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2020 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 <ftl/InitializerList.h>
+#include <ftl/SmallVector.h>
+
+#include <functional>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
+// stored in contiguous storage for cache efficiency. The map is allocated statically until its size
+// exceeds N, at which point mappings are relocated to dynamic memory.
+//
+// SmallMap<K, V, 0> unconditionally allocates on the heap.
+//
+// Example usage:
+//
+// ftl::SmallMap<int, std::string, 3> map;
+// assert(map.empty());
+// assert(!map.dynamic());
+//
+// map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+// assert(map.size() == 3u);
+// assert(!map.dynamic());
+//
+// assert(map.contains(123));
+// assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+//
+// const auto opt = map.find(-1);
+// assert(opt);
+//
+// std::string& ref = *opt;
+// assert(ref.empty());
+// ref = "xyz";
+//
+// assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+//
+template <typename K, typename V, size_t N>
+class SmallMap final {
+ using Map = SmallVector<std::pair<const K, V>, N>;
+
+public:
+ using key_type = K;
+ using mapped_type = V;
+
+ using value_type = typename Map::value_type;
+ using size_type = typename Map::size_type;
+ using difference_type = typename Map::difference_type;
+
+ using reference = typename Map::reference;
+ using iterator = typename Map::iterator;
+
+ using const_reference = typename Map::const_reference;
+ using const_iterator = typename Map::const_iterator;
+
+ // Creates an empty map.
+ SmallMap() = default;
+
+ // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments.
+ // The template arguments K, V, and N are inferred using the deduction guide defined below.
+ // The syntax for listing pairs is as follows:
+ //
+ // ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+ //
+ // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
+ // assert(map.size() == 3u);
+ // assert(map.contains(-1) && map.find(-1)->get().empty());
+ // assert(map.contains(42) && map.find(42)->get() == "???");
+ // assert(map.contains(123) && map.find(123)->get() == "abc");
+ //
+ // The types of the key and value are deduced if the first pair contains exactly two arguments:
+ //
+ // ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+ // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, char, 3>>);
+ //
+ template <typename U, size_t... Sizes, typename... Types>
+ SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& init)
+ : mMap(std::move(init)) {
+ // TODO: Enforce unique keys.
+ }
+
+ size_type max_size() const { return mMap.max_size(); }
+ size_type size() const { return mMap.size(); }
+ bool empty() const { return mMap.empty(); }
+
+ // Returns whether the map is backed by static or dynamic storage.
+ bool dynamic() const { return mMap.dynamic(); }
+
+ iterator begin() { return mMap.begin(); }
+ const_iterator begin() const { return cbegin(); }
+ const_iterator cbegin() const { return mMap.cbegin(); }
+
+ iterator end() { return mMap.end(); }
+ const_iterator end() const { return cend(); }
+ const_iterator cend() const { return mMap.cend(); }
+
+ // Returns whether a mapping exists for the given key.
+ bool contains(const key_type& key) const {
+ return find(key, [](const mapped_type&) {});
+ }
+
+ // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
+ //
+ // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+ //
+ // const auto opt = map.find('c');
+ // assert(opt == 'C');
+ //
+ // char d = 'd';
+ // const auto ref = map.find('d').value_or(std::ref(d));
+ // ref.get() = 'D';
+ // assert(d == 'D');
+ //
+ auto find(const key_type& key) const
+ -> std::optional<std::reference_wrapper<const mapped_type>> {
+ return find(key, [](const mapped_type& v) { return std::cref(v); });
+ }
+
+ auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+ return find(key, [](mapped_type& v) { return std::ref(v); });
+ }
+
+ // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
+ // for the given key, or std::nullopt if the key was not found. If F has a return type of void,
+ // then the Boolean result indicates whether the key was found.
+ //
+ // ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ //
+ // assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
+ // assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+ //
+ template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
+ auto find(const key_type& key, F f) const
+ -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
+ for (auto& [k, v] : *this) {
+ if (k == key) {
+ if constexpr (std::is_void_v<R>) {
+ f(v);
+ return true;
+ } else {
+ return f(v);
+ }
+ }
+ }
+
+ return {};
+ }
+
+ template <typename F>
+ auto find(const key_type& key, F f) {
+ return std::as_const(*this).find(key, [&f](const mapped_type& v) {
+ return f(const_cast<mapped_type&>(v));
+ });
+ }
+
+private:
+ Map mMap;
+};
+
+// Deduction guide for in-place constructor.
+template <typename K, typename V, size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
+ -> SmallMap<K, V, sizeof...(Sizes)>;
+
+// Returns whether the key-value pairs of two maps are equal.
+template <typename K, typename V, size_t N, typename Q, typename W, size_t M>
+bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+
+ for (const auto& [k, v] : lhs) {
+ const auto& lv = v;
+ if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// TODO: Remove in C++20.
+template <typename K, typename V, size_t N, typename Q, typename W, size_t M>
+inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
index cecec7f..2f05a9b 100644
--- a/include/ftl/SmallVector.h
+++ b/include/ftl/SmallVector.h
@@ -64,6 +64,16 @@
// assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
// assert(!vector.dynamic());
//
+// ftl::SmallVector strings = ftl::init::list<std::string>("abc")
+// ("123456", 3u)
+// (3u, '?');
+// assert(strings.size() == 3u);
+// assert(!strings.dynamic());
+//
+// assert(strings[0] == "abc");
+// assert(strings[1] == "123");
+// assert(strings[2] == "???");
+//
template <typename T, size_t N>
class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
using Static = StaticVector<T, N>;
@@ -365,8 +375,9 @@
SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
// Deduction guide for in-place constructor.
-template <typename T, typename... Us>
-SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>;
+template <typename T, size_t... Sizes, typename... Types>
+SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+ -> SmallVector<T, sizeof...(Sizes)>;
// Deduction guide for StaticVector conversion.
template <typename T, size_t N>
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
index 457095d..c132556 100644
--- a/include/ftl/StaticVector.h
+++ b/include/ftl/StaticVector.h
@@ -17,6 +17,7 @@
#pragma once
#include <ftl/ArrayTraits.h>
+#include <ftl/InitializerList.h>
#include <algorithm>
#include <cassert>
@@ -32,7 +33,7 @@
// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
-// default constructor, since elements are constructed in-place as the vector grows. Operations that
+// default constructor, since elements are constructed in place as the vector grows. Operations that
// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
// adheres to standard containers, except the unstable_erase operation that does not preserve order,
// and the replace operation that destructively emplaces.
@@ -63,6 +64,14 @@
// vector = ftl::StaticVector(array);
// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
//
+// ftl::StaticVector strings = ftl::init::list<std::string>("abc")
+// ("123456", 3u)
+// (3u, '?');
+// assert(strings.size() == 3u);
+// assert(strings[0] == "abc");
+// assert(strings[1] == "123");
+// assert(strings[2] == "???");
+//
template <typename T, size_t N>
class StaticVector final : ArrayTraits<T>,
ArrayIterators<StaticVector<T, N>, T>,
@@ -153,15 +162,22 @@
static_assert(sizeof...(elements) < N, "Too many elements");
}
- // Constructs at most N elements. The template arguments T and N are inferred using the
- // deduction guide defined below. Element types must be convertible to the specified T:
+ // Constructs at most N elements in place by forwarding per-element constructor arguments. The
+ // template arguments T and N are inferred using the deduction guide defined below. The syntax
+ // for listing arguments is as follows:
//
- // ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+ // ftl::StaticVector vector = ftl::init::list<std::string>("abc")()(3u, '?');
+ //
// static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
+ // assert(vector.full());
+ // assert(vector[0] == "abc");
+ // assert(vector[1].empty());
+ // assert(vector[2] == "???");
//
- template <typename... Es>
- explicit StaticVector(std::in_place_type_t<T>, Es... elements)
- : StaticVector(std::forward<Es>(elements)...) {}
+ template <typename U, size_t Size, size_t... Sizes, typename... Types>
+ StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& init)
+ : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
+ std::index_sequence<Sizes...>{}, init.tuple) {}
~StaticVector() { std::destroy(begin(), end()); }
@@ -292,6 +308,32 @@
template <size_t I>
explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
+ // Recursion for in-place constructor.
+ //
+ // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex
+ // is the position of its first argument in Args, and ArgCount is the number of arguments.
+ // The Indices sequence corresponds to [0, ArgCount).
+ //
+ // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount
+ // for the next element. The recursion stops when Sizes is empty for the last element.
+ //
+ template <size_t I, size_t ArgIndex, size_t ArgCount, size_t... Indices, size_t Size,
+ size_t... Sizes, typename... Args>
+ StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+ std::index_sequence<Size, Sizes...>, std::tuple<Args...>& tuple)
+ : StaticVector(std::index_sequence<I + 1, ArgIndex + ArgCount, Size>{},
+ std::make_index_sequence<Size>{}, std::index_sequence<Sizes...>{}, tuple) {
+ construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+ }
+
+ // Base case for in-place constructor.
+ template <size_t I, size_t ArgIndex, size_t ArgCount, size_t... Indices, typename... Args>
+ StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+ std::index_sequence<>, std::tuple<Args...>& tuple)
+ : mSize(I + 1) {
+ construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+ }
+
size_type mSize = 0;
std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
};
@@ -306,8 +348,9 @@
StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
// Deduction guide for in-place constructor.
-template <typename T, typename... Us>
-StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>;
+template <typename T, size_t... Sizes, typename... Types>
+StaticVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+ -> StaticVector<T, sizeof...(Sizes)>;
template <typename T, size_t N>
template <typename E>
diff --git a/include/input/Input.h b/include/input/Input.h
index 3facfa5..aa42db8 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -773,6 +773,25 @@
bool mInTouchMode;
};
+/*
+ * Capture events.
+ */
+class CaptureEvent : public InputEvent {
+public:
+ virtual ~CaptureEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_CAPTURE; }
+
+ inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; }
+
+ void initialize(int32_t id, bool pointerCaptureEnabled);
+
+ void initialize(const CaptureEvent& from);
+
+protected:
+ bool mPointerCaptureEnabled;
+};
+
/**
* Base class for verified events.
* Do not create a VerifiedInputEvent explicitly.
@@ -835,6 +854,7 @@
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
virtual FocusEvent* createFocusEvent() = 0;
+ virtual CaptureEvent* createCaptureEvent() = 0;
};
/*
@@ -849,11 +869,13 @@
virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
+ virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
FocusEvent mFocusEvent;
+ CaptureEvent mCaptureEvent;
};
/*
@@ -867,6 +889,7 @@
virtual KeyEvent* createKeyEvent() override;
virtual MotionEvent* createMotionEvent() override;
virtual FocusEvent* createFocusEvent() override;
+ virtual CaptureEvent* createCaptureEvent() override;
void recycle(InputEvent* event);
@@ -876,6 +899,7 @@
std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
+ std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
};
} // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ad0a14e..8744ef7 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -67,6 +67,7 @@
MOTION,
FINISHED,
FOCUS,
+ CAPTURE,
};
struct Header {
@@ -166,6 +167,13 @@
inline size_t size() const { return sizeof(Focus); }
} focus;
+
+ struct Capture {
+ int32_t eventId;
+ uint32_t pointerCaptureEnabled; // actually a bool, but we maintain 8-byte alignment
+
+ inline size_t size() const { return sizeof(Capture); }
+ } capture;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -182,6 +190,8 @@
return "FINISHED";
case Type::FOCUS:
return "FOCUS";
+ case Type::CAPTURE:
+ return "CAPTURE";
}
}
};
@@ -341,6 +351,15 @@
*/
status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode);
+ /* Publishes a capture event to the input channel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if the channel is full.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t publishCaptureEvent(uint32_t seq, int32_t eventId, bool pointerCaptureEnabled);
+
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
* If a signal was received, returns the message sequence number,
* and whether the consumer handled the message.
@@ -576,6 +595,7 @@
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
+ static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
index 1d987fe..6562348 100644
--- a/include/input/NamedEnum.h
+++ b/include/input/NamedEnum.h
@@ -115,7 +115,7 @@
// Do not specialize it to a large number to avoid performance issues.
// The recommended maximum enum number to specialize is 64.
template <typename E>
- static const std::string string(E val, const char* fallbackFormat = "0x%08x") {
+ static const std::string string(E val, const char* fallbackFormat = "%02d") {
std::string result;
std::optional<std::string_view> enumString = enum_name(val);
result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 727ea60..e45a656 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -62,23 +62,27 @@
return service != nullptr ? service->openContentUri(stringUri) : -1;
}
-void ActivityManager::registerUidObserver(const sp<IUidObserver>& observer,
+status_t ActivityManager::registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage)
{
sp<IActivityManager> service = getService();
if (service != nullptr) {
- service->registerUidObserver(observer, event, cutpoint, callingPackage);
+ return service->registerUidObserver(observer, event, cutpoint, callingPackage);
}
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
}
-void ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
+status_t ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
{
sp<IActivityManager> service = getService();
if (service != nullptr) {
- service->unregisterUidObserver(observer);
+ return service->unregisterUidObserver(observer);
}
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
}
bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage)
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 26b997e..015954d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -147,6 +147,7 @@
"-Wextra",
"-Werror",
"-Wzero-as-null-pointer-constant",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
],
product_variables: {
binder32bit: {
@@ -173,6 +174,15 @@
misc_undefined: ["integer"],
},
min_sdk_version: "29",
+
+ tidy: true,
+ tidy_flags: [
+ // Only check our headers
+ "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
+ ],
+ tidy_checks_as_errors: [
+ "*",
+ ],
}
// AIDL interface between libbinder and framework.jar
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index c183d29..bf4387a 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -137,8 +137,8 @@
}
BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
- : mHandle(handle)
- , mStability(0)
+ : mStability(0)
+ , mHandle(handle)
, mAlive(1)
, mObitsSent(0)
, mObituaries(nullptr)
@@ -223,13 +223,14 @@
if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
using android::internal::Stability;
- auto stability = Stability::get(this);
- auto required = privateVendor ? Stability::VENDOR : Stability::getLocalStability();
+ auto category = Stability::getCategory(this);
+ Stability::Level required = privateVendor ? Stability::VENDOR
+ : Stability::getLocalLevel();
- if (CC_UNLIKELY(!Stability::check(stability, required))) {
+ if (CC_UNLIKELY(!Stability::check(category, required))) {
ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
- Stability::stabilityString(stability).c_str(),
- Stability::stabilityString(required).c_str());
+ category.debugString().c_str(),
+ Stability::levelString(required).c_str());
return BAD_TYPE;
}
}
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index 64c1ff6..3a62059 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -208,7 +208,7 @@
}
for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) {
- long remain = length;
+ ssize_t remain = length;
char* c = buffer;
if (!oneLine && !cStyle) {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index e9f5aae..cf9bb46 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -59,7 +59,7 @@
return fd;
}
- virtual void registerUidObserver(const sp<IUidObserver>& observer,
+ virtual status_t registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage)
@@ -70,15 +70,23 @@
data.writeInt32(event);
data.writeInt32(cutpoint);
data.writeString16(callingPackage);
- remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ status_t err = remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
}
- virtual void unregisterUidObserver(const sp<IUidObserver>& observer)
+ virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer)
{
Parcel data, reply;
data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(observer));
- remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ status_t err = remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
}
virtual bool isUidActive(const uid_t uid, const String16& callingPackage)
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4198e49..5f3d670 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -42,6 +42,7 @@
// ----------------------------------------------------------------------
+// NOLINTNEXTLINE(google-default-arguments)
status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
switch(code) {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a3a2f87..d4c7acf 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -135,7 +135,7 @@
out << "target.ptr=" << btd->target.ptr;
}
out << " (cookie " << btd->cookie << ")" << endl
- << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(long)btd->flags << endl
+ << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << endl
<< "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
<< " bytes)" << endl
<< "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
@@ -150,7 +150,7 @@
uint32_t code = (uint32_t)*cmd++;
size_t cmdIndex = code & 0xff;
if (code == BR_ERROR) {
- out << "BR_ERROR: " << (void*)(long)(*cmd++) << endl;
+ out << "BR_ERROR: " << (void*)(uint64_t)(*cmd++) << endl;
return cmd;
} else if (cmdIndex >= N) {
out << "Unknown reply: " << code << endl;
@@ -177,21 +177,21 @@
case BR_DECREFS: {
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
+ out << ": target=" << (void*)(uint64_t)b << " (cookie " << (void*)(uint64_t)c << ")";
} break;
case BR_ATTEMPT_ACQUIRE: {
const int32_t p = *cmd++;
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c
+ out << ": target=" << (void*)(uint64_t)b << " (cookie " << (void*)(uint64_t)c
<< "), pri=" << p;
} break;
case BR_DEAD_BINDER:
case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
const int32_t c = *cmd++;
- out << ": death cookie " << (void*)(long)c;
+ out << ": death cookie " << (void*)(uint64_t)c;
} break;
default:
@@ -232,7 +232,7 @@
case BC_FREE_BUFFER: {
const int32_t buf = *cmd++;
- out << ": buffer=" << (void*)(long)buf;
+ out << ": buffer=" << (void*)(uint64_t)buf;
} break;
case BC_INCREFS:
@@ -247,7 +247,7 @@
case BC_ACQUIRE_DONE: {
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
+ out << ": target=" << (void*)(uint64_t)b << " (cookie " << (void*)(uint64_t)c << ")";
} break;
case BC_ATTEMPT_ACQUIRE: {
@@ -260,12 +260,12 @@
case BC_CLEAR_DEATH_NOTIFICATION: {
const int32_t h = *cmd++;
const int32_t c = *cmd++;
- out << ": handle=" << h << " (death cookie " << (void*)(long)c << ")";
+ out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")";
} break;
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
- out << ": death cookie " << (void*)(long)c;
+ out << ": death cookie " << (void*)(uint64_t)c;
} break;
default:
@@ -895,21 +895,21 @@
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
- freeBuffer, this);
+ freeBuffer);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(nullptr,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t), this);
+ tr.offsets_size/sizeof(binder_size_t));
}
} else {
freeBuffer(nullptr,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t), this);
+ tr.offsets_size/sizeof(binder_size_t));
continue;
}
}
@@ -1077,7 +1077,7 @@
sp<BBinder> the_context_object;
-void IPCThreadState::setTheContextObject(sp<BBinder> obj)
+void IPCThreadState::setTheContextObject(const sp<BBinder>& obj)
{
the_context_object = obj;
}
@@ -1183,7 +1183,7 @@
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
+ tr.offsets_size/sizeof(binder_size_t), freeBuffer);
const void* origServingStackPointer = mServingStackPointer;
mServingStackPointer = &origServingStackPointer; // anything on the stack
@@ -1370,7 +1370,7 @@
void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
size_t /*dataSize*/,
const binder_size_t* /*objects*/,
- size_t /*objectsSize*/, void* /*cookie*/)
+ size_t /*objectsSize*/)
{
//ALOGI("Freeing parcel %p", &parcel);
IF_LOG_COMMANDS() {
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 6d728dc..ca067e2 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -221,7 +221,7 @@
const bool isVendorService =
strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
- const long timeout = 5000;
+ constexpr int64_t timeout = 5000;
int64_t startTime = uptimeMillis();
// Vendor code can't access system properties
if (!gSystemBootCompleted && !isVendorService) {
@@ -234,7 +234,7 @@
#endif
}
// retry interval in millisecond; note that vendor services stay at 100ms
- const long sleepTime = gSystemBootCompleted ? 1000 : 100;
+ const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
ProcessState::self()->getDriverName().c_str());
@@ -310,7 +310,7 @@
// Simple RAII object to ensure a function call immediately before going out of scope
class Defer {
public:
- Defer(std::function<void()>&& f) : mF(std::move(f)) {}
+ explicit Defer(std::function<void()>&& f) : mF(std::move(f)) {}
~Defer() { mF(); }
private:
std::function<void()> mF;
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index b21af96..4714234 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -75,6 +75,7 @@
// ----------------------------------------------------------------------
+// NOLINTNEXTLINE(google-default-arguments)
status_t BnUidObserver::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 58df52d..0377075 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -171,7 +171,8 @@
if (status != OK) return status;
internal::Stability::tryMarkCompilationUnit(binder.get());
- return writeInt32(internal::Stability::get(binder.get()));
+ auto category = internal::Stability::getCategory(binder.get());
+ return writeInt32(category.repr());
}
status_t Parcel::finishUnflattenBinder(
@@ -181,7 +182,7 @@
status_t status = readInt32(&stability);
if (status != OK) return status;
- status = internal::Stability::set(binder.get(), stability, true /*log*/);
+ status = internal::Stability::setRepr(binder.get(), stability, true /*log*/);
if (status != OK) return status;
*out = binder;
@@ -2055,7 +2056,7 @@
if (size >= 0 && size < INT32_MAX) {
*outLen = size;
const char* str = (const char*)readInplace(size+1);
- if (str != nullptr) {
+ if (str != nullptr && str[size] == '\0') {
return str;
}
}
@@ -2138,7 +2139,7 @@
if (size >= 0 && size < INT32_MAX) {
*outLen = size;
const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
- if (str != nullptr) {
+ if (str != nullptr && str[size] == u'\0') {
return str;
}
}
@@ -2498,7 +2499,7 @@
}
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
+ const binder_size_t* objects, size_t objectsCount, release_func relFunc)
{
binder_size_t minOffset = 0;
freeDataNoInit();
@@ -2513,7 +2514,6 @@
mNextObjectHint = 0;
mObjectsSorted = false;
mOwner = relFunc;
- mOwnerCookie = relCookie;
for (size_t i = 0; i < mObjectsSize; i++) {
binder_size_t offset = mObjects[i];
if (offset < minOffset) {
@@ -2614,7 +2614,7 @@
if (mOwner) {
LOG_ALLOC("Parcel %p: freeing other owner data", this);
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+ mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
} else {
LOG_ALLOC("Parcel %p: freeing allocated data", this);
releaseObjects();
@@ -2777,7 +2777,7 @@
memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+ mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
mOwner = nullptr;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index 97a6c94..c807cfe 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -34,10 +34,22 @@
using android::sp;
using android::status_t;
using android::UNEXPECTED_NULL;
+
+using android::binder::VAL_BOOLEAN;
+using android::binder::VAL_INTEGER;
+using android::binder::VAL_LONG;
+using android::binder::VAL_DOUBLE;
+using android::binder::VAL_STRING;
+using android::binder::VAL_BOOLEANARRAY;
+using android::binder::VAL_INTARRAY;
+using android::binder::VAL_LONGARRAY;
+using android::binder::VAL_DOUBLEARRAY;
+using android::binder::VAL_STRINGARRAY;
+using android::binder::VAL_PERSISTABLEBUNDLE;
+
using std::map;
using std::set;
using std::vector;
-using namespace ::android::binder;
enum {
// Keep them in sync with BUNDLE_MAGIC* in frameworks/base/core/java/android/os/BaseBundle.java.
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 6115aec..339c538 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "Stability"
+
#include <binder/Stability.h>
#include <binder/BpBinder.h>
@@ -21,34 +23,59 @@
namespace android {
namespace internal {
+// the libbinder parcel format is currently unstable
+
+// oldest version which is supported
+constexpr uint8_t kBinderWireFormatOldest = 1;
+// current version
+constexpr uint8_t kBinderWireFormatVersion = 1;
+
+Stability::Category Stability::Category::currentFromLevel(Level level) {
+ return {
+ .version = kBinderWireFormatVersion,
+ .reserved = {0},
+ .level = level,
+ };
+}
+
+std::string Stability::Category::debugString() {
+ return levelString(level) + " wire protocol version "
+ + std::to_string(version);
+}
+
void Stability::markCompilationUnit(IBinder* binder) {
- status_t result = set(binder, getLocalStability(), true /*log*/);
+ auto stability = Category::currentFromLevel(getLocalLevel());
+ status_t result = setRepr(binder, stability.repr(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
void Stability::markVintf(IBinder* binder) {
- status_t result = set(binder, Level::VINTF, true /*log*/);
+ auto stability = Category::currentFromLevel(Level::VINTF);
+ status_t result = setRepr(binder, stability.repr(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
void Stability::debugLogStability(const std::string& tag, const sp<IBinder>& binder) {
- ALOGE("%s: stability is %s", tag.c_str(), stabilityString(get(binder.get())).c_str());
+ auto stability = getCategory(binder.get());
+ ALOGE("%s: stability is %s", tag.c_str(), stability.debugString().c_str());
}
void Stability::markVndk(IBinder* binder) {
- status_t result = set(binder, Level::VENDOR, true /*log*/);
+ auto stability = Category::currentFromLevel(Level::VENDOR);
+ status_t result = setRepr(binder, stability.repr(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) {
- return check(get(binder.get()), Level::VINTF);
+ return check(getCategory(binder.get()), Level::VINTF);
}
void Stability::tryMarkCompilationUnit(IBinder* binder) {
- (void) set(binder, getLocalStability(), false /*log*/);
+ auto stability = Category::currentFromLevel(getLocalLevel());
+ (void) setRepr(binder, stability.repr(), false /*log*/);
}
-Stability::Level Stability::getLocalStability() {
+Stability::Level Stability::getLocalLevel() {
#ifdef __ANDROID_VNDK__
#ifdef __ANDROID_APEX__
// TODO(b/142684679) avoid use_vendor on system APEXes
@@ -67,65 +94,81 @@
#endif
}
-status_t Stability::set(IBinder* binder, int32_t stability, bool log) {
- Level currentStability = get(binder);
+status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) {
+ auto current = getCategory(binder);
+ auto setting = Category::fromRepr(representation);
+
+ // If we have ahold of a binder with a newer declared version, then it
+ // should support older versions, and we will simply write our parcels with
+ // the current wire parcel format.
+ if (setting.version < kBinderWireFormatOldest) {
+ // always log, because this shouldn't happen
+ ALOGE("Cannot accept binder with older binder wire protocol version "
+ "%u. Versions less than %u are unsupported.", setting.version,
+ kBinderWireFormatOldest);
+ return BAD_TYPE;
+ }
// null binder is always written w/ 'UNDECLARED' stability
if (binder == nullptr) {
- if (stability == UNDECLARED) {
+ if (setting.level == UNDECLARED) {
return OK;
} else {
if (log) {
ALOGE("Null binder written with stability %s.",
- stabilityString(stability).c_str());
+ levelString(setting.level).c_str());
}
return BAD_TYPE;
}
}
- if (!isDeclaredStability(stability)) {
+ if (!isDeclaredLevel(setting.level)) {
if (log) {
- ALOGE("Can only set known stability, not %d.", stability);
+ ALOGE("Can only set known stability, not %u.", setting.level);
}
return BAD_TYPE;
}
- if (currentStability != Level::UNDECLARED && currentStability != stability) {
+ if (current.repr() != 0 && current != setting) {
if (log) {
- ALOGE("Interface being set with %s but it is already marked as %s.",
- stabilityString(stability).c_str(), stabilityString(currentStability).c_str());
+ ALOGE("Interface being set with %s but it is already marked as %s",
+ setting.debugString().c_str(),
+ current.debugString().c_str());
}
return BAD_TYPE;
}
- if (currentStability == stability) return OK;
+ if (current == setting) return OK;
BBinder* local = binder->localBinder();
if (local != nullptr) {
- local->mStability = static_cast<int32_t>(stability);
+ local->mStability = setting.repr();
} else {
- binder->remoteBinder()->mStability = static_cast<int32_t>(stability);
+ binder->remoteBinder()->mStability = setting.repr();
}
return OK;
}
-Stability::Level Stability::get(IBinder* binder) {
- if (binder == nullptr) return UNDECLARED;
+Stability::Category Stability::getCategory(IBinder* binder) {
+ if (binder == nullptr) {
+ return Category::currentFromLevel(Level::UNDECLARED);
+ }
BBinder* local = binder->localBinder();
if (local != nullptr) {
- return static_cast<Stability::Level>(local->mStability);
+ return Category::fromRepr(local->mStability);
}
- return static_cast<Stability::Level>(binder->remoteBinder()->mStability);
+ return Category::fromRepr(binder->remoteBinder()->mStability);
}
-bool Stability::check(int32_t provided, Level required) {
- bool stable = (provided & required) == required;
+bool Stability::check(Category provided, Level required) {
+ bool stable = (provided.level & required) == required;
- if (!isDeclaredStability(provided) && provided != UNDECLARED) {
- ALOGE("Unknown stability when checking interface stability %d.", provided);
+ if (provided.level != UNDECLARED && !isDeclaredLevel(provided.level)) {
+ ALOGE("Unknown stability when checking interface stability %d.",
+ provided.level);
stable = false;
}
@@ -133,18 +176,18 @@
return stable;
}
-bool Stability::isDeclaredStability(int32_t stability) {
+bool Stability::isDeclaredLevel(Level stability) {
return stability == VENDOR || stability == SYSTEM || stability == VINTF;
}
-std::string Stability::stabilityString(int32_t stability) {
- switch (stability) {
+std::string Stability::levelString(Level level) {
+ switch (level) {
case Level::UNDECLARED: return "undeclared stability";
case Level::VENDOR: return "vendor stability";
case Level::SYSTEM: return "system stability";
case Level::VINTF: return "vintf stability";
}
- return "unknown stability " + std::to_string(stability);
+ return "unknown stability " + std::to_string(level);
}
} // namespace internal
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 64ab7a9..b5a078c 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -245,10 +245,5 @@
return ret;
}
-std::stringstream& operator<< (std::stringstream& stream, const Status& s) {
- stream << s.toString8().string();
- return stream;
-}
-
} // namespace binder
} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 61a611d..1cfb560 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -50,6 +50,12 @@
"exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
}
]
+ },
+ {
+ "name": "libbinder_rs-internal_test"
+ },
+ {
+ "name": "rustBinderTest"
}
]
}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 0240858..830971b 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -74,11 +74,11 @@
ActivityManager();
int openContentUri(const String16& stringUri);
- void registerUidObserver(const sp<IUidObserver>& observer,
+ status_t registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage);
- void unregisterUidObserver(const sp<IUidObserver>& observer);
+ status_t unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 64d0657..a0e28d2 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -109,19 +109,18 @@
KeyedVector<const void*, entry_t> mObjects;
};
-protected:
+private:
BpBinder(int32_t handle,int32_t trackedUid);
virtual ~BpBinder();
virtual void onFirstRef();
virtual void onLastStrongRef(const void* id);
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
-private:
- const int32_t mHandle;
-
friend ::android::internal::Stability;
int32_t mStability;
+ const int32_t mHandle;
+
struct Obituary {
wp<DeathRecipient> recipient;
void* cookie;
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e2081ff..2d58c46 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -31,11 +31,11 @@
DECLARE_META_INTERFACE(ActivityManager)
virtual int openContentUri(const String16& stringUri) = 0;
- virtual void registerUidObserver(const sp<IUidObserver>& observer,
+ virtual status_t registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage) = 0;
- virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
+ virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
virtual status_t checkPermission(const String16& permission,
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
index ac4b4ca..f92d557 100644
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ b/libs/binder/include/binder/IMediaResourceMonitor.h
@@ -45,6 +45,7 @@
class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
public:
+ // NOLINTNEXTLINE(google-default-arguments)
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags = 0);
};
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index a3712c9..4da8aa1 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -146,7 +146,7 @@
void blockUntilThreadAvailable();
// Service manager registration
- void setTheContextObject(sp<BBinder> obj);
+ void setTheContextObject(const sp<BBinder>& obj);
// WARNING: DO NOT USE THIS API
//
@@ -185,9 +185,8 @@
static void threadDestructor(void *st);
static void freeBuffer(Parcel* parcel,
const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize,
- void* cookie);
-
+ const binder_size_t* objects, size_t objectsSize);
+
const sp<ProcessState> mProcess;
Vector<BBinder*> mPendingStrongDerefs;
Vector<RefBase::weakref_type*> mPendingWeakDerefs;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 138d957..4b2d50d 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -33,18 +33,20 @@
#include <binder/Parcelable.h>
#ifdef BINDER_IPC_32BIT
+//NOLINTNEXTLINE(google-runtime-int) b/173188702
typedef unsigned int binder_size_t;
#else
+//NOLINTNEXTLINE(google-runtime-int) b/173188702
typedef unsigned long long binder_size_t;
#endif
+struct flat_binder_object;
// ---------------------------------------------------------------------------
namespace android {
template <typename T> class Flattenable;
template <typename T> class LightFlattenable;
-struct flat_binder_object;
class IBinder;
class IPCThreadState;
class ProcessState;
@@ -208,6 +210,8 @@
template<typename T>
status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
template<typename T>
+ status_t writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val);
+ template<typename T>
status_t writeParcelableVector(const std::vector<T>& val);
template<typename T>
@@ -485,24 +489,21 @@
// uid.
uid_t readCallingWorkSourceUid() const;
+ void print(TextOutput& to, uint32_t flags = 0) const;
+
private:
typedef void (*release_func)(Parcel* parcel,
const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize,
- void* cookie);
-
+ const binder_size_t* objects, size_t objectsSize);
+
uintptr_t ipcData() const;
size_t ipcDataSize() const;
uintptr_t ipcObjects() const;
size_t ipcObjectsCount() const;
void ipcSetDataReference(const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount,
- release_func relFunc, void* relCookie);
-
-public:
- void print(TextOutput& to, uint32_t flags = 0) const;
+ release_func relFunc);
-private:
Parcel(const Parcel& o);
Parcel& operator=(const Parcel& o);
@@ -609,7 +610,9 @@
mutable bool mDeallocZero;
release_func mOwner;
- void* mOwnerCookie;
+
+ // TODO(167966510): reserved for binder/version/stability
+ void* mReserved = reinterpret_cast<void*>(0xAAAAAAAA);
class Blob {
public:
@@ -1228,6 +1231,16 @@
return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
}
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ using NullableT = std::optional<T>;
+ return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
+}
+
template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
status_t Parcel::writeEnum(const T& val) {
return writeInt32(static_cast<int32_t>(val));
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 4ba6ba8..9896fd7 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -43,22 +43,22 @@
android::status_t readFromParcel(const android::Parcel* parcel) override;
inline bool operator!=(const ParcelFileDescriptor& rhs) const {
- return mFd != rhs.mFd;
+ return mFd.get() != rhs.mFd.get();
}
inline bool operator<(const ParcelFileDescriptor& rhs) const {
- return mFd < rhs.mFd;
+ return mFd.get() < rhs.mFd.get();
}
inline bool operator<=(const ParcelFileDescriptor& rhs) const {
- return mFd <= rhs.mFd;
+ return mFd.get() <= rhs.mFd.get();
}
inline bool operator==(const ParcelFileDescriptor& rhs) const {
- return mFd == rhs.mFd;
+ return mFd.get() == rhs.mFd.get();
}
inline bool operator>(const ParcelFileDescriptor& rhs) const {
- return mFd > rhs.mFd;
+ return mFd.get() > rhs.mFd.get();
}
inline bool operator>=(const ParcelFileDescriptor& rhs) const {
- return mFd >= rhs.mFd;
+ return mFd.get() >= rhs.mFd.get();
}
private:
android::base::unique_fd mFd;
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 6566285..12272ba 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -26,6 +26,29 @@
namespace internal {
+// Stability encodes how a binder changes over time. There are two levels of
+// stability:
+// 1). the interface stability - this is how a particular set of API calls (a
+// particular ordering of things like writeInt32/readInt32) are changed over
+// time. If one release, we have 'writeInt32' and the next release, we have
+// 'writeInt64', then this interface doesn't have a very stable
+// Stability::Level. Usually this ordering is controlled by a .aidl file.
+// 2). the wire format stability - this is how these API calls map to actual
+// bytes that are written to the wire (literally, this is how they are written
+// to the kernel inside of IBinder::transact, but it may be expanded to other
+// wires in the future). For instance, writeInt32 in binder translates to
+// writing a 4-byte little-endian integer in two's complement. You can imagine
+// in the future, we change writeInt32/readInt32 to instead write 8-bytes with
+// that integer and some check bits. In this case, the wire format changes,
+// but as long as a client libbinder knows to keep on writing a 4-byte value
+// to old servers, and new servers know how to interpret the 8-byte result,
+// they can still communicate.
+//
+// Every binder object has a stability level associated with it, and when
+// communicating with a binder, we make sure that the command we sent is one
+// that it knows how to process. The summary of stability of a binder is
+// represented by a Stability::Category object.
+
// WARNING: These APIs are only ever expected to be called by auto-generated code.
// Instead of calling them, you should set the stability of a .aidl interface
class Stability final {
@@ -73,7 +96,7 @@
static void tryMarkCompilationUnit(IBinder* binder);
- enum Level : int32_t {
+ enum Level : uint8_t {
UNDECLARED = 0,
VENDOR = 0b000011,
@@ -81,19 +104,54 @@
VINTF = 0b111111,
};
+ // This is the format of stability passed on the wire.
+ struct Category {
+ static inline Category fromRepr(int32_t representation) {
+ return *reinterpret_cast<Category*>(&representation);
+ }
+ int32_t repr() const {
+ return *reinterpret_cast<const int32_t*>(this);
+ }
+ static inline Category currentFromLevel(Level level);
+
+ bool operator== (const Category& o) const {
+ return repr() == o.repr();
+ }
+ bool operator!= (const Category& o) const {
+ return !(*this == o);
+ }
+
+ std::string debugString();
+
+ // This is the version of the wire protocol associated with the host
+ // process of a particular binder. As the wire protocol changes, if
+ // sending a transaction to a binder with an old version, the Parcel
+ // class must write parcels according to the version documented here.
+ uint8_t version;
+
+ uint8_t reserved[2];
+
+ // bitmask of Stability::Level
+ Level level;
+ };
+ static_assert(sizeof(Category) == sizeof(int32_t));
+
// returns the stability according to how this was built
- static Level getLocalStability();
+ static Level getLocalLevel();
// applies stability to binder if stability level is known
__attribute__((warn_unused_result))
- static status_t set(IBinder* binder, int32_t stability, bool log);
+ static status_t setRepr(IBinder* binder, int32_t representation, bool log);
- static Level get(IBinder* binder);
+ // get stability information as encoded on the wire
+ static Category getCategory(IBinder* binder);
- static bool check(int32_t provided, Level required);
+ // whether a transaction on binder is allowed, if the transaction
+ // is done from a context with a specific stability level
+ static bool check(Category provided, Level required);
- static bool isDeclaredStability(int32_t stability);
- static std::string stabilityString(int32_t stability);
+ static bool isDeclaredLevel(Level level);
+ static std::string levelString(Level level);
Stability();
};
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index 7d889b6..c30ae01 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -18,7 +18,8 @@
#define ANDROID_BINDER_STATUS_H
#include <cstdint>
-#include <sstream>
+#include <sstream> // historical
+#include <ostream>
#include <binder/Parcel.h>
#include <utils/String8.h>
@@ -153,8 +154,9 @@
String8 mMessage;
}; // class Status
-// For gtest output logging
-std::stringstream& operator<< (std::stringstream& stream, const Status& s);
+static inline std::ostream& operator<< (std::ostream& o, const Status& s) {
+ return o << s.toString8();
+}
} // namespace binder
} // namespace android
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 0fe7f5b..5a719b8 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -17,10 +17,6 @@
#ifndef _BINDER_MODULE_H_
#define _BINDER_MODULE_H_
-#ifdef __cplusplus
-namespace android {
-#endif
-
/* obtain structures and constants from the kernel header */
// TODO(b/31559095): bionic on host
@@ -36,6 +32,10 @@
#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#ifdef __cplusplus
+namespace android {
+#endif
+
#ifndef BR_FROZEN_REPLY
// Temporary definition of BR_FROZEN_REPLY. For production
// this will come from UAPI binder.h
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index cecc759..a57beee 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -98,6 +98,14 @@
"30",
],
},
+ tidy: true,
+ tidy_flags: [
+ // Only check our headers
+ "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
+ ],
+ tidy_checks_as_errors: [
+ "*",
+ ],
}
ndk_headers {
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index b927f6f..5e2e1bd 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -367,7 +367,7 @@
mDeathRecipients.end());
}
-binder_status_t AIBinder_DeathRecipient::linkToDeath(sp<IBinder> binder, void* cookie) {
+binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp<IBinder>& binder, void* cookie) {
CHECK(binder != nullptr);
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
@@ -386,7 +386,7 @@
return STATUS_OK;
}
-binder_status_t AIBinder_DeathRecipient::unlinkToDeath(sp<IBinder> binder, void* cookie) {
+binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder, void* cookie) {
CHECK(binder != nullptr);
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 0fa47c6..6236e81 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -161,8 +161,8 @@
};
explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
- binder_status_t linkToDeath(::android::sp<::android::IBinder>, void* cookie);
- binder_status_t unlinkToDeath(::android::sp<::android::IBinder> binder, void* cookie);
+ binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
+ binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
private:
// When the user of this API deletes a Bp object but not the death recipient, the
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 8d60226..2d85f90 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -74,6 +74,9 @@
* ownership of that other object.
*/
SpAIBinder& operator=(const SpAIBinder& other) {
+ if (this == &other) {
+ return *this;
+ }
AIBinder_incStrong(other.mBinder);
set(other.mBinder);
return *this;
@@ -170,8 +173,10 @@
ScopedAResource& operator=(const ScopedAResource&) = delete;
// move-constructing/assignment is okay
- ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; }
- ScopedAResource& operator=(ScopedAResource&& other) {
+ ScopedAResource(ScopedAResource&& other) noexcept : mT(std::move(other.mT)) {
+ other.mT = DEFAULT;
+ }
+ ScopedAResource& operator=(ScopedAResource&& other) noexcept {
set(other.mT);
other.mT = DEFAULT;
return *this;
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index dfcf4dc..4858514 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -82,13 +82,13 @@
}
template <typename T>
- bool setParcelable(T* p) {
- if (p && this->mStability > T::_aidl_stability) {
+ bool setParcelable(const T& p) {
+ if (this->mStability > T::_aidl_stability) {
return false;
}
AParcel_reset(mParcel.get());
AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
- p->writeToParcel(mParcel.get());
+ p.writeToParcel(mParcel.get());
return true;
}
@@ -112,6 +112,8 @@
return std::move(ret);
}
+ void reset() { AParcel_reset(mParcel.get()); }
+
private:
mutable ndk::ScopedAParcel mParcel;
parcelable_stability_t mStability;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 3e3eda1..ec7c7d8 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -261,7 +261,7 @@
}
binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) {
- std::unique_ptr<ParcelFileDescriptor> parcelFd;
+ std::optional<ParcelFileDescriptor> parcelFd;
status_t status = parcel->get()->readParcelable(&parcelFd);
if (status != STATUS_OK) return PruneStatusT(status);
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
index a94f06f..96cd215 100644
--- a/libs/binder/parcel_fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -143,6 +143,13 @@
FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>");
},
PARCEL_READ_OPT_STATUS(android::String8, readString8),
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readString8Inplace";
+ size_t outLen = 0;
+ const char* str = p.readString8Inplace(&outLen);
+ std::string bytes = hexString(str, sizeof(char) * (outLen + 1));
+ FUZZ_LOG() << "readString8Inplace: " << bytes << " size: " << outLen;
+ },
PARCEL_READ_OPT_STATUS(android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
@@ -150,8 +157,8 @@
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
const char16_t* str = p.readString16Inplace(&outLen);
- FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen)
- << " size: " << outLen;
+ std::string bytes = hexString(str, sizeof(char16_t) * (outLen + 1));
+ FUZZ_LOG() << "readString16Inplace: " << bytes << " size: " << outLen;
},
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
diff --git a/libs/binder/rust/TEST_MAPPING b/libs/binder/rust/TEST_MAPPING
deleted file mode 100644
index 50c474c..0000000
--- a/libs/binder/rust/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "libbinder_rs-internal_test"
- }
- ],
- "postsubmit": [
- {
- "name": "rustBinderTest"
- }
- ]
-}
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
index 1764228..841d47b 100644
--- a/libs/binder/tests/binderParcelTest.cpp
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -25,6 +25,40 @@
using android::String8;
using android::status_t;
+TEST(Parcel, NonNullTerminatedString8) {
+ String8 kTestString = String8("test-is-good");
+
+ // write non-null terminated string
+ Parcel p;
+ p.writeString8(kTestString);
+ p.setDataPosition(0);
+ // BAD! assumption of wire format for test
+ // write over length of string
+ p.writeInt32(kTestString.size() - 2);
+
+ p.setDataPosition(0);
+ String8 output;
+ EXPECT_NE(OK, p.readString8(&output));
+ EXPECT_EQ(output.size(), 0);
+}
+
+TEST(Parcel, NonNullTerminatedString16) {
+ String16 kTestString = String16("test-is-good");
+
+ // write non-null terminated string
+ Parcel p;
+ p.writeString16(kTestString);
+ p.setDataPosition(0);
+ // BAD! assumption of wire format for test
+ // write over length of string
+ p.writeInt32(kTestString.size() - 2);
+
+ p.setDataPosition(0);
+ String16 output;
+ EXPECT_NE(OK, p.readString16(&output));
+ EXPECT_EQ(output.size(), 0);
+}
+
// Tests a second operation results in a parcel at the same location as it
// started.
void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
index ab4c56a..56d958c 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -245,7 +245,7 @@
double best = (double)m_best / 1.0E6;
double worst = (double)m_worst / 1.0E6;
double average = (double)m_total_time / m_transactions / 1.0E6;
- // FIXME: libjson?
+ // TODO: libjson?
int W = DUMP_PRESICION + 2;
cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left
<< average << ",\"wst\":" << setw(W) << left << worst
@@ -376,7 +376,7 @@
if (is_client(num)) {
int no_trans = iterations * 2;
double sync_ratio = (1.0 - (double)no_sync / no_trans);
- // FIXME: libjson?
+ // TODO: libjson?
cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\""
<< ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
<< "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
@@ -466,7 +466,7 @@
}
vector<Pipe> pipes;
thread_dump("main");
- // FIXME: libjson?
+ // TODO: libjson?
cout << "{" << endl;
cout << "\"cfg\":{\"pair\":" << (no_process / 2)
<< ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us
@@ -495,7 +495,7 @@
// detected in the child process
no_inherent += status;
}
- // FIXME: libjson?
+ // TODO: libjson?
cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
<< endl;
cout << "}" << endl;
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index fb32382..eb8e57a 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -5,6 +5,7 @@
address: true,
},
srcs: [
+ "SmallMap_test.cpp",
"SmallVector_test.cpp",
"StaticVector_test.cpp",
],
diff --git a/libs/ftl/SmallMap_test.cpp b/libs/ftl/SmallMap_test.cpp
new file mode 100644
index 0000000..fa00c06
--- /dev/null
+++ b/libs/ftl/SmallMap_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#include <ftl/SmallMap.h>
+#include <gtest/gtest.h>
+
+#include <cctype>
+
+namespace android::test {
+
+using ftl::SmallMap;
+
+// Keep in sync with example usage in header file.
+TEST(SmallMap, Example) {
+ ftl::SmallMap<int, std::string, 3> map;
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+
+ map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_TRUE(map.contains(123));
+
+ EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+
+ const auto opt = map.find(-1);
+ ASSERT_TRUE(opt);
+
+ std::string& ref = *opt;
+ EXPECT_TRUE(ref.empty());
+ ref = "xyz";
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+}
+
+TEST(SmallMap, Construct) {
+ {
+ // Default constructor.
+ SmallMap<int, std::string, 2> map;
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ // In-place constructor with same types.
+ SmallMap<int, std::string, 5> map =
+ ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
+
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 5u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
+ }
+ {
+ // In-place constructor with different types.
+ SmallMap<int, std::string, 5> map =
+ ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 5u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
+ }
+ {
+ // In-place constructor with implicit size.
+ SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+ static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 3u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
+ }
+}
+
+TEST(SmallMap, Find) {
+ {
+ // Constant reference.
+ const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+ const auto opt = map.find('b');
+ EXPECT_EQ(opt, 'B');
+
+ const char d = 'D';
+ const auto ref = map.find('d').value_or(std::cref(d));
+ EXPECT_EQ(ref.get(), 'D');
+ }
+ {
+ // Mutable reference.
+ ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+ const auto opt = map.find('c');
+ EXPECT_EQ(opt, 'C');
+
+ char d = 'd';
+ const auto ref = map.find('d').value_or(std::ref(d));
+ ref.get() = 'D';
+ EXPECT_EQ(d, 'D');
+ }
+ {
+ // Constant unary operation.
+ const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+ }
+ {
+ // Mutable unary operation.
+ ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
+ }
+}
+
+} // namespace android::test
diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp
index c41e622..d0c2858 100644
--- a/libs/ftl/SmallVector_test.cpp
+++ b/libs/ftl/SmallVector_test.cpp
@@ -52,6 +52,14 @@
vector = ftl::SmallVector(array);
EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
EXPECT_FALSE(vector.dynamic());
+
+ ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+ ASSERT_EQ(strings.size(), 3u);
+ EXPECT_FALSE(strings.dynamic());
+
+ EXPECT_EQ(strings[0], "abc");
+ EXPECT_EQ(strings[1], "123");
+ EXPECT_EQ(strings[2], "???");
}
TEST(SmallVector, Construct) {
@@ -99,7 +107,8 @@
}
{
// In-place constructor with same types.
- SmallVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+ SmallVector vector =
+ ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
@@ -110,9 +119,10 @@
const auto copy = "red"s;
auto move = "velvet"s;
std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
- SmallVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+ SmallVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_TRUE(move.empty());
EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
EXPECT_FALSE(vector.dynamic());
}
@@ -206,9 +216,8 @@
}
TEST(SmallVector, MovableElement) {
- // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
- // element type would be deduced from the first element, i.e. const char*.
- SmallVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+ // Construct std::string elements in place from per-element arguments.
+ SmallVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
strings.pop_back();
EXPECT_EQ(strings.max_size(), 7u);
@@ -221,6 +230,7 @@
ASSERT_FALSE(it == strings.end());
EXPECT_EQ(*it, "cake");
+ // Construct std::string from first 4 characters of string literal.
strings.unstable_erase(it);
EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
}
@@ -260,7 +270,7 @@
bool operator==(const Word& other) const { return other.str == str; }
};
- SmallVector words(std::in_place_type<Word>, "colored", "velour");
+ SmallVector words = ftl::init::list<Word>("colored")("velour");
// The replaced element can be referenced by the replacement.
{
@@ -301,7 +311,7 @@
}
TEST(SmallVector, Sort) {
- SmallVector strings(std::in_place_type<std::string>, "pie", "quince", "tart", "red", "velvet");
+ SmallVector strings = ftl::init::list<std::string>("pie")("quince")("tart")("red")("velvet");
strings.push_back("cake"s);
auto sorted = std::move(strings);
diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp
index dd5ce35..db42d23 100644
--- a/libs/ftl/StaticVector_test.cpp
+++ b/libs/ftl/StaticVector_test.cpp
@@ -51,6 +51,13 @@
const char array[] = "hi";
vector = ftl::StaticVector(array);
EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
+
+ ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+ ASSERT_EQ(strings.size(), 3u);
+
+ EXPECT_EQ(strings[0], "abc");
+ EXPECT_EQ(strings[1], "123");
+ EXPECT_EQ(strings[2], "???");
}
TEST(StaticVector, Construct) {
@@ -91,7 +98,8 @@
}
{
// In-place constructor with same types.
- StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+ StaticVector vector =
+ ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
@@ -101,9 +109,10 @@
const auto copy = "red"s;
auto move = "velvet"s;
std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
- StaticVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+ StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+ EXPECT_TRUE(move.empty());
EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
}
{
@@ -204,9 +213,8 @@
}
TEST(StaticVector, MovableElement) {
- // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
- // element type would be deduced from the first element, i.e. const char*.
- StaticVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+ // Construct std::string elements in place from per-element arguments.
+ StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
strings.pop_back();
EXPECT_EQ(strings.max_size(), 7u);
@@ -221,7 +229,7 @@
strings.unstable_erase(it);
- // Construct std::string from first 4 characters of C-style string.
+ // Construct std::string from first 4 characters of string literal.
it = strings.emplace_back("cakewalk", 4u);
ASSERT_NE(it, strings.end());
EXPECT_EQ(*it, "cake"s);
@@ -250,7 +258,7 @@
const std::string str;
};
- StaticVector words(std::in_place_type<Word>, "red", "velour", "cake");
+ StaticVector words = ftl::init::list<Word>("red")("velour")("cake");
// The replaced element can be referenced by the replacement.
const auto it = words.begin() + 1;
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 55c5de9..d54de49 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -548,7 +548,7 @@
}
// Return true if all the required libraries from vndk and sphal namespace are
-// linked to the Game Driver namespace correctly.
+// linked to the updatable gfx driver namespace correctly.
bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) {
const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
if (llndkLibraries.empty()) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index a0e9cbf..af9ef06 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -237,6 +237,10 @@
"libnativebase_headers",
],
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+
export_shared_lib_headers: [
"libEGL",
"libnativewindow",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 678613b..e6aa02a 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -111,8 +111,8 @@
int width, int height, bool enableTripleBuffering)
: mName(name),
mSurfaceControl(surface),
- mWidth(width),
- mHeight(height),
+ mSize(width, height),
+ mRequestedSize(mSize),
mNextTransaction(nullptr) {
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
@@ -130,7 +130,7 @@
mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
- mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+ mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
mTransformHint = mSurfaceControl->getTransformHint();
@@ -146,10 +146,10 @@
std::unique_lock _lock{mMutex};
mSurfaceControl = surface;
- if (mWidth != width || mHeight != height) {
- mWidth = width;
- mHeight = height;
- mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+ ui::Size newSize(width, height);
+ if (mRequestedSize != newSize) {
+ mRequestedSize.set(newSize);
+ mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);
}
}
@@ -218,6 +218,7 @@
// number of buffers.
if (mNumFrameAvailable == 0 || maxBuffersAcquired()) {
BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
+ mCallbackCV.notify_all();
return;
}
@@ -252,10 +253,13 @@
}
if (rejectBuffer(bufferItem)) {
- BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth,
- mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
- // TODO(b/168917217) temporarily don't reject buffers until we can synchronize buffer size
- // changes from ViewRootImpl.
+ BQA_LOGE("rejecting buffer:active_size=%dx%d, requested_size=%dx%d"
+ "buffer{size=%dx%d transform=%d}",
+ mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
+ buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ processNextBufferLocked(useNextTransaction);
+ return;
}
mNumAcquired++;
@@ -278,26 +282,31 @@
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
t->setFrame(mSurfaceControl,
- {0, 0, static_cast<int32_t>(mWidth), static_cast<int32_t>(mHeight)});
+ {0, 0, static_cast<int32_t>(mSize.width), static_cast<int32_t>(mSize.height)});
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
t->setDesiredPresentTime(bufferItem.mTimestamp);
t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
+ if (mAutoRefresh != bufferItem.mAutoRefresh) {
+ t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
+ mAutoRefresh = bufferItem.mAutoRefresh;
+ }
+
if (applyTransaction) {
t->apply();
}
BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
" applyTransaction=%s mTimestamp=%" PRId64,
- mWidth, mHeight, bufferItem.mFrameNumber, toString(applyTransaction),
+ mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
bufferItem.mTimestamp);
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
- return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+ return GLConsumer::scaleDownCrop(item.mCrop, mSize.width, mSize.height);
}
return item.mCrop;
}
@@ -332,8 +341,9 @@
mNextTransaction = t;
}
-bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) const {
+bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+ mSize = mRequestedSize;
// Only reject buffers if scaling mode is freeze.
return false;
}
@@ -345,9 +355,14 @@
if (item.mTransform & ui::Transform::ROT_90) {
std::swap(bufWidth, bufHeight);
}
+ ui::Size bufferSize(bufWidth, bufHeight);
+ if (mRequestedSize != mSize && mRequestedSize == bufferSize) {
+ mSize = mRequestedSize;
+ return false;
+ }
// reject buffers if the buffer size doesn't match.
- return bufWidth != mWidth || bufHeight != mHeight;
+ return mSize != bufferSize;
}
// Check if we have acquired the maximum number of buffers.
@@ -378,11 +393,11 @@
}).detach();
}
- status_t setFrameRate(float frameRate, int8_t compatibility) override {
+ status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override {
if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
return BAD_VALUE;
}
- return mBbq->setFrameRate(frameRate, compatibility);
+ return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless);
}
status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId) override {
@@ -392,12 +407,12 @@
// TODO: Can we coalesce this with frame updates? Need to confirm
// no timing issues.
-status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility) {
+status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility,
+ bool shouldBeSeamless) {
std::unique_lock _lock{mMutex};
SurfaceComposerClient::Transaction t;
- return t.setFrameRate(mSurfaceControl, frameRate, compatibility)
- .apply();
+ return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
}
status_t BLASTBufferQueue::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index abfee61..c6c9a8f 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -33,10 +33,10 @@
// using just a few large reads.
static const size_t EVENT_BUFFER_SIZE = 100;
-DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
- ISurfaceComposer::VsyncSource vsyncSource,
- ISurfaceComposer::ConfigChanged configChanged)
- : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+DisplayEventDispatcher::DisplayEventDispatcher(
+ const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration)
+ : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}
@@ -139,6 +139,7 @@
ssize_t n;
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+ mFrameRateOverrides.reserve(n);
for (ssize_t i = 0; i < n; i++) {
const DisplayEventReceiver::Event& ev = buf[i];
switch (ev.header.type) {
@@ -162,6 +163,13 @@
case DisplayEventReceiver::DISPLAY_EVENT_NULL:
dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ mFrameRateOverrides.emplace_back(ev.frameRateOverride);
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
+ std::move(mFrameRateOverrides));
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index f2b0962..03b33c7 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -32,11 +32,12 @@
// ---------------------------------------------------------------------------
-DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
- ISurfaceComposer::ConfigChanged configChanged) {
+DisplayEventReceiver::DisplayEventReceiver(
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != nullptr) {
- mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
+ mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
if (mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 6f92233..e46a415 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -223,8 +223,8 @@
return NO_ERROR;
}
- virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,
- ConfigChanged configChanged) {
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection(
+ VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) {
Parcel data, reply;
sp<IDisplayEventConnection> result;
int err = data.writeInterfaceToken(
@@ -233,7 +233,7 @@
return result;
}
data.writeInt32(static_cast<int32_t>(vsyncSource));
- data.writeInt32(static_cast<int32_t>(configChanged));
+ data.writeUint32(eventRegistration.get());
err = remote()->transact(
BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
data, &reply);
@@ -1114,7 +1114,7 @@
}
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, bool shouldBeSeamless) {
Parcel data, reply;
status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (err != NO_ERROR) {
@@ -1140,6 +1140,12 @@
return err;
}
+ err = data.writeBool(shouldBeSeamless);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
@@ -1367,10 +1373,11 @@
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32());
- auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32());
+ EventRegistrationFlags eventRegistration =
+ static_cast<EventRegistration>(data.readUint32());
sp<IDisplayEventConnection> connection(
- createDisplayEventConnection(vsyncSource, configChanged));
+ createDisplayEventConnection(vsyncSource, eventRegistration));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
@@ -2033,7 +2040,13 @@
ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
return err;
}
- status_t result = setFrameRate(surface, frameRate, compatibility);
+ bool shouldBeSeamless;
+ err = data.readBool(&shouldBeSeamless);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless);
reply->writeInt32(result);
return NO_ERROR;
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9722f36..7d2c7b8 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -59,8 +59,11 @@
frameRateSelectionPriority(-1),
frameRate(0.0f),
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ shouldBeSeamless(true),
fixedTransformHint(ui::Transform::ROT_INVALID),
- frameNumber(0) {
+ frameNumber(0),
+ frameTimelineVsyncId(ISurfaceComposer::INVALID_VSYNC_ID),
+ autoRefresh(false) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -144,9 +147,11 @@
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
+ SAFE_PARCEL(output.writeBool, shouldBeSeamless);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeUint64, frameNumber);
SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId);
+ SAFE_PARCEL(output.writeBool, autoRefresh);
SAFE_PARCEL(output.writeUint32, blurRegions.size());
for (auto region : blurRegions) {
@@ -262,10 +267,12 @@
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
+ SAFE_PARCEL(input.readBool, &shouldBeSeamless);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readUint64, &frameNumber);
SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId);
+ SAFE_PARCEL(input.readBool, &autoRefresh);
uint32_t numRegions = 0;
SAFE_PARCEL(input.readUint32, &numRegions);
@@ -521,6 +528,7 @@
what |= eFrameRateChanged;
frameRate = other.frameRate;
frameRateCompatibility = other.frameRateCompatibility;
+ shouldBeSeamless = other.shouldBeSeamless;
}
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
@@ -530,6 +538,20 @@
what |= eFrameNumberChanged;
frameNumber = other.frameNumber;
}
+ if (other.what & eFrameTimelineVsyncChanged) {
+ // When merging vsync Ids we take the oldest valid one
+ if (frameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
+ other.frameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
+ frameTimelineVsyncId = std::max(frameTimelineVsyncId, other.frameTimelineVsyncId);
+ } else if (frameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID) {
+ frameTimelineVsyncId = other.frameTimelineVsyncId;
+ }
+ what |= eFrameTimelineVsyncChanged;
+ }
+ if (other.what & eAutoRefreshChanged) {
+ what |= eAutoRefreshChanged;
+ autoRefresh = other.autoRefresh;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c1155ab..94390aa 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1443,7 +1443,8 @@
int Surface::dispatchSetFrameRate(va_list args) {
float frameRate = static_cast<float>(va_arg(args, double));
int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
- return setFrameRate(frameRate, compatibility);
+ bool shouldBeSeamless = static_cast<bool>(va_arg(args, int));
+ return setFrameRate(frameRate, compatibility, shouldBeSeamless);
}
int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2279,7 +2280,7 @@
mSurfaceListener->onBuffersDiscarded(discardedBufs);
}
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) {
ATRACE_CALL();
ALOGV("Surface::setFrameRate");
@@ -2287,7 +2288,8 @@
return BAD_VALUE;
}
- return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
+ return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
+ shouldBeSeamless);
}
status_t Surface::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 039e900..47a08ab 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1474,7 +1474,8 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
- const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) {
+ const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
+ bool shouldBeSeamless) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1487,6 +1488,7 @@
s->what |= layer_state_t::eFrameRateChanged;
s->frameRate = frameRate;
s->frameRateCompatibility = compatibility;
+ s->shouldBeSeamless = shouldBeSeamless;
return *this;
}
@@ -1525,6 +1527,19 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAutoRefresh(
+ const sp<SurfaceControl>& sc, bool autoRefresh) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eAutoRefreshChanged;
+ s->autoRefresh = autoRefresh;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 2300e81..9fb7d6f 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -85,7 +85,7 @@
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
void flushShadowQueue() { mFlushShadowQueue = true; }
- status_t setFrameRate(float frameRate, int8_t compatibility);
+ status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
virtual ~BLASTBufferQueue() = default;
@@ -100,7 +100,7 @@
void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
// Return true if we need to reject the buffer based on the scaling mode and the buffer size.
- bool rejectBuffer(const BufferItem& item) const REQUIRES(mMutex);
+ bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
bool maxBuffersAcquired() const REQUIRES(mMutex);
std::string mName;
@@ -126,8 +126,8 @@
// is ready to be presented.
PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
- uint32_t mWidth GUARDED_BY(mMutex);
- uint32_t mHeight GUARDED_BY(mMutex);
+ ui::Size mSize GUARDED_BY(mMutex);
+ ui::Size mRequestedSize GUARDED_BY(mMutex);
uint32_t mTransformHint GUARDED_BY(mMutex);
@@ -139,6 +139,10 @@
// If set to true, the next queue buffer will wait until the shadow queue has been processed by
// the adapter.
bool mFlushShadowQueue = false;
+ // Last requested auto refresh state set by the producer. The state indicates that the consumer
+ // should acquire the next frame as soon as it can and not wait for a frame to become available.
+ // This is only relevant for shared buffer mode.
+ bool mAutoRefresh GUARDED_BY(mMutex) = false;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index c65618b..5587acf 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -19,6 +19,7 @@
#include <utils/Looper.h>
namespace android {
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
struct VsyncEventData {
// The Vsync Id corresponsing to this vsync event. This will be used to
@@ -36,8 +37,7 @@
explicit DisplayEventDispatcher(
const sp<Looper>& looper,
ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged =
- ISurfaceComposer::eConfigChangedSuppress);
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
status_t initialize();
void dispose();
@@ -54,6 +54,8 @@
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
+ std::vector<FrameRateOverride> mFrameRateOverrides;
+
virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -64,6 +66,9 @@
// can be properly poked.
virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
+ virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) = 0;
+
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
};
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index df3118f..3191fc9 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -54,6 +54,8 @@
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
+ DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
+ DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
};
struct Event {
@@ -85,11 +87,17 @@
nsecs_t vsyncPeriod __attribute__((aligned(8)));
};
+ struct FrameRateOverride {
+ uid_t uid __attribute__((aligned(8)));
+ float frameRateHz __attribute__((aligned(8)));
+ };
+
Header header;
union {
VSync vsync;
Hotplug hotplug;
Config config;
+ FrameRateOverride frameRateOverride;
};
};
@@ -98,13 +106,12 @@
* DisplayEventReceiver creates and registers an event connection with
* SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
* or requestNextVsync to receive them.
- * To receive Config Changed events specify this in the constructor.
- * Other events start being delivered immediately.
+ * To receive ConfigChanged and/or FrameRateOverrides events specify this in
+ * the constructor. Other events start being delivered immediately.
*/
explicit DisplayEventReceiver(
ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged =
- ISurfaceComposer::eConfigChangedSuppress);
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
/*
* ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 5cd9356..86e3a25 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -26,6 +26,8 @@
#include <gui/IScreenCaptureListener.h>
#include <gui/ITransactionCompletedListener.h>
+#include <input/Flags.h>
+
#include <math/vec4.h>
#include <ui/ConfigStoreTypes.h>
@@ -107,7 +109,12 @@
eVsyncSourceSurfaceFlinger = 1
};
- enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
+ enum class EventRegistration {
+ configChanged = 1 << 0,
+ frameRateOverride = 1 << 1,
+ };
+
+ using EventRegistrationFlags = Flags<EventRegistration>;
// Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java
static constexpr int64_t INVALID_VSYNC_ID = -1;
@@ -120,7 +127,7 @@
/* return an IDisplayEventConnection */
virtual sp<IDisplayEventConnection> createDisplayEventConnection(
VsyncSource vsyncSource = eVsyncSourceApp,
- ConfigChanged configChanged = eConfigChangedSuppress) = 0;
+ EventRegistrationFlags eventRegistration = {}) = 0;
/* create a virtual display
* requires ACCESS_SURFACE_FLINGER permission.
@@ -475,7 +482,7 @@
* Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) = 0;
+ int8_t compatibility, bool shouldBeSeamless) = 0;
/*
* Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a73d9a6..f1c5d67 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -130,6 +130,7 @@
eFrameNumberChanged = 0x400'00000000,
eFrameTimelineVsyncChanged = 0x800'00000000,
eBlurRegionsChanged = 0x1000'00000000,
+ eAutoRefreshChanged = 0x2000'00000000,
};
layer_state_t();
@@ -218,6 +219,7 @@
// Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
float frameRate;
int8_t frameRateCompatibility;
+ bool shouldBeSeamless;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -233,6 +235,11 @@
uint64_t frameNumber;
int64_t frameTimelineVsyncId;
+
+ // Indicates that the consumer should acquire the next frame as soon as it
+ // can and not wait for a frame to become available. This is only relevant
+ // in shared buffer mode.
+ bool autoRefresh;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 4aa076e..82bc5c9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -186,7 +186,7 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- virtual status_t setFrameRate(float frameRate, int8_t compatibility);
+ virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
virtual status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 73909a3..2eb97f2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -524,7 +524,7 @@
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
- int8_t compatibility);
+ int8_t compatibility, bool shouldBeSeamless);
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -541,6 +541,11 @@
Transaction& setFrameTimelineVsync(const sp<SurfaceControl>& sc,
int64_t frameTimelineVsyncId);
+ // Indicates that the consumer should acquire the next frame as soon as it
+ // can and not wait for a frame to become available. This is only relevant
+ // in shared buffer mode.
+ Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index f3559fa..4282ef9 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -55,9 +55,9 @@
mBlastBufferQueueAdapter->setNextTransaction(next);
}
- int getWidth() { return mBlastBufferQueueAdapter->mWidth; }
+ int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
- int getHeight() { return mBlastBufferQueueAdapter->mHeight; }
+ int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
@@ -250,8 +250,15 @@
PIXEL_FORMAT_RGBA_8888);
adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
- ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth());
- ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight());
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ int32_t width;
+ igbProducer->query(NATIVE_WINDOW_WIDTH, &width);
+ ASSERT_EQ(mDisplayWidth / 2, width);
+ int32_t height;
+ igbProducer->query(NATIVE_WINDOW_HEIGHT, &height);
+ ASSERT_EQ(mDisplayHeight / 2, height);
}
TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index 7210910..c5093e2 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -40,6 +40,9 @@
CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0);
CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, uid, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, frameRateHz, 8);
}
} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0cd3962..ce3afa2 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -687,7 +687,7 @@
sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
sp<IDisplayEventConnection> createDisplayEventConnection(
- ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override {
+ ISurfaceComposer::VsyncSource, ISurfaceComposer::EventRegistrationFlags) override {
return nullptr;
}
sp<IBinder> createDisplay(const String8& /*displayName*/,
@@ -869,7 +869,7 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
- int8_t /*compatibility*/) override {
+ int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override {
return NO_ERROR;
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 0ea3889..0a00d68 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -89,6 +89,9 @@
case AINPUT_EVENT_TYPE_FOCUS: {
return "FOCUS";
}
+ case AINPUT_EVENT_TYPE_CAPTURE: {
+ return "CAPTURE";
+ }
}
return "UNKNOWN";
}
@@ -754,6 +757,19 @@
mInTouchMode = from.mInTouchMode;
}
+// --- CaptureEvent ---
+
+void CaptureEvent::initialize(int32_t id, bool pointerCaptureEnabled) {
+ InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE, INVALID_HMAC);
+ mPointerCaptureEnabled = pointerCaptureEnabled;
+}
+
+void CaptureEvent::initialize(const CaptureEvent& from) {
+ InputEvent::initialize(from);
+ mPointerCaptureEnabled = from.mPointerCaptureEnabled;
+}
+
// --- PooledInputEventFactory ---
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -790,6 +806,15 @@
return event;
}
+CaptureEvent* PooledInputEventFactory::createCaptureEvent() {
+ if (mCaptureEventPool.empty()) {
+ return new CaptureEvent();
+ }
+ CaptureEvent* event = mCaptureEventPool.front().release();
+ mCaptureEventPool.pop();
+ return event;
+}
+
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
@@ -810,6 +835,13 @@
return;
}
break;
+ case AINPUT_EVENT_TYPE_CAPTURE:
+ if (mCaptureEventPool.size() < mMaxPoolSize) {
+ mCaptureEventPool.push(
+ std::unique_ptr<CaptureEvent>(static_cast<CaptureEvent*>(event)));
+ return;
+ }
+ break;
}
delete event;
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 85df405..acea473 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -105,6 +105,8 @@
return true;
case Type::FOCUS:
return true;
+ case Type::CAPTURE:
+ return true;
}
}
return false;
@@ -120,6 +122,8 @@
return sizeof(Header) + body.finished.size();
case Type::FOCUS:
return sizeof(Header) + body.focus.size();
+ case Type::CAPTURE:
+ return sizeof(Header) + body.capture.size();
}
return sizeof(Header);
}
@@ -238,6 +242,11 @@
msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
+ case InputMessage::Type::CAPTURE: {
+ msg->body.capture.eventId = body.capture.eventId;
+ msg->body.capture.pointerCaptureEnabled = body.capture.pointerCaptureEnabled;
+ break;
+ }
}
}
@@ -571,6 +580,23 @@
return mChannel->sendMessage(&msg);
}
+status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId,
+ bool pointerCaptureEnabled) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishCaptureEvent(inputChannel=%s, pointerCaptureEnabled=%s)",
+ mChannel->getName().c_str(), toString(pointerCaptureEnabled));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::CAPTURE;
+ msg.header.seq = seq;
+ msg.body.capture.eventId = eventId;
+ msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled ? 1 : 0;
+ return mChannel->sendMessage(&msg);
+}
+
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
@@ -739,6 +765,16 @@
*outEvent = focusEvent;
break;
}
+
+ case InputMessage::Type::CAPTURE: {
+ CaptureEvent* captureEvent = factory->createCaptureEvent();
+ if (!captureEvent) return NO_MEMORY;
+
+ initializeCaptureEvent(captureEvent, &mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = captureEvent;
+ break;
+ }
}
}
return OK;
@@ -1171,6 +1207,10 @@
msg->body.focus.inTouchMode == 1);
}
+void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled == 1);
+}
+
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
uint32_t pointerCount = msg->body.motion.pointerCount;
PointerProperties pointerProperties[pointerCount];
@@ -1274,6 +1314,12 @@
toString(msg.body.focus.inTouchMode));
break;
}
+ case InputMessage::Type::CAPTURE: {
+ out += android::base::StringPrintf("hasCapture=%s",
+ toString(msg.body.capture
+ .pointerCaptureEnabled));
+ break;
+ }
}
out += "\n";
}
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 4f53dc9..9da7b69 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -51,6 +51,7 @@
void PublishAndConsumeKeyEvent();
void PublishAndConsumeMotionEvent();
void PublishAndConsumeFocusEvent();
+ void PublishAndConsumeCaptureEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -309,6 +310,43 @@
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 42;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool captureEnabled = true;
+
+ status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
+ ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_CAPTURE, event->getType())
+ << "consumer should have returned a capture event";
+
+ const CaptureEvent* captureEvent = static_cast<CaptureEvent*>(event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, captureEvent->getId());
+ EXPECT_EQ(captureEnabled, captureEvent->getPointerCaptureEnabled());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ uint32_t finishedSeq = 0;
+ bool handled = false;
+ status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+ ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, finishedSeq)
+ << "publisher receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
@@ -321,6 +359,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -385,6 +427,9 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
} // namespace android
diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/input/tests/NamedEnum_test.cpp
index 4e93f71..74a0044 100644
--- a/libs/input/tests/NamedEnum_test.cpp
+++ b/libs/input/tests/NamedEnum_test.cpp
@@ -86,9 +86,9 @@
TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::string(e), "0x00000005");
+ ASSERT_EQ(NamedEnum::string(e), "05");
e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::string(e), "0x00000009");
+ ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009");
}
TEST(NamedEnum, CompileTimeFlagName) {
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 3c5fb22..4107d61 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -79,6 +79,9 @@
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+ CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
+
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
@@ -99,6 +102,7 @@
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
static_assert(sizeof(InputMessage::Body::Finished) == 8);
static_assert(sizeof(InputMessage::Body::Focus) == 8);
+ static_assert(sizeof(InputMessage::Body::Capture) == 8);
}
// --- VerifiedInputEvent ---
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 9e515cd..6d91916 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -141,6 +141,8 @@
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
nsecs_t vsyncPeriod) override;
void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
+ void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) override;
void scheduleCallbacks();
@@ -175,8 +177,7 @@
}
Choreographer::Choreographer(const sp<Looper>& looper)
- : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged::eConfigChangedSuppress),
+ : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp),
mLooper(looper),
mThreadId(std::this_thread::get_id()) {
std::lock_guard<std::mutex> _l(gChoreographers.lock);
@@ -381,15 +382,13 @@
this, to_string(displayId).c_str(), toString(connected));
}
-// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
-// Choreographer only supports dispatching VSYNC events for the internal
-// display, so as such Choreographer does not support the notion of multiple
-// displays. When multi-display choreographer is properly supported, then
-// PhysicalDisplayId should no longer be ignored.
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
- nsecs_t) {
- ALOGV("choreographer %p ~ received config change event (displayId=%s, configId=%d).",
- this, to_string(displayId).c_str(), configId);
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+ LOG_ALWAYS_FATAL("dispatchConfigChanged was called but was never registered");
+}
+
+void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
+ std::vector<FrameRateOverride>) {
+ LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
}
void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 1ec73ce..a375d43 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -397,6 +397,16 @@
return 0;
}
+int AHardwareBuffer_getId(const AHardwareBuffer* buffer, uint64_t* outId) {
+ if (!buffer || !outId) return BAD_VALUE;
+
+ const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ if (!gb) return BAD_VALUE;
+
+ *outId = gb->getId();
+
+ return OK;
+}
// ----------------------------------------------------------------------------
// VNDK functions
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index fd1793b..b406a9c 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,10 +159,8 @@
}
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, frameRate, compatibility);
+ return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility,
+ /*shouldBeSeamless*/ true);
}
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -172,6 +170,13 @@
window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
}
+int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
+ int8_t compatibility, bool shouldBeSeamless) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return -EINVAL;
+ }
+ return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless);
+}
/**************************************************************************************************
* vndk-stable
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index ae5e47b..4fcca9e 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -45,14 +45,14 @@
#ifndef ANDROID_HARDWARE_BUFFER_H
#define ANDROID_HARDWARE_BUFFER_H
+#include <android/rect.h>
#include <inttypes.h>
-
#include <sys/cdefs.h>
-#include <android/rect.h>
-
__BEGIN_DECLS
+// clang-format off
+
/**
* Buffer pixel formats.
*/
@@ -201,9 +201,9 @@
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4,
/// The buffer will be read from by the GPU as a texture.
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
/// The buffer will be written to by the GPU as a framebuffer attachment.
- AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9,
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9,
/**
* The buffer will be written to by the GPU as a framebuffer
* attachment.
@@ -214,7 +214,7 @@
* attachment should also have this flag. Use the equivalent flag
* AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion.
*/
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
/**
* The buffer will be used as a composer HAL overlay layer.
*
@@ -225,7 +225,7 @@
* directly through AHardwareBuffer_allocate instead of buffers allocated
* by the framework.
*/
- AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11,
+ AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11,
/**
* The buffer is protected from direct CPU access or being read by
* non-secure hardware, such as video encoders.
@@ -236,19 +236,19 @@
* GL_EXT_protected_textures for more information on how these
* buffers are expected to behave.
*/
- AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
+ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
/// The buffer will be read by a hardware video encoder.
- AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16,
+ AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16,
/**
* The buffer will be used for direct writes from sensors.
* When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
*/
- AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23,
+ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23,
/**
* The buffer will be used as a shader storage or uniform buffer object.
* When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
*/
- AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
+ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
/**
* The buffer will be used as a cube map texture.
* When this flag is present, the buffer must have a layer count
@@ -256,13 +256,13 @@
* bound to OpenGL textures using the extension
* GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
*/
- AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25,
+ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25,
/**
* The buffer contains a complete mipmap hierarchy.
* Note that buffers with this flag must be bound to OpenGL textures using
* the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
*/
- AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26,
+ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26,
AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28,
AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29,
@@ -291,8 +291,8 @@
* parameters of existing ones.
*/
typedef struct AHardwareBuffer_Desc {
- uint32_t width; ///< Width in pixels.
- uint32_t height; ///< Height in pixels.
+ uint32_t width; ///< Width in pixels.
+ uint32_t height; ///< Height in pixels.
/**
* Number of images in an image array. AHardwareBuffers with one
* layer correspond to regular 2D textures. AHardwareBuffers with
@@ -301,21 +301,21 @@
* AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is
* a cube map or a cube map array.
*/
- uint32_t layers;
- uint32_t format; ///< One of AHardwareBuffer_Format.
- uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags.
- uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
- uint32_t rfu0; ///< Initialize to zero, reserved for future use.
- uint64_t rfu1; ///< Initialize to zero, reserved for future use.
+ uint32_t layers;
+ uint32_t format; ///< One of AHardwareBuffer_Format.
+ uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags.
+ uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
+ uint32_t rfu0; ///< Initialize to zero, reserved for future use.
+ uint64_t rfu1; ///< Initialize to zero, reserved for future use.
} AHardwareBuffer_Desc;
/**
* Holds data for a single image plane.
*/
typedef struct AHardwareBuffer_Plane {
- void* data; ///< Points to first byte in plane
- uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
- uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to
+ void* _Nullable data; ///< Points to first byte in plane
+ uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
+ uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to
/// the first value of the next row.
} AHardwareBuffer_Plane;
@@ -323,8 +323,8 @@
* Holds all image planes that contain the pixel data.
*/
typedef struct AHardwareBuffer_Planes {
- uint32_t planeCount; ///< Number of distinct planes
- AHardwareBuffer_Plane planes[4]; ///< Array of image planes
+ uint32_t planeCount; ///< Number of distinct planes
+ AHardwareBuffer_Plane planes[4]; ///< Array of image planes
} AHardwareBuffer_Planes;
/**
@@ -332,6 +332,8 @@
*/
typedef struct AHardwareBuffer AHardwareBuffer;
+// clang-format on
+
#if __ANDROID_API__ >= 26
/**
@@ -347,8 +349,8 @@
* \return 0 on success, or an error number of the allocation fails for
* any reason. The returned buffer has a reference count of 1.
*/
-int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
- AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* _Nonnull desc,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(26);
/**
* Acquire a reference on the given AHardwareBuffer object.
*
@@ -357,7 +359,7 @@
*
* Available since API level 26.
*/
-void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
+void AHardwareBuffer_acquire(AHardwareBuffer* _Nonnull buffer) __INTRODUCED_IN(26);
/**
* Remove a reference that was previously acquired with
@@ -365,7 +367,7 @@
*
* Available since API level 26.
*/
-void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
+void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer) __INTRODUCED_IN(26);
/**
* Return a description of the AHardwareBuffer in the passed
@@ -373,8 +375,8 @@
*
* Available since API level 26.
*/
-void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26);
+void AHardwareBuffer_describe(const AHardwareBuffer* _Nonnull buffer,
+ AHardwareBuffer_Desc* _Nonnull outDesc) __INTRODUCED_IN(26);
/**
* Lock the AHardwareBuffer for direct CPU access.
@@ -428,8 +430,57 @@
* has more than one layer. Error number if the lock fails for any other
* reason.
*/
-int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
+int AHardwareBuffer_lock(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence,
+ const ARect* _Nullable rect, void* _Nullable* _Nonnull outVirtualAddress)
+ __INTRODUCED_IN(26);
+
+/**
+ * Unlock the AHardwareBuffer from direct CPU access.
+ *
+ * Must be called after all changes to the buffer are completed by the
+ * caller. If \a fence is NULL, the function will block until all work
+ * is completed. Otherwise, \a fence will be set either to a valid file
+ * descriptor or to -1. The file descriptor will become signaled once
+ * the unlocking is complete and buffer contents are updated.
+ * The caller is responsible for closing the file descriptor once it's
+ * no longer needed. The value -1 indicates that unlocking has already
+ * completed before the function returned and no further operations are
+ * necessary.
+ *
+ * Available since API level 26.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
+ * the unlock fails for any reason.
+ */
+int AHardwareBuffer_unlock(AHardwareBuffer* _Nonnull buffer, int32_t* _Nullable fence)
+ __INTRODUCED_IN(26);
+
+/**
+ * Send the AHardwareBuffer to an AF_UNIX socket.
+ *
+ * Available since API level 26.
+ *
+ * \return 0 on success, -EINVAL if \a buffer is NULL, or an error
+ * number if the operation fails for any reason.
+ */
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* _Nonnull buffer, int socketFd)
+ __INTRODUCED_IN(26);
+
+/**
+ * Receive an AHardwareBuffer from an AF_UNIX socket.
+ *
+ * Available since API level 26.
+ *
+ * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
+ * number if the operation fails for any reason.
+ */
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer)
+ __INTRODUCED_IN(26);
+
+#endif // __ANDROID_API__ >= 26
+
+#if __ANDROID_API__ >= 29
/**
* Lock a potentially multi-planar AHardwareBuffer for direct CPU access.
@@ -458,52 +509,9 @@
* has more than one layer. Error number if the lock fails for any other
* reason.
*/
-int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29);
-
-/**
- * Unlock the AHardwareBuffer from direct CPU access.
- *
- * Must be called after all changes to the buffer are completed by the
- * caller. If \a fence is NULL, the function will block until all work
- * is completed. Otherwise, \a fence will be set either to a valid file
- * descriptor or to -1. The file descriptor will become signaled once
- * the unlocking is complete and buffer contents are updated.
- * The caller is responsible for closing the file descriptor once it's
- * no longer needed. The value -1 indicates that unlocking has already
- * completed before the function returned and no further operations are
- * necessary.
- *
- * Available since API level 26.
- *
- * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
- * the unlock fails for any reason.
- */
-int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26);
-
-/**
- * Send the AHardwareBuffer to an AF_UNIX socket.
- *
- * Available since API level 26.
- *
- * \return 0 on success, -EINVAL if \a buffer is NULL, or an error
- * number if the operation fails for any reason.
- */
-int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26);
-
-/**
- * Receive an AHardwareBuffer from an AF_UNIX socket.
- *
- * Available since API level 26.
- *
- * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
- * number if the operation fails for any reason.
- */
-int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
-
-#endif // __ANDROID_API__ >= 26
-
-#if __ANDROID_API__ >= 29
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence,
+ const ARect* _Nullable rect,
+ AHardwareBuffer_Planes* _Nonnull outPlanes) __INTRODUCED_IN(29);
/**
* Test whether the given format and usage flag combination is
@@ -524,7 +532,7 @@
* \return 1 if the format and usage flag combination is allocatable,
* 0 otherwise.
*/
-int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29);
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* _Nonnull desc) __INTRODUCED_IN(29);
/**
* Lock an AHardwareBuffer for direct CPU access.
@@ -537,11 +545,29 @@
*
* Available since API level 29.
*/
-int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, void** outVirtualAddress,
- int32_t* outBytesPerPixel, int32_t* outBytesPerStride) __INTRODUCED_IN(29);
+int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence,
+ const ARect* _Nullable rect,
+ void* _Nullable* _Nonnull outVirtualAddress,
+ int32_t* _Nonnull outBytesPerPixel,
+ int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
+
#endif // __ANDROID_API__ >= 29
+#if __ANDROID_API__ >= 31
+
+/**
+ * Get the system wide unique id for an AHardwareBuffer.
+ *
+ * Available since API level 31.
+ *
+ * \return 0 on success, -EINVAL if \a buffer or \a outId is NULL, or an error number if the
+ * operation fails for any reason.
+ */
+int AHardwareBuffer_getId(const AHardwareBuffer* _Nonnull buffer, uint64_t* _Nonnull outId)
+ __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
__END_DECLS
#endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 36aad2e..deea59b 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -34,6 +34,7 @@
#define ANDROID_NATIVE_WINDOW_H
#include <stdint.h>
+#include <stdbool.h>
#include <sys/cdefs.h>
#include <android/data_space.h>
@@ -256,36 +257,11 @@
};
/**
- * Sets the intended frame rate for this window.
+ * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true).
*
- * On devices that are capable of running the display at different refresh
- * rates, the system may choose a display refresh rate to better match this
- * window's frame rate. Usage of this API won't introduce frame rate throttling,
- * or affect other aspects of the application's frame production
- * pipeline. However, because the system may change the display refresh rate,
- * calls to this function may result in changes to Choreographer callback
- * timings, and changes to the time interval at which the system releases
- * buffers back to the application.
- *
- * Note that this only has an effect for windows presented on the display. If
- * this ANativeWindow is consumed by something other than the system compositor,
- * e.g. a media codec, this call has no effect.
+ * See ANativeWindow_setFrameRateWithSeamlessness().
*
* Available since API level 30.
- *
- * \param frameRate The intended frame rate of this window, in frames per
- * second. 0 is a special value that indicates the app will accept the system's
- * choice for the display frame rate, which is the default behavior if this
- * function isn't called. The frameRate param does <em>not</em> need to be a
- * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
- * to a device that can only run the display at 60fps.
- *
- * \param compatibility The frame rate compatibility of this window. The
- * compatibility value may influence the system's choice of display refresh
- * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
- *
- * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
- * value are invalid.
*/
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility)
__INTRODUCED_IN(30);
@@ -303,6 +279,51 @@
#endif // __ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Sets the intended frame rate for this window.
+ *
+ * On devices that are capable of running the display at different refresh
+ * rates, the system may choose a display refresh rate to better match this
+ * window's frame rate. Usage of this API won't introduce frame rate throttling,
+ * or affect other aspects of the application's frame production
+ * pipeline. However, because the system may change the display refresh rate,
+ * calls to this function may result in changes to Choreographer callback
+ * timings, and changes to the time interval at which the system releases
+ * buffers back to the application.
+ *
+ * Note that this only has an effect for windows presented on the display. If
+ * this ANativeWindow is consumed by something other than the system compositor,
+ * e.g. a media codec, this call has no effect.
+ *
+ * Available since API level 31.
+ *
+ * \param frameRate The intended frame rate of this window, in frames per
+ * second. 0 is a special value that indicates the app will accept the system's
+ * choice for the display frame rate, which is the default behavior if this
+ * function isn't called. The frameRate param does <em>not</em> need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
+ * to a device that can only run the display at 60fps.
+ *
+ * \param compatibility The frame rate compatibility of this window. The
+ * compatibility value may influence the system's choice of display refresh
+ * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ *
+ * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
+ * seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. True indicates that any frame rate changes caused by this
+ * request should be seamless. False indicates that non-seamless refresh rates are also
+ * acceptable.
+ *
+ * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
+ * value are invalid.
+ */
+int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
+ int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
#ifdef __cplusplus
};
#endif
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 138e08f..82d2e66 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1018,9 +1018,9 @@
}
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, bool shouldBeSeamless) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
- (int)compatibility);
+ (int)compatibility, (int)shouldBeSeamless);
}
static inline int native_window_set_frame_timeline_vsync(struct ANativeWindow* window,
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 3392d7f..50fe0b7 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -24,7 +24,14 @@
__BEGIN_DECLS
-const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer);
+/**
+ * Get the native handle from an AHardwareBuffer.
+ *
+ * \return a non-NULL native handle on success, NULL if \a buffer is nullptr or the operation fails
+ * for any reason.
+ */
+const native_handle_t* _Nullable AHardwareBuffer_getNativeHandle(
+ const AHardwareBuffer* _Nonnull buffer);
enum CreateFromHandleMethod {
// enum values chosen to match internal GraphicBuffer::HandleWrapMethod
@@ -33,9 +40,9 @@
};
/**
- * Create a AHardwareBuffer from a native handle.
+ * Create an AHardwareBuffer from a native handle.
*
- * This function wraps a native handle in a AHardwareBuffer suitable for use by applications or
+ * This function wraps a native handle in an AHardwareBuffer suitable for use by applications or
* other parts of the system. The contents of desc will be returned by AHardwareBuffer_describe().
*
* If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER, the handle is assumed to be
@@ -44,10 +51,13 @@
*
* If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, the handle will be cloned and the
* clone registered. The AHardwareBuffer will own the cloned handle but not the original.
+ *
+ * \return 0 on success, -EINVAL if \a desc or \a handle or outBuffer is NULL, or an error number if
+ * the operation fails for any reason.
*/
-int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc,
- const native_handle_t* handle, int32_t method,
- AHardwareBuffer** outBuffer);
+int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* _Nonnull desc,
+ const native_handle_t* _Nonnull handle, int32_t method,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer);
/**
* Buffer pixel formats.
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 1b5d20d..24d0e3b 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -4,6 +4,7 @@
AHardwareBuffer_allocate;
AHardwareBuffer_createFromHandle; # llndk # apex
AHardwareBuffer_describe;
+ AHardwareBuffer_getId; # introduced=31
AHardwareBuffer_getNativeHandle; # llndk # apex
AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
@@ -46,6 +47,7 @@
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setFrameRate; # introduced=30
+ ANativeWindow_setFrameRateWithSeamlessness; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index 71b1f9f..ef863b6 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -17,12 +17,11 @@
#define LOG_TAG "AHardwareBuffer_test"
//#define LOG_NDEBUG 0
-#include <android/hardware_buffer.h>
-#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.0/types.h>
-#include <vndk/hardware_buffer.h>
-
#include <gtest/gtest.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <ui/GraphicBuffer.h>
+#include <vndk/hardware_buffer.h>
using namespace android;
using android::hardware::graphics::common::V1_0::BufferUsage;
@@ -131,3 +130,43 @@
AHardwareBuffer_release(buffer);
AHardwareBuffer_release(otherBuffer);
}
+
+TEST(AHardwareBufferTest, GetIdTest) {
+ const uint32_t testWidth = 4;
+ const uint32_t testHeight = 4;
+ const uint32_t testLayers = 1;
+
+ AHardwareBuffer* ahb1 = nullptr;
+ uint64_t id1 = 0;
+ const AHardwareBuffer_Desc desc = {
+ .width = testWidth,
+ .height = testHeight,
+ .layers = testLayers,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+ };
+ int res = AHardwareBuffer_allocate(&desc, &ahb1);
+ EXPECT_EQ(NO_ERROR, res);
+ EXPECT_NE(nullptr, ahb1);
+ EXPECT_EQ(0, AHardwareBuffer_getId(ahb1, &id1));
+ const GraphicBuffer* gb1 = AHardwareBuffer_to_GraphicBuffer(ahb1);
+ EXPECT_NE(nullptr, gb1);
+ EXPECT_EQ(id1, gb1->getId());
+ EXPECT_NE(id1, 0);
+
+ sp<GraphicBuffer> gb2(new GraphicBuffer(testWidth,
+ testHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ testLayers,
+ GraphicBuffer::USAGE_SW_READ_RARELY,
+ std::string("test")));
+ EXPECT_NE(nullptr, gb2.get());
+ const AHardwareBuffer* ahb2 = AHardwareBuffer_from_GraphicBuffer(gb2.get());
+ EXPECT_NE(nullptr, ahb2);
+ uint64_t id2 = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(ahb2, &id2));
+ EXPECT_EQ(id2, gb2->getId());
+ EXPECT_NE(id2, 0);
+
+ EXPECT_NE(id1, id2);
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index cdb3d20..2e4bd99 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -24,6 +24,7 @@
"liblog",
"libnativewindow",
"libsync",
+ "libui",
"libutils",
"android.hardware.graphics.common@1.0",
],
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1f25fbf..579260b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -252,8 +252,8 @@
// initialize the renderer while GL is current
std::unique_ptr<SkiaGLRenderEngine> engine =
- std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
- protectedContext, protectedPlaceholder);
+ std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
+ protectedPlaceholder);
ALOGI("OpenGL ES informations:");
ALOGI("vendor : %s", extensions.getVendor());
@@ -306,38 +306,52 @@
}
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
- EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
+ EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
: mEGLDisplay(display),
- mEGLConfig(config),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
mProtectedPlaceholderSurface(protectedPlaceholder),
mUseColorManagement(args.useColorManagement) {
- // Suppress unused field warnings for things we definitely will need/use
- // These EGL fields will all be needed for toggling between protected & unprotected contexts
- // Or we need different RE instances for that
- (void)mEGLDisplay;
- (void)mEGLConfig;
- (void)mEGLContext;
- (void)mPlaceholderSurface;
- (void)mProtectedEGLContext;
- (void)mProtectedPlaceholderSurface;
-
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
options.fPreferExternalImagesOverES3 = true;
options.fDisableDistanceFieldPaths = true;
- mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);
+ mGrContext = GrDirectContext::MakeGL(glInterface, options);
+ if (useProtectedContext(true)) {
+ mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
+ useProtectedContext(false);
+ }
if (args.supportsBackgroundBlur) {
mBlurFilter = new BlurFilter();
}
}
+bool SkiaGLRenderEngine::supportsProtectedContent() const {
+ return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
+ if (useProtectedContext == mInProtectedContext) {
+ return true;
+ }
+ if (useProtectedContext && supportsProtectedContent()) {
+ return false;
+ }
+ const EGLSurface surface =
+ useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
+ const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+ const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+ if (success) {
+ mInProtectedContext = useProtectedContext;
+ }
+ return success;
+}
+
base::unique_fd SkiaGLRenderEngine::flush() {
ATRACE_CALL();
if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
@@ -471,22 +485,23 @@
return BAD_VALUE;
}
+ auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto& cache = mInProtectedContext ? mProtectedSurfaceCache : mSurfaceCache;
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
-
LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
"missing usage");
sk_sp<SkSurface> surface;
if (useFramebufferCache) {
- auto iter = mSurfaceCache.find(buffer->getId());
- if (iter != mSurfaceCache.end()) {
+ auto iter = cache.find(buffer->getId());
+ if (iter != cache.end()) {
ALOGV("Cache hit!");
surface = iter->second;
}
}
if (!surface) {
- surface = SkSurface::MakeFromAHardwareBuffer(mGrContext.get(), buffer->toAHardwareBuffer(),
+ surface = SkSurface::MakeFromAHardwareBuffer(grContext.get(), buffer->toAHardwareBuffer(),
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
mUseColorManagement
? toColorSpace(display.outputDataspace)
@@ -494,7 +509,7 @@
nullptr);
if (useFramebufferCache && surface) {
ALOGD("Adding to cache");
- mSurfaceCache.insert({buffer->getId(), surface});
+ cache.insert({buffer->getId(), surface});
}
}
if (!surface) {
@@ -547,9 +562,11 @@
const auto layerRect = drawTransform.mapRect(dest);
if (layer->backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
- auto blurredSurface =
- mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius, layerRect);
+ auto blurredSurface = mBlurFilter->generate(canvas, surface,
+ layer->backgroundBlurRadius, layerRect);
cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+
+ drawBlurRegion(canvas, getBlurRegion(layer), drawTransform, blurredSurface);
}
if (layer->blurRegions.size() > 0) {
for (auto region : layer->blurRegions) {
@@ -635,7 +652,16 @@
matrix.postConcat(texMatrix);
matrix.postScale(rotatedBufferWidth, rotatedBufferHeight);
- sk_sp<SkShader> shader = image->makeShader(matrix);
+ sk_sp<SkShader> shader;
+
+ if (layer->source.buffer.useTextureFiltering) {
+ shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(
+ {SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ &matrix);
+ } else {
+ shader = image->makeShader(matrix);
+ }
if (mUseColorManagement &&
needsToneMapping(layer->sourceDataspace, display.outputDataspace)) {
@@ -651,6 +677,8 @@
} else {
paint.setShader(shader);
}
+ // Make sure to take into the account the alpha set on the layer.
+ paint.setAlphaf(layer->alpha);
} else {
ATRACE_NAME("DrawColor");
const auto color = layer->source.solidColor;
@@ -663,13 +691,13 @@
paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
- canvas->save();
- canvas->concat(drawTransform);
-
for (const auto effectRegion : layer->blurRegions) {
- drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]);
+ drawBlurRegion(canvas, effectRegion, drawTransform,
+ cachedBlurs[effectRegion.blurRadius]);
}
+ canvas->save();
+ canvas->concat(drawTransform);
if (layer->shadow.length > 0) {
const auto rect = layer->geometry.roundedCornersRadius > 0
? getSkRect(layer->geometry.roundedCornersCrop)
@@ -703,7 +731,7 @@
} else {
ATRACE_BEGIN("Submit(sync=false)");
}
- bool success = mGrContext->submit(requireSync);
+ bool success = grContext->submit(requireSync);
ATRACE_END();
if (!success) {
ALOGE("Failed to flush RenderEngine commands");
@@ -730,6 +758,21 @@
return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
}
+inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) {
+ const auto rect = getSkRect(layer->geometry.boundaries);
+ const auto cornersRadius = layer->geometry.roundedCornersRadius;
+ return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius),
+ .cornerRadiusTL = cornersRadius,
+ .cornerRadiusTR = cornersRadius,
+ .cornerRadiusBL = cornersRadius,
+ .cornerRadiusBR = cornersRadius,
+ .alpha = 1,
+ .left = static_cast<int>(rect.fLeft),
+ .top = static_cast<int>(rect.fTop),
+ .right = static_cast<int>(rect.fRight),
+ .bottom = static_cast<int>(rect.fBottom)};
+}
+
inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
}
@@ -777,18 +820,19 @@
}
void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
- const SkRect& layerBoundaries,
+ const SkMatrix& drawTransform,
sk_sp<SkSurface> blurredSurface) {
ATRACE_CALL();
+
SkPaint paint;
paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
- const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
- effectRegion.bottom);
-
- const auto matrix = mBlurFilter->getShaderMatrix(
- SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top()));
+ const auto matrix = mBlurFilter->getShaderMatrix();
paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix));
+ auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+ effectRegion.bottom);
+ drawTransform.mapRect(&rect);
+
if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
const SkVector radii[4] =
@@ -879,6 +923,7 @@
void SkiaGLRenderEngine::cleanFramebufferCache() {
mSurfaceCache.clear();
+ mProtectedSurfaceCache.clear();
}
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index c65e431..965cb41 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -40,8 +40,8 @@
class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
public:
static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
- EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
+ SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
+ EGLSurface placeholder, EGLContext protectedContext,
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override{};
@@ -51,6 +51,9 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
void cleanFramebufferCache() override;
+ bool isProtected() const override { return mInProtectedContext; }
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
protected:
void dump(std::string& /*result*/) override{};
@@ -67,6 +70,7 @@
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
inline SkRRect getRoundedRect(const LayerSettings* layer);
+ inline BlurRegion getBlurRegion(const LayerSettings* layer);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkMatrix getDrawTransform(const LayerSettings* layer, const SkMatrix& screenTransform);
@@ -76,11 +80,10 @@
bool waitFence(base::unique_fd fenceFd);
void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
const ShadowSettings& shadowSettings);
- void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds,
- sk_sp<SkSurface> blurrendSurface);
+ void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion,
+ const SkMatrix& drawTransform, sk_sp<SkSurface> blurrendSurface);
EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
EGLContext mEGLContext;
EGLSurface mPlaceholderSurface;
EGLContext mProtectedEGLContext;
@@ -99,9 +102,14 @@
sp<Fence> mLastDrawFence;
+ // Graphics context used for creating surfaces and submitting commands
sk_sp<GrDirectContext> mGrContext;
+ // Same as above, but for protected content (eg. DRM)
+ sk_sp<GrDirectContext> mProtectedGrContext;
std::unordered_map<uint64_t, sk_sp<SkSurface>> mSurfaceCache;
+ std::unordered_map<uint64_t, sk_sp<SkSurface>> mProtectedSurfaceCache;
+ bool mInProtectedContext = false;
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index dfb306f..c6b8d3a 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "BlurFilter.h"
#include <SkCanvas.h>
#include <SkData.h>
@@ -38,11 +40,11 @@
half4 main(float2 xy) {
float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
- float4 c = float4(sample(input, scaled_xy));
- c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, in_blurOffset.y)));
- c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
- c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y)));
- c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));
+ half4 c = sample(input, scaled_xy);
+ c += sample(input, scaled_xy + float2( in_blurOffset.x, in_blurOffset.y));
+ c += sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y));
+ c += sample(input, scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y));
+ c += sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y));
return half4(c.rgb * 0.2, 1.0);
}
@@ -57,8 +59,6 @@
sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
const uint32_t blurRadius, SkRect rect) const {
- ATRACE_CALL();
-
// Kawase is an approximation of Gaussian, but it behaves differently from it.
// A radius transformation is required for approximating them, and also to introduce
// non-integer steps, necessary to smoothly interpolate large radii.
@@ -77,7 +77,7 @@
const float stepY = radiusByPasses;
// start by drawing and downscaling and doing the first blur pass
- SkFilterOptions linear = {SkSamplingMode::kLinear, SkMipmapMode::kNone};
+ SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
blurBuilder.child("input") =
input->makeImageSnapshot()->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
@@ -126,30 +126,16 @@
}
lastDrawTarget = readSurface;
}
- lastDrawTarget->flushAndSubmit();
+
+ {
+ ATRACE_NAME("Flush Offscreen Surfaces");
+ lastDrawTarget->flushAndSubmit();
+ }
return lastDrawTarget;
}
-sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
- const uint32_t blurRadius, SkRect rect) const {
- ATRACE_CALL();
- sk_sp<SkSurface> surface = generate(canvas, input, blurRadius, rect);
- const auto image = surface->makeImageSnapshot();
-
- SkPaint paint;
- paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale)));
- paint.setFilterQuality(kLow_SkFilterQuality);
- paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
-
- canvas->drawRect(rect, paint);
-
- return surface;
-}
-
-SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const {
- SkMatrix matrix;
- matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale));
- return matrix;
+SkMatrix BlurFilter::getShaderMatrix() const {
+ return SkMatrix::MakeScale(kInverseInputScale);
}
} // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index c0a92f6..734bfcb 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -50,11 +50,8 @@
// Execute blur, saving it to a texture
sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
SkRect rect) const;
- // Same as generate but also drawing to the screen
- sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
- SkRect rect) const;
// Returns a matrix that should be applied to the blur shader
- SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const;
+ SkMatrix getShaderMatrix() const;
private:
sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index f799ce4..636fbde 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -1052,7 +1052,7 @@
Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
- ALOGW("allocator 3.x is not supported");
+ ALOGW("allocator 4.x is not supported");
return;
}
}
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 340d7bf..d5a19d3 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -80,6 +80,7 @@
cc_library_shared {
name: "libdvr.google",
+ system_ext_specific: true,
owner: "google",
cflags: cflags,
header_libs: ["libdvr_headers"],
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 1ce9c99..b3534de 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -45,6 +45,7 @@
cc_binary {
name: "pdx_tool",
+ system_ext_specific: true,
defaults: ["pdx_default_transport_compiler_defaults"],
srcs: [
"pdx_tool.cpp",
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 1afc693..76fd7f0 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -514,6 +514,8 @@
if (so) {
return so;
}
+ ALOGE("Could not load %s from updatable gfx driver namespace: %s.", name.c_str(),
+ dlerror());
}
return nullptr;
}
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 77a0716..eafb5ab 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -405,6 +405,11 @@
mListener->notifyDeviceReset(args);
}
+void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ // pass through
+ mListener->notifyPointerCaptureChanged(args);
+}
+
void InputClassifier::setMotionClassifier(
std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 03510a6..6965940 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -230,6 +230,7 @@
virtual void notifyMotion(const NotifyMotionArgs* args) override;
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
virtual void dump(std::string& dump) override;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 84838ec..49a813e 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -48,7 +48,6 @@
listener->notifyConfigurationChanged(this);
}
-
// --- NotifyKeyArgs ---
NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
@@ -90,7 +89,6 @@
listener->notifyKey(this);
}
-
// --- NotifyMotionArgs ---
NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
@@ -189,7 +187,6 @@
listener->notifyMotion(this);
}
-
// --- NotifySwitchArgs ---
NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
@@ -214,7 +211,6 @@
listener->notifySwitch(this);
}
-
// --- NotifyDeviceResetArgs ---
NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
@@ -231,6 +227,23 @@
listener->notifyDeviceReset(this);
}
+// --- NotifyPointerCaptureChangedArgs ---
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime,
+ bool enabled)
+ : NotifyArgs(id, eventTime), enabled(enabled) {}
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+ const NotifyPointerCaptureChangedArgs& other)
+ : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {}
+
+bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
+ return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled;
+}
+
+void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
+ listener->notifyPointerCaptureChanged(this);
+}
// --- QueuedInputListener ---
@@ -278,6 +291,11 @@
mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
}
+void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ traceEvent(__func__, args->id);
+ mArgsQueue.push_back(new NotifyPointerCaptureChangedArgs(*args));
+}
+
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
@@ -288,5 +306,4 @@
mArgsQueue.clear();
}
-
} // namespace android
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 33520b2..8073a93 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -19,6 +19,25 @@
},
{
"name": "libinputservice_test"
+ },
+ {
+ "name": "CtsInputTestCases"
+ },
+ {
+ "name": "CtsViewTestCases",
+ "options": [
+ {
+ "include-filter": "android.view.cts.MotionEventTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSecurityTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.MotionEventTest"
+ }
+ ]
}
]
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index c2d165e..9fea298 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -50,12 +50,18 @@
private:
void notifyConfigurationChanged(nsecs_t) override {}
- std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>&,
- const sp<IBinder>&, const std::string& name) override {
- ALOGE("The window is not responding : %s", name.c_str());
- return 0s;
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+ ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
}
+ void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
+ const std::string& reason) override {
+ ALOGE("Connection is not responding: %s", reason.c_str());
+ }
+
+ void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {}
+
void notifyInputChannelBroken(const sp<IBinder>&) override {}
void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e5d208a..91e9536 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2336,7 +2336,7 @@
}
std::string InputDispatcher::getApplicationWindowLabel(
- const std::shared_ptr<InputApplicationHandle>& applicationHandle,
+ const InputApplicationHandle* applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
if (applicationHandle != nullptr) {
if (windowHandle != nullptr) {
@@ -3542,6 +3542,15 @@
}
}
+void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
+ args->enabled ? "true" : "false");
+#endif
+
+ // TODO(prabirmsp): Implement.
+}
+
InputEventInjectionResult InputDispatcher::injectInputEvent(
const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
@@ -4490,7 +4499,7 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
"portalToDisplayId=%d, paused=%s, focusable=%s, "
"hasWallpaper=%s, visible=%s, alpha=%.2f, "
- "flags=%s, type=0x%08x, "
+ "flags=%s, type=%s, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
"applicationInfo=%s, "
"touchableRegion=",
@@ -4501,7 +4510,7 @@
toString(windowInfo->hasWallpaper),
toString(windowInfo->visible), windowInfo->alpha,
windowInfo->flags.string().c_str(),
- static_cast<int32_t>(windowInfo->type),
+ NamedEnum::string(windowInfo->type).c_str(),
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
@@ -4510,11 +4519,13 @@
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
- "ms, trustedOverlay=%s, hasToken=%s\n",
+ "ms, trustedOverlay=%s, hasToken=%s, "
+ "touchOcclusionMode=%s\n",
windowInfo->ownerPid, windowInfo->ownerUid,
millis(windowInfo->dispatchingTimeout),
toString(windowInfo->trustedOverlay),
- toString(windowInfo->token != nullptr));
+ toString(windowInfo->token != nullptr),
+ toString(windowInfo->touchOcclusionMode).c_str());
windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
@@ -4922,24 +4933,21 @@
sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = nullptr;
+ std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible);
commandEntry->connectionToken = connectionToken;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) {
- std::string reason = android::base::StringPrintf("%s does not have a focused window",
- application->getName().c_str());
+void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
+ std::string reason =
+ StringPrintf("%s does not have a focused window", application->getName().c_str());
+ updateLastAnrStateLocked(*application, reason);
- updateLastAnrStateLocked(application, reason);
-
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = application;
- commandEntry->reason = std::move(reason);
+ std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = std::move(application);
postCommandLocked(std::move(commandEntry));
}
@@ -4956,9 +4964,9 @@
updateLastAnrStateLocked(windowLabel, reason);
}
-void InputDispatcher::updateLastAnrStateLocked(
- const std::shared_ptr<InputApplicationHandle>& application, const std::string& reason) {
- const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+void InputDispatcher::updateLastAnrStateLocked(const InputApplicationHandle& application,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(&application, nullptr);
updateLastAnrStateLocked(windowLabel, reason);
}
@@ -5006,24 +5014,36 @@
mLock.lock();
}
-void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- const sp<IBinder>& token = commandEntry->connectionToken;
- const std::chrono::nanoseconds timeoutExtension =
- mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
+
+ mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyConnectionUnresponsive(commandEntry->connectionToken, commandEntry->reason);
mLock.lock();
- if (timeoutExtension > 0s) {
- extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
- } else {
- // stop waking up for events in this connection, it is already not responding
- sp<Connection> connection = getConnectionLocked(token);
- if (connection == nullptr) {
- return;
- }
- cancelEventsForAnrLocked(connection);
+ // stop waking up for events in this connection, it is already not responding
+ sp<Connection> connection = getConnectionLocked(commandEntry->connectionToken);
+ if (connection == nullptr) {
+ return;
}
+ cancelEventsForAnrLocked(connection);
+}
+
+void InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyConnectionResponsive(commandEntry->connectionToken);
+
+ mLock.lock();
}
void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
@@ -5034,35 +5054,6 @@
mLock.lock();
}
-void InputDispatcher::extendAnrTimeoutsLocked(
- const std::shared_ptr<InputApplicationHandle>& application,
- const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
- if (connectionToken == nullptr && application != nullptr) {
- // The ANR happened because there's no focused window
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
- mAwaitedFocusedApplication = application;
- }
-
- sp<Connection> connection = getConnectionLocked(connectionToken);
- if (connection == nullptr) {
- // It's possible that the connection already disappeared. No action necessary.
- return;
- }
-
- ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
- connection->inputChannel->getName().c_str(), millis(timeoutExtension));
-
- connection->responsive = true;
- const nsecs_t newTimeout = now() + timeoutExtension.count();
- for (DispatchEntry* entry : connection->waitQueue) {
- if (newTimeout >= entry->timeoutTime) {
- // Already removed old entries when connection was marked unresponsive
- entry->timeoutTime = newTimeout;
- mAnrTracker.insert(entry->timeoutTime, connectionToken);
- }
- }
-}
-
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry& entry = *(commandEntry->keyEntry);
@@ -5150,10 +5141,19 @@
if (dispatchEntryIt != connection->waitQueue.end()) {
dispatchEntry = *dispatchEntryIt;
connection->waitQueue.erase(dispatchEntryIt);
- mAnrTracker.erase(dispatchEntry->timeoutTime,
- connection->inputChannel->getConnectionToken());
+ const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+ mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
if (!connection->responsive) {
connection->responsive = isConnectionResponsive(*connection);
+ if (connection->responsive) {
+ // The connection was unresponsive, and now it's responsive. Tell the policy
+ // about it so that it can stop ANR.
+ std::unique_ptr<CommandEntry> connectionResponsiveCommand =
+ std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible);
+ connectionResponsiveCommand->connectionToken = connectionToken;
+ postCommandLocked(std::move(connectionResponsiveCommand));
+ }
}
traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5387c40..9aaae74 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -94,6 +94,7 @@
virtual void notifyMotion(const NotifyMotionArgs* args) override;
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
virtual android::os::InputEventInjectionResult injectInputEvent(
const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
@@ -419,9 +420,6 @@
// Once a connection becomes unresponsive, its entries are removed from AnrTracker to
// prevent unneeded wakeups.
AnrTracker mAnrTracker GUARDED_BY(mLock);
- void extendAnrTimeoutsLocked(const std::shared_ptr<InputApplicationHandle>& application,
- const sp<IBinder>& connectionToken,
- std::chrono::nanoseconds timeoutExtension) REQUIRES(mLock);
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
@@ -475,9 +473,8 @@
int32_t y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const;
- std::string getApplicationWindowLabel(
- const std::shared_ptr<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle);
+ std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
+ const sp<InputWindowHandle>& windowHandle);
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
@@ -554,11 +551,11 @@
void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
REQUIRES(mLock);
void onAnrLocked(const Connection& connection) REQUIRES(mLock);
- void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
+ void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
- void updateLastAnrStateLocked(const std::shared_ptr<InputApplicationHandle>& application,
+ void updateLastAnrStateLocked(const InputApplicationHandle& application,
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
@@ -568,7 +565,11 @@
REQUIRES(mLock);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyConnectionUnresponsiveLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
+ void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 463c5f1..1125257 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -45,11 +45,25 @@
/* Notifies the system that a configuration change has occurred. */
virtual void notifyConfigurationChanged(nsecs_t when) = 0;
- /* Notifies the system that an application is not responding.
- * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual std::chrono::nanoseconds notifyAnr(
- const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) = 0;
+ /* Notifies the system that an application does not have a focused window.
+ */
+ virtual void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+ /* Notifies the system that a connection just became unresponsive. This indicates that ANR
+ * should be raised for this connection. The connection is identified via token.
+ * The string reason contains information about the input event that we haven't received
+ * a response for.
+ */
+ virtual void notifyConnectionUnresponsive(const sp<IBinder>& token,
+ const std::string& reason) = 0;
+
+ /* Notifies the system that a connection just became responsive. This is only called after the
+ * connection was first marked "unresponsive". This indicates that ANR dialog (if any) should
+ * no longer should be shown to the user. The connection is eligible to cause a new ANR in the
+ * future.
+ */
+ virtual void notifyConnectionResponsive(const sp<IBinder>& token) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 8317b05..58eb915 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -181,6 +181,22 @@
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
+/* Describes a change in the state of Pointer Capture. */
+struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
+ bool enabled;
+
+ inline NotifyPointerCaptureChangedArgs() {}
+
+ NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled);
+
+ NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
+
+ bool operator==(const NotifyPointerCaptureChangedArgs& rhs) const;
+
+ virtual ~NotifyPointerCaptureChangedArgs() {}
+
+ virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
/*
* The interface used by the InputReader to notify the InputListener about input events.
@@ -196,6 +212,7 @@
virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
};
@@ -210,11 +227,12 @@
public:
explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
- virtual void notifyKey(const NotifyKeyArgs* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
- virtual void notifySwitch(const NotifySwitchArgs* args);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
void flush();
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 2028b91..e263f01 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -338,24 +338,30 @@
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
- if (changes) {
- ALOGI("Reconfiguring input devices, changes=%s",
- InputReaderConfiguration::changesToString(changes).c_str());
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (!changes) return;
- if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
- updatePointerDisplayLocked();
- }
+ ALOGI("Reconfiguring input devices, changes=%s",
+ InputReaderConfiguration::changesToString(changes).c_str());
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
- mEventHub->requestReopenDevices();
- } else {
- for (auto& devicePair : mDevices) {
- std::shared_ptr<InputDevice>& device = devicePair.second;
- device->configure(now, &mConfig, changes);
- }
+ if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
+ updatePointerDisplayLocked();
+ }
+
+ if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+ mEventHub->requestReopenDevices();
+ } else {
+ for (auto& devicePair : mDevices) {
+ std::shared_ptr<InputDevice>& device = devicePair.second;
+ device->configure(now, &mConfig, changes);
}
}
+
+ if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
+ const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+ mConfig.pointerCapture);
+ mQueuedListener->notifyPointerCaptureChanged(&args);
+ }
}
void InputReader::updateGlobalMetaStateLocked() {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 40471b2..5ab2ae3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -126,35 +126,62 @@
// This function must be called soon after the expected ANR timer starts,
// because we are also checking how much time has passed.
- void assertNotifyAnrWasCalled(
+ void assertNotifyNoFocusedWindowAnrWasCalled(
std::chrono::nanoseconds timeout,
- const std::shared_ptr<InputApplicationHandle>& expectedApplication,
- const sp<IBinder>& expectedToken) {
- std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData;
- ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
- ASSERT_EQ(expectedApplication, anrData.first);
- ASSERT_EQ(expectedToken, anrData.second);
+ const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
+ std::shared_ptr<InputApplicationHandle> application;
+ { // acquire lock
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ ASSERT_NO_FATAL_FAILURE(
+ application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
+ } // release lock
+ ASSERT_EQ(expectedApplication, application);
}
- std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
- std::chrono::nanoseconds timeout) {
- const std::chrono::time_point start = std::chrono::steady_clock::now();
+ void assertNotifyConnectionUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+ const sp<IBinder>& expectedConnectionToken) {
+ sp<IBinder> connectionToken = getUnresponsiveConnectionToken(timeout);
+ ASSERT_EQ(expectedConnectionToken, connectionToken);
+ }
+
+ void assertNotifyConnectionResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
+ sp<IBinder> connectionToken = getResponsiveConnectionToken();
+ ASSERT_EQ(expectedConnectionToken, connectionToken);
+ }
+
+ sp<IBinder> getUnresponsiveConnectionToken(std::chrono::nanoseconds timeout) {
std::unique_lock lock(mLock);
- std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(timeout, mAnrConnectionTokens, lock);
+ }
+
+ sp<IBinder> getResponsiveConnectionToken() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(0s, mResponsiveConnectionTokens, lock);
+ }
+
+ // All three ANR-related callbacks behave the same way, so we use this generic function to wait
+ // for a specific container to become non-empty. When the container is non-empty, return the
+ // first entry from the container and erase it.
+ template <class T>
+ T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
// If there is an ANR, Dispatcher won't be idle because there are still events
// in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
// before checking if ANR was called.
- // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
- // it some time to act. 100ms seems reasonable.
- mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
- return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
- });
+ // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
+ // to provide it some time to act. 100ms seems reasonable.
+ mNotifyAnr.wait_for(lock, timeToWait,
+ [&storage]() REQUIRES(mLock) { return !storage.empty(); });
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
- ADD_FAILURE() << "Did not receive ANR callback";
- return {};
+ if (storage.empty()) {
+ ADD_FAILURE() << "Did not receive the ANR callback";
+ return nullptr;
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
@@ -165,17 +192,18 @@
<< std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
<< "ms instead";
}
- std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> result =
- std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
- mAnrApplications.pop();
- mAnrWindowTokens.pop();
- return result;
+ T token = storage.front();
+ storage.pop();
+ return token;
}
void assertNotifyAnrWasNotCalled() {
std::scoped_lock lock(mLock);
ASSERT_TRUE(mAnrApplications.empty());
- ASSERT_TRUE(mAnrWindowTokens.empty());
+ ASSERT_TRUE(mAnrConnectionTokens.empty());
+ ASSERT_TRUE(mResponsiveConnectionTokens.empty())
+ << "ANR was not called, but please also consume the 'connection is responsive' "
+ "signal";
}
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -183,8 +211,6 @@
mConfig.keyRepeatDelay = delay;
}
- void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
-
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -194,23 +220,33 @@
// ANR handling
std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
- std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mAnrConnectionTokens GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mResponsiveConnectionTokens GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
- std::chrono::nanoseconds mAnrTimeout = 0ms;
void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
- std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>& application,
- const sp<IBinder>& windowToken,
- const std::string&) override {
+ void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
+ const std::string&) override {
std::scoped_lock lock(mLock);
- mAnrApplications.push(application);
- mAnrWindowTokens.push(windowToken);
+ mAnrConnectionTokens.push(connectionToken);
mNotifyAnr.notify_all();
- return mAnrTimeout;
+ }
+
+ void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {
+ std::scoped_lock lock(mLock);
+ mResponsiveConnectionTokens.push(connectionToken);
+ mNotifyAnr.notify_all();
+ }
+
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+ std::scoped_lock lock(mLock);
+ mAnrApplications.push(applicationHandle);
+ mNotifyAnr.notify_all();
}
void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -811,6 +847,12 @@
expectedFlags);
}
+ void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId,
+ expectedFlags);
+ }
+
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
ASSERT_NE(mInputReceiver, nullptr)
<< "Cannot consume events from a window with no receiver";
@@ -1761,9 +1803,11 @@
std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(DISPATCHING_TIMEOUT,
+ monitor.getToken());
monitor.finishEvent(*consumeSeq);
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(monitor.getToken());
}
TEST_F(InputDispatcherTest, TestMoveEvent) {
@@ -2882,13 +2926,13 @@
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
- // The remaining lines are not really needed for the test, but kept as a sanity check
mWindow->finishEvent(*sequenceNum);
mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
}
// Send a key to the app and have the app not respond right away.
@@ -2898,7 +2942,7 @@
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
ASSERT_TRUE(mDispatcher->waitForIdle());
}
@@ -2924,19 +2968,16 @@
InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
ASSERT_TRUE(mDispatcher->waitForIdle());
}
// We have a focused application, but no focused window
-// If the policy wants to keep waiting on the focused window to be added, make sure
-// that this timeout extension is honored and ANR is raised again.
-TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+// Make sure that we don't notify policy twice about the same ANR.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
mWindow->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
mWindow->consumeFocusEvent(false);
- const std::chrono::duration timeout = 5ms;
- mFakePolicy->setAnrTimeout(timeout);
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
@@ -2947,14 +2988,14 @@
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(appTimeout, mApplication);
- // After the extended time has passed, ANR should be raised again
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ std::this_thread::sleep_for(appTimeout);
+ // ANR should not be raised again. It is up to policy to do that if it desires.
+ mFakePolicy->assertNotifyAnrWasNotCalled();
- // If we stop extending the timeout, dispatcher should go to idle.
- // Another ANR may be raised during this time
- mFakePolicy->setAnrTimeout(0ms);
+ // If we now get a focused window, the ANR should stop, but the policy handles that via
+ // 'notifyFocusChanged' callback. This is implemented in the policy so we can't test it here.
ASSERT_TRUE(mDispatcher->waitForIdle());
}
@@ -2971,7 +3012,7 @@
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
// Future focused events get dropped right away
ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
@@ -3006,7 +3047,7 @@
// We have now sent down and up. Let's consume first event and then ANR on the second.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
}
// If an app is not responding to a key event, gesture monitors should continue to receive
@@ -3023,7 +3064,7 @@
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
// New tap will go to the gesture monitor, but not to the window
tapOnWindow();
@@ -3032,6 +3073,7 @@
mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
monitor.assertNoEvents();
}
@@ -3050,7 +3092,7 @@
mWindow->consumeMotionDown();
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
// New tap will go to the gesture monitor, but not to the window
tapOnWindow();
@@ -3059,6 +3101,7 @@
mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
monitor.assertNoEvents();
}
@@ -3075,46 +3118,43 @@
mWindow->consumeMotionDown();
// Block on ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->consumeMotionUp(); // Now the connection should be healthy again
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
tapOnWindow();
mWindow->consumeMotionDown();
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->consumeMotionUp();
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
mWindow->assertNoEvents();
}
-// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
-// is honored
-TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
- const std::chrono::duration timeout = 5ms;
- mFakePolicy->setAnrTimeout(timeout);
-
+// If a connection remains unresponsive for a while, make sure policy is only notified once about
+// it.
+TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
- mWindow->getToken());
-
- // Since the policy wanted to extend ANR, make sure it is called again after the extension
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
- mFakePolicy->setAnrTimeout(0ms);
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
std::this_thread::sleep_for(windowTimeout);
- // We are not checking if ANR has been called, because it may have been called again by the
- // time we set the timeout to 0
-
- // When the policy finally says stop, we should get ACTION_CANCEL
+ // 'notifyConnectionUnresponsive' should only be called once per connection
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+ // When the ANR happened, dispatcher should abort the current event stream via ACTION_CANCEL
mWindow->consumeMotionDown();
mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
mWindow->assertNoEvents();
+ mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
/**
@@ -3278,17 +3318,22 @@
FOCUSED_WINDOW_LOCATION));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
- std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
- ASSERT_TRUE(focusedSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
- mFocusedWindow->getToken());
-
- mFocusedWindow->finishEvent(*focusedSequenceNum);
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
+ // sequence to make it consistent
+ mFocusedWindow->consumeMotionCancel();
mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+ mFocusedWindow->consumeMotionDown();
+ // This cancel is generated because the connection was unresponsive
+ mFocusedWindow->consumeMotionCancel();
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
// If we have 2 windows with identical timeouts that are both unresponsive,
@@ -3301,19 +3346,31 @@
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData1 =
- mFakePolicy->getNotifyAnrData(10ms);
- std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData2 =
- mFakePolicy->getNotifyAnrData(0ms);
+ sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveConnectionToken(10ms);
+ sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveConnectionToken(0ms);
// We don't know which window will ANR first. But both of them should happen eventually.
- ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
- mFocusedWindow->getToken() == anrData2.second);
- ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
- mUnfocusedWindow->getToken() == anrData2.second);
+ ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
+ mFocusedWindow->getToken() == anrConnectionToken2);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == anrConnectionToken1 ||
+ mUnfocusedWindow->getToken() == anrConnectionToken2);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ mFocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionUp();
+ mUnfocusedWindow->consumeMotionOutside();
+
+ sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveConnectionToken();
+ sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveConnectionToken();
+
+ // Both applications should be marked as responsive, in any order
+ ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
+ mFocusedWindow->getToken() == responsiveToken2);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == responsiveToken1 ||
+ mUnfocusedWindow->getToken() == responsiveToken2);
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
// If a window is already not responding, the second tap on the same window should be ignored.
@@ -3330,8 +3387,7 @@
ASSERT_TRUE(upEventSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
- mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
@@ -3351,7 +3407,8 @@
ASSERT_TRUE(mDispatcher->waitForIdle());
// The second tap did not go to the focused window
mFocusedWindow->assertNoEvents();
- // should not have another ANR after the window just became healthy again
+ // Since all events are finished, connection should be deemed healthy again
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -3437,6 +3494,7 @@
mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
mFocusedWindow->assertNoEvents();
mUnfocusedWindow->assertNoEvents();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
// When the touch stream is split across 2 windows, and one of them does not respond,
@@ -3462,8 +3520,7 @@
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
- mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
mUnfocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionDown();
@@ -3481,10 +3538,12 @@
} else {
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
}
-
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+
mUnfocusedWindow->assertNoEvents();
mFocusedWindow->assertNoEvents();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
/**
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a72d5c3..99eaac6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1792,6 +1792,29 @@
mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_C));
}
+TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
+ NotifyPointerCaptureChangedArgs args;
+
+ mFakePolicy->setPointerCapture(true);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyCaptureWasCalled(&args);
+ ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled.";
+
+ mFakePolicy->setPointerCapture(false);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyCaptureWasCalled(&args);
+ ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+
+ // Verify that the Pointer Capture state is re-configured correctly when the configuration value
+ // does not change.
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyCaptureWasCalled(&args);
+ ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 9bff166..352995c 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -82,6 +82,14 @@
"Expected notifySwitch() to have been called."));
}
+void TestInputListener::assertNotifyCaptureWasCalled(
+ NotifyPointerCaptureChangedArgs* outEventArgs) {
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifyPointerCaptureChangedArgs>(outEventArgs,
+ "Expected notifyPointerCaptureChanged() "
+ "to have been called."));
+}
+
template <class NotifyArgsType>
void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
std::unique_lock<std::mutex> lock(mLock);
@@ -145,4 +153,8 @@
notify<NotifySwitchArgs>(args);
}
+void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ notify<NotifyPointerCaptureChangedArgs>(args);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index d50c6bc..887d4ea 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -54,6 +54,8 @@
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
+ void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+
private:
template <class NotifyArgsType>
void assertCalled(NotifyArgsType* outEventArgs, std::string message);
@@ -74,16 +76,19 @@
virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+
std::mutex mLock;
std::condition_variable mCondition;
const std::chrono::milliseconds mEventHappenedTimeout;
const std::chrono::milliseconds mEventDidNotHappenTimeout;
- std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
- std::vector<NotifyDeviceResetArgs>, //
- std::vector<NotifyKeyArgs>, //
- std::vector<NotifyMotionArgs>, //
- std::vector<NotifySwitchArgs>> //
+ std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
+ std::vector<NotifyDeviceResetArgs>, //
+ std::vector<NotifyKeyArgs>, //
+ std::vector<NotifyMotionArgs>, //
+ std::vector<NotifySwitchArgs>, //
+ std::vector<NotifyPointerCaptureChangedArgs>> //
mQueues GUARDED_BY(mLock);
};
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index deaf846..63dfe5f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -178,13 +178,17 @@
/// the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+ bool getAutoRefresh() const { return mAutoRefresh; }
+ bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
+
+ std::atomic<bool> mAutoRefresh{false};
+ std::atomic<bool> mSidebandStreamChanged{false};
+
private:
virtual bool fenceHasSignaled() const = 0;
virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
- virtual bool getAutoRefresh() const = 0;
- virtual bool getSidebandStreamChanged() const = 0;
// Latch sideband stream and returns true if the dirty region should be updated.
virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 71b05fd..dc99986 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -196,14 +196,6 @@
return frameNumber;
}
-bool BufferQueueLayer::getAutoRefresh() const {
- return mAutoRefresh;
-}
-
-bool BufferQueueLayer::getSidebandStreamChanged() const {
- return mSidebandStreamChanged;
-}
-
bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
// We need to update the sideband stream if the layer has both a buffer and a sideband stream.
const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
@@ -265,8 +257,10 @@
const uint64_t maxFrameNumberToAcquire =
std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber);
- status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh,
+ bool autoRefresh;
+ status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh,
&queuedBuffer, maxFrameNumberToAcquire);
+ mAutoRefresh = autoRefresh;
if (updateResult == BufferQueue::PRESENT_LATER) {
// Producer doesn't want buffer to be displayed yet. Signal a
// layer update so we check again at the next opportunity.
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index fb8a0c2..b45900e 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -90,9 +90,6 @@
private:
uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
- bool getAutoRefresh() const override;
- bool getSidebandStreamChanged() const override;
-
bool latchSidebandStream(bool& recomputeVisibleRegions) override;
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
@@ -140,11 +137,8 @@
std::vector<BufferData> mQueueItems;
std::atomic<uint64_t> mLastFrameNumberReceived{0};
- bool mAutoRefresh{false};
-
// thread-safe
std::atomic<int32_t> mQueuedFrames{0};
- std::atomic<bool> mSidebandStreamChanged{false};
sp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a64b243..963e541 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -482,13 +482,10 @@
return mCurrentState.frameNumber;
}
-bool BufferStateLayer::getAutoRefresh() const {
- // TODO(marissaw): support shared buffer mode
- return false;
-}
-
-bool BufferStateLayer::getSidebandStreamChanged() const {
- return mSidebandStreamChanged.load();
+void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
+ if (!mAutoRefresh.exchange(autoRefresh)) {
+ mFlinger->signalLayerUpdate();
+ }
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
@@ -526,37 +523,12 @@
return NO_ERROR;
}
- const int32_t layerId = getSequence();
-
- // Reject if the layer is invalid
- uint32_t bufferWidth = s.buffer->width;
- uint32_t bufferHeight = s.buffer->height;
-
- if (s.transform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
-
- if (s.transformToDisplayInverse) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- }
-
- if (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
- (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
- ALOGE("[%s] rejecting buffer: "
- "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
- getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
- return BAD_VALUE;
- }
-
for (auto& handle : mDrawingState.callbackHandles) {
handle->latchTime = latchTime;
handle->frameNumber = mDrawingState.frameNumber;
}
+ const int32_t layerId = getSequence();
mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
std::make_shared<FenceTime>(mDrawingState.acquireFence));
mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 104a13b..42be62a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -100,6 +100,7 @@
Rect getBufferSize(const State& s) const override;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
Layer::RoundedCornerState getRoundedCornerState() const override;
+ void setAutoRefresh(bool autoRefresh) override;
// -----------------------------------------------------------------------
@@ -123,9 +124,6 @@
uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
- bool getAutoRefresh() const override;
- bool getSidebandStreamChanged() const override;
-
bool latchSidebandStream(bool& recomputeVisibleRegions) override;
bool hasFrameUpdate() const override;
@@ -149,8 +147,6 @@
std::unique_ptr<renderengine::Image> mTextureImage;
- std::atomic<bool> mSidebandStreamChanged{false};
-
mutable uint64_t mFrameNumber{0};
uint64_t mFrameCounter{0};
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 4b4c050..901e19a 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -64,7 +64,7 @@
PowerAdvisor::PowerAdvisor()
: mUseUpdateImminentTimer(getUpdateTimeout() > 0),
mUpdateImminentTimer(
- OneShotTimer::Interval(getUpdateTimeout()),
+ "UpdateImminentTimer", OneShotTimer::Interval(getUpdateTimeout()),
/* resetCallback */ [this] { mSendUpdateImminent.store(false); },
/* timeoutCallback */ [this] { mSendUpdateImminent.store(true); }) {
if (mUseUpdateImminentTimer) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1911a0a..b6b754b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1687,8 +1687,9 @@
crop.bottom);
if (layerState.frameRate.rate != 0 ||
layerState.frameRate.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "% 6.2ffps %15s", layerState.frameRate.rate,
- frameRateCompatibilityString(layerState.frameRate.type).c_str());
+ StringAppendF(&result, "% 6.2ffps %15s seamless=%d", layerState.frameRate.rate,
+ frameRateCompatibilityString(layerState.frameRate.type).c_str(),
+ layerState.frameRate.shouldBeSeamless);
} else {
StringAppendF(&result, " ");
}
@@ -2750,6 +2751,12 @@
// ---------------------------------------------------------------------------
+std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
+ return stream << "{rate=" << rate.rate
+ << " type=" << Layer::frameRateCompatibilityString(rate.type)
+ << " shouldBeSeamless=" << rate.shouldBeSeamless << "}";
+}
+
}; // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b1ab9ec..8d67ce5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -153,12 +153,15 @@
struct FrameRate {
float rate;
FrameRateCompatibility type;
+ bool shouldBeSeamless;
- FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
- FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
+ FrameRate() : rate(0), type(FrameRateCompatibility::Default), shouldBeSeamless(true) {}
+ FrameRate(float rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
+ : rate(rate), type(type), shouldBeSeamless(shouldBeSeamless) {}
bool operator==(const FrameRate& other) const {
- return rate == other.rate && type == other.type;
+ return rate == other.rate && type == other.type &&
+ shouldBeSeamless == other.shouldBeSeamless;
}
bool operator!=(const FrameRate& other) const { return !(*this == other); }
@@ -441,6 +444,7 @@
virtual bool setColorSpaceAgnostic(const bool agnostic);
virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
+ virtual void setAutoRefresh(bool /* autoRefresh */) {}
// If the variable is not set on the layer, it traverses up the tree to inherit the frame
// rate priority from its parent.
virtual int32_t getFrameRateSelectionPriority() const;
@@ -1126,4 +1130,6 @@
const std::vector<BlurRegion>& getBlurRegions() const;
};
+std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
+
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index b7b7e46..2511eb3 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -175,6 +175,7 @@
mScheduler(scheduler),
mTunables(tunables),
mIdleTimer(
+ "RegionSamplingIdleTimer",
std::chrono::duration_cast<std::chrono::milliseconds>(
mTunables.mSamplingTimerTimeout),
[] {}, [this] { checkForStaleLuma(); }),
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bf5be47..b63e8c8 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,6 +45,9 @@
#include "FrameTimeline.h"
#include "HwcStrongTypes.h"
+#undef LOG_TAG
+#define LOG_TAG "EventThread"
+
using namespace std::chrono_literals;
namespace android {
@@ -123,14 +126,36 @@
return event;
}
+DisplayEventReceiver::Event makeFrameRateOverrideEvent(PhysicalDisplayId displayId,
+ FrameRateOverride frameRateOverride) {
+ return DisplayEventReceiver::Event{
+ .header =
+ DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ },
+ .frameRateOverride = frameRateOverride,
+ };
+}
+
+DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId displayId) {
+ return DisplayEventReceiver::Event{
+ .header = DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ }};
+}
+
} // namespace
-EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
- ResyncCallback resyncCallback,
- ISurfaceComposer::ConfigChanged configChanged)
+EventThreadConnection::EventThreadConnection(
+ EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration)
: resyncCallback(std::move(resyncCallback)),
- mConfigChanged(configChanged),
mOwnerUid(callingUid),
+ mEventRegistration(eventRegistration),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -161,8 +186,25 @@
}
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
- ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
- return size < 0 ? status_t(size) : status_t(NO_ERROR);
+ constexpr auto toStatus = [](ssize_t size) {
+ return size < 0 ? status_t(size) : status_t(NO_ERROR);
+ };
+
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
+ event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
+ mPendingEvents.emplace_back(event);
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
+ return status_t(NO_ERROR);
+ }
+
+ auto size = DisplayEventReceiver::sendEvents(&mChannel, mPendingEvents.data(),
+ mPendingEvents.size());
+ mPendingEvents.clear();
+ return toStatus(size);
+ }
+
+ auto size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
+ return toStatus(size);
}
// ---------------------------------------------------------------------------
@@ -220,10 +262,11 @@
}
sp<EventThreadConnection> EventThread::createEventConnection(
- ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
+ ResyncCallback resyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration) const {
return new EventThreadConnection(const_cast<EventThread*>(this),
IPCThreadState::self()->getCallingUid(),
- std::move(resyncCallback), configChanged);
+ std::move(resyncCallback), eventRegistration);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -333,6 +376,18 @@
mCondition.notify_all();
}
+void EventThread::onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ for (auto frameRateOverride : overrides) {
+ mPendingEvents.push_back(makeFrameRateOverrideEvent(displayId, frameRateOverride));
+ }
+ mPendingEvents.push_back(makeFrameRateOverrideFlushEvent(displayId));
+
+ mCondition.notify_all();
+}
+
size_t EventThread::getEventThreadConnectionCount() {
std::lock_guard<std::mutex> lock(mMutex);
return mDisplayEventConnections.size();
@@ -459,7 +514,8 @@
return true;
case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
- return connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+ return connection->mEventRegistration.test(
+ ISurfaceComposer::EventRegistration::configChanged);
}
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
@@ -488,6 +544,12 @@
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ [[fallthrough]];
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ return connection->mEventRegistration.test(
+ ISurfaceComposer::EventRegistration::frameRateOverride);
+
default:
return false;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 2e2d989..e75b718 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -48,6 +48,7 @@
// ---------------------------------------------------------------------------
using ResyncCallback = std::function<void()>;
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
enum class VSyncRequest {
None = -2,
@@ -82,7 +83,7 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
- ISurfaceComposer::ConfigChanged configChanged);
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
virtual ~EventThreadConnection();
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
@@ -95,15 +96,15 @@
const ResyncCallback resyncCallback;
VSyncRequest vsyncRequest = VSyncRequest::None;
- const ISurfaceComposer::ConfigChanged mConfigChanged =
- ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
-
const uid_t mOwnerUid;
+ const ISurfaceComposer::EventRegistrationFlags mEventRegistration;
private:
virtual void onFirstRef();
EventThread* const mEventThread;
gui::BitTube mChannel;
+
+ std::vector<DisplayEventReceiver::Event> mPendingEvents;
};
class EventThread {
@@ -111,7 +112,8 @@
virtual ~EventThread();
virtual sp<EventThreadConnection> createEventConnection(
- ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const = 0;
+ ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const = 0;
// called before the screen is turned off from main thread
virtual void onScreenReleased() = 0;
@@ -125,6 +127,10 @@
virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
nsecs_t vsyncPeriod) = 0;
+ // called when SF updates the Frame Rate Override list
+ virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) = 0;
+
virtual void dump(std::string& result) const = 0;
virtual void setDuration(std::chrono::nanoseconds workDuration,
@@ -152,7 +158,8 @@
~EventThread();
sp<EventThreadConnection> createEventConnection(
- ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const override;
+ ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
@@ -169,6 +176,9 @@
void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
nsecs_t vsyncPeriod) override;
+ void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) override;
+
void dump(std::string& result) const override;
void setDuration(std::chrono::nanoseconds workDuration,
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 36433c2..28af930 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -125,12 +125,23 @@
return LayerVoteType::NoVote;
}
}();
- summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f,
- layerFocused});
+ summary.push_back(
+ RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
+ .vote = voteType,
+ .desiredRefreshRate = frameRate.rate,
+ .shouldBeSeamless =
+ frameRate.shouldBeSeamless,
+ .weight = 1.0f,
+ .focused = layerFocused});
} else if (recent) {
- summary.push_back({layer->getName(), LayerVoteType::Heuristic,
- info->getRefreshRate(now),
- /* weight */ 1.0f, layerFocused});
+ summary.push_back(
+ RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
+ .vote = LayerVoteType::Heuristic,
+ .desiredRefreshRate =
+ info->getRefreshRate(now),
+ .shouldBeSeamless = true,
+ .weight = 1.0f,
+ .focused = layerFocused});
}
if (CC_UNLIKELY(mTraceEnabled)) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 37e67e1..a63ccc1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -130,9 +130,9 @@
ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
frameRateSelectionPriority, layerFocused ? "" : "not");
- const auto [type, refreshRate] = info->getRefreshRate(now);
+ const auto vote = info->getRefreshRateVote(now);
// Skip NoVote layer as those don't have any requirements
- if (type == LayerHistory::LayerVoteType::NoVote) {
+ if (vote.type == LayerHistory::LayerVoteType::NoVote) {
continue;
}
@@ -144,10 +144,11 @@
const float layerArea = transformed.getWidth() * transformed.getHeight();
float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});
+ summary.push_back({strong->getName(), vote.type, vote.fps, vote.shouldBeSeamless, weight,
+ layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
+ trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps)));
}
}
@@ -178,7 +179,7 @@
if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote(type, frameRate.rate);
+ info->setLayerVote({type, frameRate.rate, frameRate.shouldBeSeamless});
} else {
info->resetLayerVote();
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 44f20d0..94e7e20 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -198,10 +198,10 @@
: std::make_optional(mLastRefreshRate.reported);
}
-std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
+LayerInfoV2::LayerVote LayerInfoV2::getRefreshRateVote(nsecs_t now) {
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
- return {mLayerVote.type, mLayerVote.fps};
+ return mLayerVote;
}
if (isAnimating(now)) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 33dc66f..2305bc3 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -56,6 +56,13 @@
friend class LayerHistoryTestV2;
public:
+ // Holds information about the layer vote
+ struct LayerVote {
+ LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+ float fps = 0.0f;
+ bool shouldBeSeamless = true;
+ };
+
static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
@@ -76,7 +83,7 @@
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
- void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
+ void setLayerVote(LayerVote vote) { mLayerVote = vote; }
// Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
// This is used for layers that called to setLayerVote() and then removed the vote, so that the
@@ -84,9 +91,9 @@
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
// Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; }
+ void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, true}; }
- std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now);
+ LayerVote getRefreshRateVote(nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
@@ -130,12 +137,6 @@
bool animatingOrInfrequent = false;
};
- // Holds information about the layer vote
- struct LayerVote {
- LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
- float fps = 0.0f;
- };
-
// Class to store past calculated refresh rate and determine whether
// the refresh rate calculated is consistent with past values
class RefreshRateHistory {
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 2783800..ce3b0c6 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -16,6 +16,8 @@
#include "OneShotTimer.h"
+#include <utils/Log.h>
+#include <utils/Timers.h>
#include <chrono>
#include <sstream>
#include <thread>
@@ -29,25 +31,30 @@
// (tv_sec) is the whole count of seconds. The second (tv_nsec) is the
// nanosecond part of the count. This function takes care of translation.
void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) {
- clock_gettime(CLOCK_MONOTONIC, spec);
- spec->tv_sec += static_cast<__kernel_time_t>(timestamp.count() / kNsToSeconds);
- spec->tv_nsec += timestamp.count() % kNsToSeconds;
+ const nsecs_t timeout = systemTime(CLOCK_MONOTONIC) + timestamp.count();
+ spec->tv_sec = static_cast<__kernel_time_t>(timeout / kNsToSeconds);
+ spec->tv_nsec = timeout % kNsToSeconds;
}
} // namespace
namespace android {
namespace scheduler {
-OneShotTimer::OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
+OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
+ const ResetCallback& resetCallback,
const TimeoutCallback& timeoutCallback)
- : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
+ : mName(std::move(name)),
+ mInterval(interval),
+ mResetCallback(resetCallback),
+ mTimeoutCallback(timeoutCallback) {}
OneShotTimer::~OneShotTimer() {
stop();
}
void OneShotTimer::start() {
- sem_init(&mSemaphore, 0, 0);
+ int result = sem_init(&mSemaphore, 0, 0);
+ LOG_ALWAYS_FATAL_IF(result, "sem_init failed");
if (!mThread.joinable()) {
// Only create thread if it has not been created.
@@ -57,15 +64,21 @@
void OneShotTimer::stop() {
mStopTriggered = true;
- sem_post(&mSemaphore);
+ int result = sem_post(&mSemaphore);
+ LOG_ALWAYS_FATAL_IF(result, "sem_post failed");
if (mThread.joinable()) {
mThread.join();
- sem_destroy(&mSemaphore);
+ result = sem_destroy(&mSemaphore);
+ LOG_ALWAYS_FATAL_IF(result, "sem_destroy failed");
}
}
void OneShotTimer::loop() {
+ if (pthread_setname_np(pthread_self(), mName.c_str())) {
+ ALOGW("Failed to set thread name on dispatch thread");
+ }
+
TimerState state = TimerState::RESET;
while (true) {
bool triggerReset = false;
@@ -77,7 +90,12 @@
}
if (state == TimerState::IDLE) {
- sem_wait(&mSemaphore);
+ int result = sem_wait(&mSemaphore);
+ if (result && errno != EINTR) {
+ std::stringstream ss;
+ ss << "sem_wait failed (" << errno << ")";
+ LOG_ALWAYS_FATAL("%s", ss.str().c_str());
+ }
continue;
}
@@ -101,7 +119,12 @@
// Wait for mInterval time for semaphore signal.
struct timespec ts;
calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
- sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
+ int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
+ if (result && errno != ETIMEDOUT && errno != EINTR) {
+ std::stringstream ss;
+ ss << "sem_clockwait failed (" << errno << ")";
+ LOG_ALWAYS_FATAL("%s", ss.str().c_str());
+ }
state = checkForResetAndStop(state);
if (state == TimerState::RESET) {
@@ -135,7 +158,8 @@
void OneShotTimer::reset() {
mResetTriggered = true;
- sem_post(&mSemaphore);
+ int result = sem_post(&mSemaphore);
+ LOG_ALWAYS_FATAL_IF(result, "sem_post failed");
}
std::string OneShotTimer::dump() const {
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 8bbd4f5..3690ce7 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -36,7 +36,7 @@
using ResetCallback = std::function<void()>;
using TimeoutCallback = std::function<void()>;
- OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
+ OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
const TimeoutCallback& timeoutCallback);
~OneShotTimer();
@@ -81,6 +81,9 @@
// Semaphore to keep mThread synchronized.
sem_t mSemaphore;
+ // Timer's name.
+ std::string mName;
+
// Interval after which timer expires.
const Interval mInterval;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 150f925..8b4283c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -31,6 +31,12 @@
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+std::string RefreshRate::toString() const {
+ return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
+ getConfigId().value(), hwcConfig->getId(), getFps(),
+ hwcConfig->getWidth(), hwcConfig->getHeight(), getConfigGroup());
+}
+
std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
switch (vote) {
case LayerVoteType::NoVote:
@@ -125,7 +131,7 @@
const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
- ALOGV("getRefreshRateForContent %zu layers", layers.size());
+ ALOGV("getBestRefreshRate %zu layers", layers.size());
if (outSignalsConsidered) *outSignalsConsidered = {};
const auto setTouchConsidered = [&] {
@@ -148,6 +154,7 @@
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
float maxExplicitWeight = 0;
+ int seamedLayers = 0;
for (const auto& layer : layers) {
if (layer.vote == LayerVoteType::NoVote) {
noVoteLayers++;
@@ -162,6 +169,10 @@
explicitExactOrMultipleVoteLayers++;
maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
}
+
+ if (!layer.shouldBeSeamless) {
+ seamedLayers++;
+ }
}
const bool hasExplicitVoteLayers =
@@ -206,6 +217,8 @@
scores.emplace_back(refreshRate, 0.0f);
}
+ const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
+
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), layer.weight);
@@ -216,6 +229,30 @@
auto weight = layer.weight;
for (auto i = 0u; i < scores.size(); i++) {
+ // If there are no layers with shouldBeSeamless=false and the current
+ // config group is different from the default one, this means a layer with
+ // shouldBeSeamless=false has just disappeared and we should switch back to
+ // the default config group.
+ const bool isSeamlessSwitch = seamedLayers > 0
+ ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
+ : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+
+ if (layer.shouldBeSeamless && !isSeamlessSwitch) {
+ ALOGV("%s (weight %.2f) ignores %s (group=%d) to avoid non-seamless switch."
+ "Current config = %s",
+ layer.name.c_str(), weight, scores[i].first->name.c_str(),
+ scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
+ if (!layer.shouldBeSeamless && !isSeamlessSwitch && !layer.focused) {
+ ALOGV("%s (weight %.2f) ignores %s (group=%d) because it's not focused"
+ " and the switch is going to be seamed. Current config = %s",
+ layer.name.c_str(), weight, scores[i].first->name.c_str(),
+ scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
bool inPrimaryRange =
scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
@@ -292,10 +329,13 @@
return 1.0f / iter;
}();
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
scores[i].first->name.c_str(), layerScore);
- scores[i].second += weight * layerScore;
+ scores[i].second += weight * layerScore * seamlessness;
continue;
}
}
@@ -367,6 +407,15 @@
}
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+ for (auto refreshRate : mPrimaryRefreshRates) {
+ if (mCurrentRefreshRate->getConfigGroup() == refreshRate->getConfigGroup()) {
+ return *refreshRate;
+ }
+ }
+ ALOGE("Can't find min refresh rate by policy with the same config group"
+ " as the current config %s",
+ mCurrentRefreshRate->toString().c_str());
+ // Defaulting to the lowest refresh rate
return *mPrimaryRefreshRates.front();
}
@@ -376,6 +425,16 @@
}
const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+ for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
+ const auto& refreshRate = (**it);
+ if (mCurrentRefreshRate->getConfigGroup() == refreshRate.getConfigGroup()) {
+ return refreshRate;
+ }
+ }
+ ALOGE("Can't find max refresh rate by policy with the same config group"
+ " as the current config %s",
+ mCurrentRefreshRate->toString().c_str());
+ // Defaulting to the highest refresh rate
return *mPrimaryRefreshRates.back();
}
@@ -414,7 +473,7 @@
const float fps = 1e9f / config->getVsyncPeriod();
mRefreshRates.emplace(configId,
std::make_unique<RefreshRate>(configId, config,
- base::StringPrintf("%.0ffps", fps), fps,
+ base::StringPrintf("%.2ffps", fps), fps,
RefreshRate::ConstructorTag(0)));
if (configId == currentConfigId) {
mCurrentRefreshRate = mRefreshRates.at(configId).get();
@@ -628,21 +687,20 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
-void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) {
- if (refreshRateHz > 0 && refreshRateHz < 1) {
+void RefreshRateConfigs::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ if (frameRateOverride.frameRateHz > 0 && frameRateOverride.frameRateHz < 1) {
return;
}
std::lock_guard lock(mLock);
- if (refreshRateHz != 0) {
- mPreferredRefreshRateForUid[uid] = refreshRateHz;
+ if (frameRateOverride.frameRateHz != 0) {
+ mPreferredRefreshRateForUid[frameRateOverride.uid] = frameRateOverride.frameRateHz;
} else {
- mPreferredRefreshRateForUid.erase(uid);
+ mPreferredRefreshRateForUid.erase(frameRateOverride.uid);
}
}
int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
- constexpr float kThreshold = 0.1f;
std::lock_guard lock(mLock);
const auto iter = mPreferredRefreshRateForUid.find(uid);
@@ -650,6 +708,9 @@
return 1;
}
+ // This calculation needs to be in sync with the java code
+ // in DisplayManagerService.getDisplayInfoForFrameRateOverride
+ constexpr float kThreshold = 0.1f;
const auto refreshRateHz = iter->second;
const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz;
const auto numPeriodsRounded = std::round(numPeriods);
@@ -657,7 +718,41 @@
return 1;
}
- return static_cast<int>(numPeriods);
+ return static_cast<int>(numPeriodsRounded);
+}
+
+std::vector<FrameRateOverride> RefreshRateConfigs::getFrameRateOverrides() {
+ std::lock_guard lock(mLock);
+ std::vector<FrameRateOverride> overrides;
+ overrides.reserve(mPreferredRefreshRateForUid.size());
+
+ for (const auto [uid, frameRate] : mPreferredRefreshRateForUid) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate});
+ }
+
+ return overrides;
+}
+
+void RefreshRateConfigs::dump(std::string& result) const {
+ std::lock_guard lock(mLock);
+ base::StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
+ mDisplayManagerPolicy.toString().c_str());
+ scheduler::RefreshRateConfigs::Policy currentPolicy = *getCurrentPolicyLocked();
+ if (mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
+ base::StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
+ currentPolicy.toString().c_str());
+ }
+
+ auto config = mCurrentRefreshRate->hwcConfig;
+ base::StringAppendF(&result, "Current config: %s\n", mCurrentRefreshRate->toString().c_str());
+
+ result.append("Refresh rates:\n");
+ for (const auto& [id, refreshRate] : mRefreshRates) {
+ config = refreshRate->hwcConfig;
+ base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
+ }
+
+ result.append("\n");
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 8ff92a0..a873777 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -17,6 +17,7 @@
#pragma once
#include <android-base/stringprintf.h>
+#include <gui/DisplayEventReceiver.h>
#include <algorithm>
#include <numeric>
@@ -39,6 +40,8 @@
return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
/**
* This class is used to encapsulate configuration for refresh rates. It holds information
* about available refresh rates on the device, and the mapping between the numbers and human
@@ -86,6 +89,8 @@
bool operator==(const RefreshRate& other) const { return !(*this != other); }
+ std::string toString() const;
+
private:
friend RefreshRateConfigs;
friend class RefreshRateConfigsTest;
@@ -216,6 +221,8 @@
LayerVoteType vote = LayerVoteType::NoVote;
// Layer's desired refresh rate, if applicable.
float desiredRefreshRate = 0.0f;
+ // If a seamless mode switch is required.
+ bool shouldBeSeamless = true;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
// would have on choosing the refresh rate.
float weight = 0.0f;
@@ -312,12 +319,17 @@
KernelIdleTimerAction getIdleTimerAction() const;
// Stores the preferred refresh rate that an app should run at.
- // refreshRate == 0 means no preference.
- void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock);
+ // FrameRateOverride.refreshRateHz == 0 means no preference.
+ void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mLock);
// Returns a divider for the current refresh rate
int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+ void dump(std::string& result) const EXCLUDES(mLock);
+
+ // Returns the current frame rate overrides
+ std::vector<FrameRateOverride> getFrameRateOverrides() EXCLUDES(mLock);
+
private:
friend class RefreshRateConfigsTest;
@@ -375,6 +387,8 @@
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+ // A mapping between a UID and a preferred refresh rate that this app would
+ // run at.
std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock);
// The min and max refresh rates supported by the device.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index a14019e..f6d712e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -135,7 +135,7 @@
const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
: &Scheduler::idleTimerCallback;
mIdleTimer.emplace(
- std::chrono::milliseconds(millis),
+ "IdleTimer", std::chrono::milliseconds(millis),
[this, callback] { std::invoke(callback, this, TimerState::Reset); },
[this, callback] { std::invoke(callback, this, TimerState::Expired); });
mIdleTimer->start();
@@ -144,7 +144,7 @@
if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
mTouchTimer.emplace(
- std::chrono::milliseconds(millis),
+ "TouchTimer", std::chrono::milliseconds(millis),
[this] { touchTimerCallback(TimerState::Reset); },
[this] { touchTimerCallback(TimerState::Expired); });
mTouchTimer->start();
@@ -152,7 +152,7 @@
if (const int64_t millis = set_display_power_timer_ms(0); millis > 0) {
mDisplayPowerTimer.emplace(
- std::chrono::milliseconds(millis),
+ "DisplayPowerTimer", std::chrono::milliseconds(millis),
[this] { displayPowerTimerCallback(TimerState::Reset); },
[this] { displayPowerTimerCallback(TimerState::Expired); });
mDisplayPowerTimer->start();
@@ -239,8 +239,7 @@
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
- auto connection =
- createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+ auto connection = createConnectionInternal(eventThread.get());
std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
@@ -248,15 +247,15 @@
}
sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
- return eventThread->createEventConnection([&] { resync(); }, configChanged);
+ EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
+ return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+ ConnectionHandle handle, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
+ return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration);
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -297,6 +296,17 @@
thread->onScreenReleased();
}
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) {
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
+}
+
void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4c86d26..76e8f57 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -79,8 +79,8 @@
std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback);
- sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
- ISurfaceComposer::ConfigChanged);
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ ConnectionHandle, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
@@ -93,6 +93,9 @@
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
+ void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId,
+ std::vector<FrameRateOverride>);
+
// Modifies work duration in the event thread.
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
@@ -203,8 +206,8 @@
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
- sp<EventThreadConnection> createConnectionInternal(EventThread*,
- ISurfaceComposer::ConfigChanged);
+ sp<EventThreadConnection> createConnectionInternal(
+ EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
// Update feature state machine to given state when corresponding timer resets or expires.
void kernelIdleTimerCallback(TimerState);
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index e8ca0ba..6a60257 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -70,6 +70,10 @@
T const& value() const { return mValue; }
T& value() { return mValue; }
+ friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) {
+ return os << value.value();
+ }
+
private:
T mValue;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 75d1e6f..a6f9372 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -77,8 +77,12 @@
if (!validate(timestamp)) {
// VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
- // don't insert this ts into mTimestamps ringbuffer.
- if (!mTimestamps.empty()) {
+ // don't insert this ts into mTimestamps ringbuffer. If we are still
+ // in the learning phase we should just clear all timestamps and start
+ // over.
+ if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+ clearTimestamps();
+ } else if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
} else {
@@ -218,10 +222,10 @@
/*
* Returns whether a given vsync timestamp is in phase with a vsync divider.
- * For example, if the vsync timestamps are (0,16,32,48):
- * isVSyncInPhase(0, 2) = true
- * isVSyncInPhase(16, 2) = false
- * isVSyncInPhase(32, 2) = true
+ * For example, if the vsync timestamps are (16,32,48,64):
+ * isVSyncInPhase(16, 2) = true
+ * isVSyncInPhase(32, 2) = false
+ * isVSyncInPhase(48, 2) = true
*/
bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
struct VsyncError {
@@ -231,11 +235,11 @@
bool operator<(const VsyncError& other) const { return error < other.error; }
};
- std::lock_guard lock(mMutex);
- if (divider <= 1) {
+ if (divider <= 1 || timePoint == 0) {
return true;
}
+ std::lock_guard lock(mMutex);
const nsecs_t period = mRateMap[mIdealPeriod].slope;
const nsecs_t justBeforeTimePoint = timePoint - period / 2;
const nsecs_t dividedPeriod = mIdealPeriod / divider;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b8b2fbc..f9fe6e1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -330,7 +330,7 @@
: mFactory(factory),
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
- mFrameTracer(std::make_unique<FrameTracer>()),
+ mFrameTracer(mFactory.createFrameTracer()),
mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
@@ -1527,11 +1527,12 @@
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration) {
const auto& handle =
vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, configChanged);
+ return mScheduler->createDisplayEventConnection(handle, eventRegistration);
}
void SurfaceFlinger::signalTransaction() {
@@ -3781,7 +3782,8 @@
"SurfaceFlinger::setClientStateLocked") &&
layer->setFrameRate(Layer::FrameRate(s.frameRate,
Layer::FrameRate::convertCompatibility(
- s.frameRateCompatibility)))) {
+ s.frameRateCompatibility),
+ s.shouldBeSeamless))) {
flags |= eTraversalNeeded;
}
}
@@ -3795,6 +3797,9 @@
flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
}
}
+ if (what & layer_state_t::eAutoRefreshChanged) {
+ layer->setAutoRefresh(s.autoRefresh);
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
@@ -4373,16 +4378,10 @@
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
- scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
- StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
- policy.toString().c_str());
+ mRefreshRateConfigs->dump(result);
+
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
- scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
- if (currentPolicy != policy) {
- StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
- currentPolicy.toString().c_str());
- }
mScheduler->dump(mAppConnectionHandle, result);
mScheduler->dumpVsync(result);
@@ -5279,10 +5278,18 @@
return NO_ERROR;
}
case 1039: {
- // The first parameter is the uid
- n = data.readInt32();
- const float refreshRateHz = data.readFloat();
- mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz);
+ PhysicalDisplayId displayId = [&]() {
+ Mutex::Autolock lock(mStateLock);
+ return getDefaultDisplayDeviceLocked()->getPhysicalId();
+ }();
+
+ auto inUid = static_cast<uid_t>(data.readInt32());
+ const auto refreshRate = data.readFloat();
+ mRefreshRateConfigs->setPreferredRefreshRateForUid(
+ FrameRateOverride{inUid, refreshRate});
+ const auto mappings = mRefreshRateConfigs->getFrameRateOverrides();
+ mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId,
+ std::move(mappings));
}
return NO_ERROR;
}
@@ -5660,9 +5667,14 @@
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
- traverseLayers([&](Layer* layer) {
- hasProtectedLayer = hasProtectedLayer || (layer->isVisible() && layer->isProtected());
- });
+ hasProtectedLayer = schedule([=]() {
+ bool protectedLayerFound = false;
+ traverseLayers([&](Layer* layer) {
+ protectedLayerFound = protectedLayerFound ||
+ (layer->isVisible() && layer->isProtected());
+ });
+ return protectedLayerFound;
+ }).get();
}
const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6136,7 +6148,7 @@
}
status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, bool shouldBeSeamless) {
if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
return BAD_VALUE;
}
@@ -6149,10 +6161,10 @@
ALOGE("Attempt to set frame rate on a layer that no longer exists");
return BAD_VALUE;
}
-
if (layer->setFrameRate(
Layer::FrameRate(frameRate,
- Layer::FrameRate::convertCompatibility(compatibility)))) {
+ Layer::FrameRate::convertCompatibility(compatibility),
+ shouldBeSeamless))) {
setTransactionFlags(eTraversalNeeded);
}
} else {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a821d44..a909ad8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -530,8 +530,7 @@
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
sp<IDisplayEventConnection> createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged =
- ISurfaceComposer::eConfigChangedSuppress) override;
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
status_t captureDisplay(const DisplayCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) override;
status_t captureDisplay(uint64_t displayOrLayerStack,
@@ -600,7 +599,7 @@
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) override;
+ int8_t compatibility, bool shouldBeSeamless) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 9a8deae..a93f5f6 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -28,6 +28,7 @@
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
@@ -130,6 +131,9 @@
return new EffectLayer(args);
}
+std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
+ return std::make_unique<FrameTracer>();
+}
} // namespace android::surfaceflinger
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 40774ef..90032d4 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -54,6 +54,7 @@
sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
+ std::unique_ptr<FrameTracer> createFrameTracer() override;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 2dd563b..69e9c3c 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -34,6 +34,7 @@
class EffectLayer;
class ContainerLayer;
class DisplayDevice;
+class FrameTracer;
class GraphicBuffer;
class HWComposer;
class IGraphicBufferConsumer;
@@ -100,6 +101,7 @@
virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+ virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
protected:
~Factory() = default;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index fe9e737..28a3a81 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -716,23 +716,27 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- // Only update layer stats if we're allowed to do so.
+ // Only update layer stats if we're already tracking the layer in TimeStats.
+ // Otherwise, continue tracking the statistic but use a default layer name instead.
// As an implementation detail, we do this because this method is expected to be
- // called from FrameTimeline, which is allowed to do jank analysis well after a frame is
- // presented. This means that we can't rely on TimeStats to flush layer records over to the
- // aggregated stats.
- if (!canAddNewAggregatedStats(uid, layerName)) {
- return;
- }
+ // called from FrameTimeline, whose jank classification includes transaction jank
+ // that occurs without a buffer. But, in general those layer names are not suitable as
+ // aggregation keys: e.g., it's normal and expected for Window Manager to include the hash code
+ // for an animation leash. So while we can show that jank in dumpsys, aggregating based on the
+ // layer blows up the stats size, so as a workaround drop those stats. This assumes that
+ // TimeStats will flush the first present fence for a layer *before* FrameTimeline does so that
+ // the first jank record is not dropped.
- // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences
- // before TimeStats does.
+ bool useDefaultLayerKey = false;
+ static const std::string kDefaultLayerName = "none";
if (!mTimeStats.stats.count({uid, layerName})) {
- mTimeStats.stats[{uid, layerName}].uid = uid;
- mTimeStats.stats[{uid, layerName}].layerName = layerName;
+ mTimeStats.stats[{uid, kDefaultLayerName}].uid = uid;
+ mTimeStats.stats[{uid, kDefaultLayerName}].layerName = kDefaultLayerName;
+ useDefaultLayerKey = true;
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer =
+ mTimeStats.stats[{uid, useDefaultLayerKey ? kDefaultLayerName : layerName}];
updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
}
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index da71dad..b87c734 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -123,7 +123,7 @@
}
virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
+ uint32_t bufferWidth, uint32_t bufferHeight) {
ANativeWindow_Buffer buffer;
ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
TransactionUtils::fillANativeWindowBufferColor(buffer,
@@ -145,7 +145,7 @@
}
void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
+ uint32_t bufferWidth, uint32_t bufferHeight) {
switch (mLayerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp
index 02ba9e2..d1bed0c 100644
--- a/services/surfaceflinger/tests/SetFrameRate_test.cpp
+++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <system/window.h>
#include <thread>
@@ -50,8 +46,8 @@
}
}
- const int mLayerWidth = 32;
- const int mLayerHeight = 32;
+ const uint32_t mLayerWidth = 32;
+ const uint32_t mLayerHeight = 32;
sp<SurfaceControl> mLayer;
uint32_t mLayerType;
};
@@ -59,26 +55,27 @@
TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) {
CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue);
native_window_set_frame_rate(mLayer->getSurface().get(), 100.f,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true);
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
Transaction()
- .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true)
.apply();
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
native_window_set_frame_rate(mLayer->getSurface().get(), 300.f,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true);
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
}
TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) {
CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState);
Transaction()
- .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true)
.apply();
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN));
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 01badf4..a361b1e 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -98,14 +98,14 @@
outTransformHint, format);
}
- void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
- int32_t bufferHeight) {
+ void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, uint32_t bufferWidth,
+ uint32_t bufferHeight) {
ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
bufferWidth, bufferHeight));
}
- void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
- int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+ void fillLayerQuadrant(const sp<SurfaceControl>& layer, uint32_t bufferWidth,
+ uint32_t bufferHeight, const Color& topLeft, const Color& topRight,
const Color& bottomLeft, const Color& bottomRight) {
ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
bufferWidth, bufferHeight,
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 0a70f5c..31a5126 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -240,8 +240,9 @@
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
- mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::eConfigChangedDispatch));
+ mReceiver.reset(
+ new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::EventRegistration::configChanged));
mLooper = new Looper(false);
mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 0911712..be9d336 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -133,17 +133,13 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index f0311bd..9069200 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -70,15 +70,13 @@
void DisplayTransactionTest::injectMockScheduler() {
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(
+ new EventThreadConnection(mEventThread, /*callingUid=*/0, ResyncCallback())));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(
+ new EventThreadConnection(mSFEventThread, /*callingUid=*/0, ResyncCallback())));
mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 3aafd45..ee56178 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -29,6 +29,7 @@
using namespace std::chrono_literals;
using namespace std::placeholders;
+using namespace android::flag_operators;
using testing::_;
using testing::Invoke;
@@ -61,9 +62,9 @@
public:
MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
ResyncCallback&& resyncCallback,
- ISurfaceComposer::ConfigChanged configChanged)
+ ISurfaceComposer::EventRegistrationFlags eventRegistration)
: EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
- configChanged) {}
+ eventRegistration) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -74,9 +75,10 @@
~EventThreadTest() override;
void createThread(std::unique_ptr<VSyncSource>);
- sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
- ISurfaceComposer::ConfigChanged configChanged,
- uid_t ownerUid = mConnectionUid);
+ sp<MockEventThreadConnection> createConnection(
+ ConnectionEventRecorder& recorder,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {},
+ uid_t ownerUid = mConnectionUid);
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
@@ -93,6 +95,8 @@
int32_t expectedConfigId,
nsecs_t expectedVsyncPeriod);
void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
+ void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+ std::vector<FrameRateOverride>);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -133,10 +137,11 @@
createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
- ISurfaceComposer::eConfigChangedDispatch);
- mThrottledConnection =
- createConnection(mThrottledConnectionEventCallRecorder,
- ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid);
+ ISurfaceComposer::EventRegistration::configChanged |
+ ISurfaceComposer::EventRegistration::frameRateOverride);
+ mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
+ ISurfaceComposer::EventRegistration::configChanged,
+ mThrottledConnectionUid);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -169,11 +174,11 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged,
- uid_t ownerUid) {
+ ConnectionEventRecorder& recorder,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration, uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
new MockEventThreadConnection(mThread.get(), ownerUid,
- mResyncCallRecorder.getInvocable(), configChanged);
+ mResyncCallRecorder.getInvocable(), eventRegistration);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -254,6 +259,25 @@
EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod);
}
+void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection(
+ PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride> expectedOverrides) {
+ for (const auto [uid, frameRateHz] : expectedOverrides) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
+ EXPECT_EQ(uid, event.frameRateOverride.uid);
+ EXPECT_EQ(frameRateHz, event.frameRateOverride.frameRateHz);
+ }
+
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
+}
+
namespace {
/* ------------------------------------------------------------------------
@@ -313,9 +337,7 @@
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
- sp<MockEventThreadConnection> firstConnection =
- createConnection(firstConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
mThread->setVsyncRate(0, firstConnection);
// By itself, this should not enable vsync events
@@ -325,8 +347,7 @@
// However if there is another connection which wants events at a nonzero rate.....
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnection(secondConnectionEventRecorder);
mThread->setVsyncRate(1, secondConnection);
// EventThread should enable vsync callbacks.
@@ -418,9 +439,7 @@
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection =
- createConnection(errorConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -445,15 +464,12 @@
TEST_F(EventThreadTest, tracksEventConnections) {
EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection =
- createConnection(errorConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnection(secondConnectionEventRecorder);
mThread->setVsyncRate(1, secondConnection);
EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
@@ -472,9 +488,7 @@
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
- sp<MockEventThreadConnection> errorConnection =
- createConnection(errorConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -539,8 +553,7 @@
TEST_F(EventThreadTest, suppressConfigChanged) {
ConnectionEventRecorder suppressConnectionEventRecorder{0};
sp<MockEventThreadConnection> suppressConnection =
- createConnection(suppressConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnection(suppressConnectionEventRecorder);
mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666);
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
@@ -549,6 +562,35 @@
ASSERT_FALSE(args.has_value());
}
+TEST_F(EventThreadTest, postUidFrameRateMapping) {
+ const std::vector<FrameRateOverride> overrides = {
+ {.uid = 1, .frameRateHz = 20},
+ {.uid = 3, .frameRateHz = 40},
+ {.uid = 5, .frameRateHz = 60},
+ };
+
+ mThread->onFrameRateOverridesChanged(INTERNAL_DISPLAY_ID, overrides);
+ expectUidFrameRateMappingEventReceivedByConnection(INTERNAL_DISPLAY_ID, overrides);
+}
+
+TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
+ const std::vector<FrameRateOverride> overrides = {
+ {.uid = 1, .frameRateHz = 20},
+ {.uid = 3, .frameRateHz = 40},
+ {.uid = 5, .frameRateHz = 60},
+ };
+
+ ConnectionEventRecorder suppressConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> suppressConnection =
+ createConnection(suppressConnectionEventRecorder);
+
+ mThread->onFrameRateOverridesChanged(INTERNAL_DISPLAY_ID, overrides);
+ expectUidFrameRateMappingEventReceivedByConnection(INTERNAL_DISPLAY_ID, overrides);
+
+ auto args = suppressConnectionEventRecorder.waitForCall();
+ ASSERT_FALSE(args.has_value());
+}
+
TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
// Signal that we want the next vsync event to be posted to the throttled connection
mThread->requestNextVsync(mThrottledConnection);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index cb376cd..3b50321 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -78,7 +78,7 @@
for (auto& [weak, info] : history().mLayerInfos) {
if (auto strong = weak.promote(); strong && strong.get() == layer) {
info->setDefaultLayerVote(vote);
- info->setLayerVote(vote, 0);
+ info->setLayerVote({vote, 0, false});
return;
}
}
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 0208728..cfbb3f5 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -57,11 +57,12 @@
namespace {
TEST_F(OneShotTimerTest, createAndDestroyTest) {
mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
- 3ms, [] {}, [] {});
+ "TestTimer", 3ms, [] {}, [] {});
}
TEST_F(OneShotTimerTest, startStopTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(30ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 30ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
auto startTime = std::chrono::steady_clock::now();
mIdleTimer->start();
@@ -81,7 +82,8 @@
}
TEST_F(OneShotTimerTest, resetTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 20ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -106,7 +108,8 @@
}
TEST_F(OneShotTimerTest, resetBackToBackTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 20ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -137,7 +140,8 @@
}
TEST_F(OneShotTimerTest, startNotCalledTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
// The start hasn't happened, so the callback does not happen.
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
@@ -149,7 +153,8 @@
}
TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -169,7 +174,8 @@
}
TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -178,7 +184,8 @@
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -192,7 +199,8 @@
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+ mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 4762fd4..2fbc72a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -57,6 +57,8 @@
static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(2);
static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_25 = HwcConfigIndexType(5);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_50 = HwcConfigIndexType(6);
// Test configs
std::shared_ptr<const HWC2::Display::Config> mConfig60 =
@@ -77,8 +79,16 @@
createConfig(HWC_CONFIG_ID_120, 1, static_cast<int64_t>(1e9f / 120));
std::shared_ptr<const HWC2::Display::Config> mConfig30 =
createConfig(HWC_CONFIG_ID_30, 0, static_cast<int64_t>(1e9f / 30));
+ std::shared_ptr<const HWC2::Display::Config> mConfig30DifferentGroup =
+ createConfig(HWC_CONFIG_ID_30, 1, static_cast<int64_t>(1e9f / 30));
+ std::shared_ptr<const HWC2::Display::Config> mConfig25DifferentGroup =
+ createConfig(HWC_CONFIG_ID_25, 1, static_cast<int64_t>(1e9f / 25));
+ std::shared_ptr<const HWC2::Display::Config> mConfig50 =
+ createConfig(HWC_CONFIG_ID_50, 0, static_cast<int64_t>(1e9f / 50));
// Test device configurations
+ // The positions of the configs in the arrays below MUST match their IDs. For example,
+ // the first config should always be 60Hz, the second 90Hz etc.
std::vector<std::shared_ptr<const HWC2::Display::Config>> m60OnlyConfigDevice = {mConfig60};
std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90Device = {mConfig60, mConfig90};
std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentGroups =
@@ -104,6 +114,14 @@
{mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, mConfig30};
std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_90Device =
{mConfig60, mConfig90, mConfig72DifferentGroup, mConfig120DifferentGroup, mConfig30};
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> m25_30_50_60Device =
+ {mConfig60,
+ mConfig90,
+ mConfig72DifferentGroup,
+ mConfig120DifferentGroup,
+ mConfig30DifferentGroup,
+ mConfig25DifferentGroup,
+ mConfig50};
// Expected RefreshRate objects
RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, "60fps", 60,
@@ -292,8 +310,8 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
- return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f,
- /*focused*/ false}};
+ return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*shouldBeSeamless*/ true,
+ /*weight*/ 1.0f, /*focused*/ false}};
};
EXPECT_EQ(mExpected90Config,
@@ -1245,7 +1263,9 @@
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
layer.desiredRefreshRate = 90.0f;
+ layer.shouldBeSeamless = false;
layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
@@ -1258,6 +1278,104 @@
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getConfigId());
+
+ // Verify that we won't change the group if seamless switch is required.
+ layer.shouldBeSeamless = true;
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ // At this point the default config in the DisplayManager policy with be 60Hz.
+ // Verify that if the current config is in another group and there are no layers with
+ // shouldBeSeamless=false we'll go back to the default group.
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ layer.desiredRefreshRate = 60.0f;
+ layer.name = "60Hz ExplicitDefault";
+ layer.shouldBeSeamless = true;
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ // If there's a layer with shouldBeSeamless=false, another layer with shouldBeSeamless=true
+ // can't change the config group.
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ auto layer2 = LayerRequirement{.weight = 0.5f};
+ layer2.vote = LayerVoteType::ExplicitDefault;
+ layer2.desiredRefreshRate = 90.0f;
+ layer2.name = "90Hz ExplicitDefault";
+ layer2.shouldBeSeamless = false;
+ layer2.focused = false;
+ layers.push_back(layer2);
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ layer.desiredRefreshRate = 60.0f;
+ layer.shouldBeSeamless = false;
+ layer.name = "60Hz ExplicitExactOrMultiple";
+ layer.focused = true;
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+ ASSERT_EQ(HWC_CONFIG_ID_120,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m25_30_50_60Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<
+ LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault",
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = 60.0f,
+ .shouldBeSeamless = false,
+ .weight = 0.5f,
+ .focused = false},
+ LayerRequirement{.name = "25Hz ExplicitExactOrMultiple",
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = 25.0f,
+ .shouldBeSeamless = true,
+ .weight = 1.0f,
+ .focused = true}};
+ auto& seamedLayer = layers[0];
+
+ ASSERT_EQ(HWC_CONFIG_ID_50,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = 30.0f;
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_30);
+
+ ASSERT_EQ(HWC_CONFIG_ID_25,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
}
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
@@ -1483,7 +1601,7 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_30);
const uid_t uid = 1234;
- refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30);
+ refreshRateConfigs->setPreferredRefreshRateForUid({uid, 30});
EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
@@ -1497,6 +1615,12 @@
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.5});
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+ refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.6f});
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 8cd8372..c5deb7c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -132,14 +132,12 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ ResyncCallback())));
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index de66f8f..d0bb9e2 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -108,7 +108,7 @@
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(0u, times.count("90fps"));
+ EXPECT_EQ(0u, times.count("90.00fps"));
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
mRefreshRateStats->setPowerMode(PowerMode::ON);
@@ -116,15 +116,15 @@
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90fps"));
- EXPECT_LT(0, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90.00fps"));
+ EXPECT_LT(0, times["90.00fps"]);
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
- int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ int ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
@@ -133,7 +133,7 @@
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
@@ -163,53 +163,53 @@
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90fps"));
- EXPECT_LT(0, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90.00fps"));
+ EXPECT_LT(0, times["90.00fps"]);
// When power mode is normal, time for configs updates.
mRefreshRateStats->setConfigMode(CONFIG_ID_1);
- int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ int ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- ASSERT_EQ(1u, times.count("60fps"));
- EXPECT_LT(0, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ ASSERT_EQ(1u, times.count("60.00fps"));
+ EXPECT_LT(0, times["60.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
- int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ int sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_LT(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_LT(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_1);
- ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_LT(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_LT(sixty, times["60.00fps"]);
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
- sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_1);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
}
} // namespace
} // namespace scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 509858a..647689b 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -42,8 +42,7 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress) {}
+ : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -98,9 +97,7 @@
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- const sp<IDisplayEventConnection> connection =
- mScheduler.createDisplayEventConnection(handle,
- ISurfaceComposer::eConfigChangedSuppress);
+ const sp<IDisplayEventConnection> connection = mScheduler.createDisplayEventConnection(handle);
EXPECT_FALSE(connection);
EXPECT_FALSE(mScheduler.getEventConnection(handle));
@@ -126,8 +123,7 @@
TEST_F(SchedulerTest, validConnectionHandle) {
const sp<IDisplayEventConnection> connection =
- mScheduler.createDisplayEventConnection(mConnectionHandle,
- ISurfaceComposer::eConfigChangedSuppress);
+ mScheduler.createDisplayEventConnection(mConnectionHandle);
ASSERT_EQ(mEventThreadConnection, connection);
EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index e25d501..5278641 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -172,14 +172,12 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ ResyncCallback())));
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a9d9dc0..1b6e9ed 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -83,7 +83,7 @@
mTouchTimer.reset();
}
mTouchTimer.emplace(
- std::chrono::milliseconds(millis),
+ "Testable Touch timer", std::chrono::milliseconds(millis),
[this] { touchTimerCallback(TimerState::Reset); },
[this] { touchTimerCallback(TimerState::Expired); });
mTouchTimer->start();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6ce738a..eba3f8e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,6 +30,7 @@
#include "DisplayDevice.h"
#include "EffectLayer.h"
#include "FakeVsyncConfiguration.h"
+#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
@@ -41,6 +42,7 @@
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockDisplayIdGenerator.h"
+#include "mock/MockFrameTracer.h"
namespace android {
@@ -148,6 +150,10 @@
return nullptr;
}
+ std::unique_ptr<FrameTracer> createFrameTracer() override {
+ return std::make_unique<mock::FrameTracer>();
+ }
+
using CreateBufferQueueFunction =
std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
sp<IGraphicBufferConsumer>* /* outConsumer */,
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 68cf330..c36d994 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -65,17 +65,13 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
- ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
EXPECT_CALL(*mVSyncTracker, currentPeriod())
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 3d60479..a142022 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -475,6 +475,20 @@
}
}
+TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) {
+ EXPECT_TRUE(tracker.addVsyncTimestamp(600));
+ EXPECT_TRUE(tracker.needsMoreSamples());
+
+ EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += mPeriod));
+
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += mPeriod));
+ }
+
+ EXPECT_FALSE(tracker.needsMoreSamples());
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index b4594c1..650d52d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -28,11 +28,14 @@
~EventThread() override;
MOCK_CONST_METHOD2(createEventConnection,
- sp<EventThreadConnection>(ResyncCallback, ISurfaceComposer::ConfigChanged));
+ sp<EventThreadConnection>(ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags));
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
+ MOCK_METHOD2(onFrameRateOverridesChanged,
+ void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_METHOD2(setDuration,
void(std::chrono::nanoseconds workDuration,
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index afb3004..7097e7a 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -15,7 +15,6 @@
sharedLibraries = [
"libbase",
"libcutils",
- "libgtest_prod",
"libgui",
"liblog",
"libpdx_default_transport",
@@ -48,6 +47,7 @@
cc_binary {
srcs: ["bufferhubd.cpp"],
+ system_ext_specific: true,
cflags: [
"-DLOG_TAG=\"bufferhubd\"",
"-DTRACE=0",
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 4df7b7c..3728731 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -1,6 +1,8 @@
cc_library_shared {
name: "libvr_hwc-hal",
+ system_ext_specific: true,
+
srcs: [
"impl/vr_hwc.cpp",
"impl/vr_composer_client.cpp",
@@ -97,6 +99,7 @@
cc_binary {
name: "vr_hwc",
+ system_ext_specific: true,
vintf_fragments: ["manifest_vr_hwc.xml"],
srcs: [
"vr_hardware_composer_service.cpp",
diff --git a/services/vr/performanced/Android.bp b/services/vr/performanced/Android.bp
index 20301f6..0ef8cc4 100644
--- a/services/vr/performanced/Android.bp
+++ b/services/vr/performanced/Android.bp
@@ -30,6 +30,7 @@
cc_binary {
name: "performanced",
+ system_ext_specific: true,
defaults: ["performanced_defaults"],
srcs: [
"cpu_set.cpp",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 6f09a8c..d7fdab5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -183,6 +183,8 @@
.library_namespace = library_namespace,
};
so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
+ ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
+ lib_name.c_str(), dlerror());
} else {
// load built-in driver
so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);