summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TEST_MAPPING3
-rw-r--r--cmds/installd/Android.bp4
-rw-r--r--cmds/installd/dexopt.cpp38
-rw-r--r--cmds/installd/file_parsing.h21
-rw-r--r--cmds/installd/otapreopt.cpp75
-rw-r--r--cmds/installd/otapreopt_chroot.cpp1
-rw-r--r--cmds/installd/tests/Android.bp4
-rw-r--r--cmds/servicemanager/ServiceManager.cpp7
-rw-r--r--include/android/imagedecoder.h31
-rw-r--r--include/android/surface_control.h12
-rw-r--r--include/input/Input.h50
-rw-r--r--include/input/InputTransport.h9
-rw-r--r--include/input/InputWindow.h4
-rw-r--r--libs/binder/Android.bp2
-rw-r--r--libs/binder/Binder.cpp10
-rw-r--r--libs/binder/BpBinder.cpp28
-rw-r--r--libs/binder/IPCThreadState.cpp3
-rw-r--r--libs/binder/Parcel.cpp27
-rw-r--r--libs/binder/PermissionCache.cpp5
-rw-r--r--libs/binder/ProcessState.cpp2
-rw-r--r--libs/binder/RpcConnection.cpp445
-rw-r--r--libs/binder/RpcServer.cpp169
-rw-r--r--libs/binder/RpcSession.cpp376
-rw-r--r--libs/binder/RpcSocketAddress.h122
-rw-r--r--libs/binder/RpcState.cpp153
-rw-r--r--libs/binder/RpcState.h47
-rw-r--r--libs/binder/RpcWireFormat.h4
-rw-r--r--libs/binder/binder_module.h (renamed from libs/binder/include/private/binder/binder_module.h)26
-rw-r--r--libs/binder/include/binder/BpBinder.h18
-rw-r--r--libs/binder/include/binder/IBinder.h31
-rw-r--r--libs/binder/include/binder/Parcel.h6
-rw-r--r--libs/binder/include/binder/PermissionCache.h2
-rw-r--r--libs/binder/include/binder/RpcServer.h82
-rw-r--r--libs/binder/include/binder/RpcSession.h (renamed from libs/binder/include/binder/RpcConnection.h)147
-rw-r--r--libs/binder/ndk/Android.bp14
-rw-r--r--libs/binder/parcel_fuzzer/main.cpp10
-rw-r--r--libs/binder/rust/Android.bp2
-rw-r--r--libs/binder/rust/sys/lib.rs8
-rw-r--r--libs/binder/tests/Android.bp3
-rw-r--r--libs/binder/tests/IBinderRpcTest.aidl6
-rw-r--r--libs/binder/tests/binderLibTest.cpp2
-rw-r--r--libs/binder/tests/binderRpcBenchmark.cpp25
-rw-r--r--libs/binder/tests/binderRpcTest.cpp228
-rw-r--r--libs/dumputils/dump_utils.cpp1
-rw-r--r--libs/gui/BLASTBufferQueue.cpp2
-rw-r--r--libs/gui/LayerDebugInfo.cpp8
-rw-r--r--libs/gui/LayerState.cpp8
-rw-r--r--libs/gui/SurfaceComposerClient.cpp23
-rw-r--r--libs/gui/include/gui/JankInfo.h4
-rw-r--r--libs/gui/include/gui/LayerState.h4
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h19
-rw-r--r--libs/input/Input.cpp75
-rw-r--r--libs/input/InputDevice.cpp6
-rw-r--r--libs/input/InputTransport.cpp71
-rw-r--r--libs/input/tests/Android.bp2
-rw-r--r--libs/input/tests/InputEvent_test.cpp102
-rw-r--r--libs/input/tests/InputPublisherAndConsumer_test.cpp15
-rw-r--r--libs/input/tests/StructLayout_test.cpp8
-rw-r--r--libs/input/tests/VelocityTracker_test.cpp3
-rw-r--r--libs/input/tests/VerifiedInputEvent_test.cpp6
-rw-r--r--libs/nativewindow/Android.bp15
-rw-r--r--libs/nativewindow/include/android/native_window.h12
-rw-r--r--libs/permission/Android.bp28
-rw-r--r--libs/permission/aidl/android/content/AttributionSourceState.aidl40
-rw-r--r--libs/permission/aidl/android/permission/IPermissionChecker.aidl37
-rw-r--r--libs/permission/android/permission/PermissionChecker.cpp114
-rw-r--r--libs/permission/include/android/permission/PermissionChecker.h136
-rw-r--r--libs/permission/include/binder/AppOpsManager.h3
-rw-r--r--libs/renderengine/Android.bp3
-rw-r--r--libs/renderengine/gl/GLESRenderEngine.cpp2
-rw-r--r--libs/renderengine/include/renderengine/LayerSettings.h25
-rw-r--r--libs/renderengine/skia/Cache.cpp176
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.cpp247
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.h20
-rwxr-xr-xlibs/renderengine/skia/debug/record.sh4
-rw-r--r--libs/renderengine/skia/filters/BlurFilter.cpp49
-rw-r--r--libs/renderengine/skia/filters/BlurFilter.h16
-rw-r--r--libs/renderengine/skia/filters/LinearEffect.cpp2
-rw-r--r--libs/renderengine/skia/filters/StretchShaderFactory.cpp247
-rw-r--r--libs/renderengine/skia/filters/StretchShaderFactory.h36
-rw-r--r--libs/renderengine/tests/RenderEngineTest.cpp112
-rw-r--r--libs/ui/include/ui/StretchEffect.h58
-rw-r--r--opengl/Android.bp8
-rw-r--r--opengl/libs/Android.bp52
-rw-r--r--services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp3
-rw-r--r--services/inputflinger/dispatcher/Entry.cpp3
-rw-r--r--services/inputflinger/dispatcher/Entry.h3
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp265
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h24
-rw-r--r--services/inputflinger/dispatcher/InputTarget.h3
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.cpp22
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.h21
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp89
-rw-r--r--services/sensorservice/SensorDevice.cpp30
-rw-r--r--services/sensorservice/SensorDeviceUtils.h11
-rw-r--r--services/surfaceflinger/Android.bp2
-rw-r--r--services/surfaceflinger/BufferStateLayer.cpp20
-rw-r--r--services/surfaceflinger/BufferStateLayer.h5
-rw-r--r--services/surfaceflinger/Clock.h42
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h5
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h6
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h2
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp3
-rw-r--r--services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp6
-rw-r--r--services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp2
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp2
-rw-r--r--services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp6
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp12
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp22
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp5
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp69
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp58
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp257
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp128
-rw-r--r--services/surfaceflinger/DisplayHardware/ComposerHal.cpp17
-rw-r--r--services/surfaceflinger/DisplayHardware/ComposerHal.h6
-rw-r--r--services/surfaceflinger/FpsReporter.cpp16
-rw-r--r--services/surfaceflinger/FpsReporter.h10
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.cpp55
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.h16
-rw-r--r--services/surfaceflinger/Layer.cpp36
-rw-r--r--services/surfaceflinger/Layer.h12
-rw-r--r--services/surfaceflinger/Scheduler/OneShotTimer.cpp8
-rw-r--r--services/surfaceflinger/Scheduler/OneShotTimer.h11
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp11
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp39
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h27
-rw-r--r--services/surfaceflinger/SurfaceInterceptor.cpp4
-rw-r--r--services/surfaceflinger/TimeStats/TimeStats.cpp17
-rw-r--r--services/surfaceflinger/tests/LayerCallback_test.cpp6
-rw-r--r--services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp3
-rw-r--r--services/surfaceflinger/tests/unittests/FpsReporterTest.cpp35
-rw-r--r--services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp88
-rw-r--r--services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp48
-rw-r--r--services/surfaceflinger/tests/unittests/TimeStatsTest.cpp60
-rw-r--r--services/surfaceflinger/tests/unittests/fake/FakeClock.h34
-rw-r--r--services/vr/hardware_composer/Android.bp36
-rw-r--r--services/vr/hardware_composer/manifest_vr_hwc.xml11
-rw-r--r--services/vr/hardware_composer/vr_hardware_composer_service.cpp55
-rw-r--r--services/vr/hardware_composer/vr_hwc.rc6
-rw-r--r--vulkan/libvulkan/Android.bp15
143 files changed, 4107 insertions, 1766 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 307e21cc44..4b64203387 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -60,9 +60,6 @@
]
},
{
- "name": "libsurfaceflinger_unittest"
- },
- {
"name": "CtsGraphicsTestCases",
"options": [
{
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index a5462367f8..3f180d94a7 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -47,6 +47,9 @@ cc_defaults {
"libutils",
"server_configurable_flags",
],
+ static_libs: [
+ "libasync_safe",
+ ],
export_shared_lib_headers: [
"libbinder",
],
@@ -250,6 +253,7 @@ cc_binary {
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libotapreoptparameters",
],
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 0cf50a3e32..204953cd07 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -36,6 +36,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
@@ -727,7 +728,8 @@ bool copy_system_profile(const std::string& system_profile,
if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
if (errno != EWOULDBLOCK) {
- PLOG(WARNING) << "Error locking profile " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error locking profile %s: %d",
+ package_name.c_str(), errno);
}
// This implies that the app owning this profile is running
// (and has acquired the lock).
@@ -735,13 +737,15 @@ bool copy_system_profile(const std::string& system_profile,
// The app never acquires the lock for the reference profiles of primary apks.
// Only dex2oat from installd will do that. Since installd is single threaded
// we should not see this case. Nevertheless be prepared for it.
- PLOG(WARNING) << "Failed to flock " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Failed to flock %s: %d",
+ package_name.c_str(), errno);
return false;
}
bool truncated = ftruncate(out_fd.get(), 0) == 0;
if (!truncated) {
- PLOG(WARNING) << "Could not truncate " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Could not truncate %s: %d",
+ package_name.c_str(), errno);
}
// Copy over data.
@@ -755,7 +759,8 @@ bool copy_system_profile(const std::string& system_profile,
write(out_fd.get(), buffer, bytes);
}
if (flock(out_fd.get(), LOCK_UN) != 0) {
- PLOG(WARNING) << "Error unlocking profile " << package_name;
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error unlocking profile %s: %d",
+ package_name.c_str(), errno);
}
// Use _exit since we don't want to run the global destructors in the child.
// b/62597429
@@ -1513,7 +1518,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char
// Validate the path structure.
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
_exit(kSecondaryDexDexoptAnalyzerSkippedValidatePath);
}
@@ -1809,7 +1815,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins
drop_capabilities(uid);
if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
- PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "flock(%s) failed",
+ out_oat.path().c_str());
_exit(DexoptReturnCodes::kFlock);
}
@@ -1904,7 +1911,8 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
_exit(kReconcileSecondaryDexValidationError);
}
@@ -1917,7 +1925,8 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
default:
- LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unexpected result from check_secondary_dex_access: %d", access_check);
_exit(kReconcileSecondaryDexValidationError);
}
@@ -1930,7 +1939,7 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
std::string error_msg;
if (!create_secondary_dex_oat_layout(
dex_path,isas[i], oat_dir, oat_isa_dir, oat_path, &error_msg)) {
- LOG(ERROR) << error_msg;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
_exit(kReconcileSecondaryDexValidationError);
}
@@ -1957,7 +1966,8 @@ bool reconcile_secondary_dex_file(const std::string& dex_path,
result = rmdir_if_empty(oat_dir) && result;
}
if (!result) {
- PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
}
_exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
}
@@ -2030,7 +2040,8 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg
pipe_read.reset();
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Could not validate secondary dex path %s", dex_path.c_str());
_exit(DexoptReturnCodes::kHashValidatePath);
}
@@ -2041,6 +2052,8 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg
_exit(0);
}
PLOG(ERROR) << "Failed to open secondary dex " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Failed to open secondary dex %s: %d", dex_path.c_str(), errno);
_exit(DexoptReturnCodes::kHashOpenPath);
}
@@ -2053,7 +2066,8 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
- PLOG(ERROR) << "Failed to read secondary dex " << dex_path;
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Failed to read secondary dex %s: %d", dex_path.c_str(), errno);
_exit(DexoptReturnCodes::kHashReadDex);
}
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
index 3e2f815f32..88801ca671 100644
--- a/cmds/installd/file_parsing.h
+++ b/cmds/installd/file_parsing.h
@@ -19,18 +19,14 @@
#include <fstream>
#include <functional>
-#include <string>
+#include <string_view>
+#include "android-base/unique_fd.h"
namespace android {
namespace installd {
-bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) {
- std::ifstream input_stream(strFile);
-
- if (!input_stream.is_open()) {
- return false;
- }
-
+template<typename Func>
+bool ParseFile(std::istream& input_stream, Func parse) {
while (!input_stream.eof()) {
// Read the next line.
std::string line;
@@ -54,6 +50,15 @@ bool ParseFile(const std::string& strFile, std::function<bool (const std::string
return true;
}
+template<typename Func>
+bool ParseFile(std::string_view str_file, Func parse) {
+ std::ifstream ifs(str_file);
+ if (!ifs.is_open()) {
+ return false;
+ }
+ return ParseFile(ifs, parse);
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ed31ad9cfa..6aa32b8d8e 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,6 +26,7 @@
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
+#include <sys/mman.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -36,6 +37,7 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include "android-base/file.h"
#include "dexopt.h"
#include "file_parsing.h"
#include "globals.h"
@@ -195,38 +197,63 @@ private:
// export NAME VALUE
// For simplicity, don't respect string quotation. The values we are interested in can be
// encoded without them.
- // init.environ.rc and etc/classpath have the same format for
- // environment variable exports and can be matched by the same regex.
+ //
+ // init.environ.rc and derive_classpath all have the same format for
+ // environment variable exports (since they are all meant to be read by
+ // init) and can be matched by the same regex.
+
+ std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+ auto parse_results = [&](auto& input) {
+ ParseFile(input, [&](const std::string& line) {
+ std::smatch export_match;
+ if (!std::regex_match(line, export_match, export_regex)) {
+ return true;
+ }
+
+ if (export_match.size() != 3) {
+ return true;
+ }
+
+ std::string name = export_match[1].str();
+ std::string value = export_match[2].str();
+
+ system_properties_.SetProperty(name, value);
+
+ return true;
+ });
+ };
+
// TODO Just like with the system-properties above we really should have
// common code between init and otapreopt to deal with reading these
// things. See b/181182967
+ // There have been a variety of places the various env-vars have been
+ // over the years. Expand or reduce this list as needed.
static constexpr const char* kEnvironmentVariableSources[] = {
- "/init.environ.rc", "/etc/classpath"
+ "/init.environ.rc",
};
-
- std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+ // First get everything from the static files.
for (const char* env_vars_file : kEnvironmentVariableSources) {
- bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) {
- std::smatch export_match;
- if (!std::regex_match(line, export_match, export_regex)) {
- return true;
- }
-
- if (export_match.size() != 3) {
- return true;
- }
-
- std::string name = export_match[1].str();
- std::string value = export_match[2].str();
-
- system_properties_.SetProperty(name, value);
+ parse_results(env_vars_file);
+ }
- return true;
- });
- if (!parse_result) {
- return false;
- }
+ // Next get everything from derive_classpath, since we're already in the
+ // chroot it will get the new versions of any dependencies.
+ {
+ android::base::unique_fd fd(memfd_create("derive_classpath_temp", MFD_CLOEXEC));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Unable to create fd for derive_classpath";
+ return false;
+ }
+ std::string memfd_file = StringPrintf("/proc/%d/fd/%d", getpid(), fd.get());
+ std::string error_msg;
+ if (!Exec({"/apex/com.android.sdkext/bin/derive_classpath", memfd_file}, &error_msg)) {
+ PLOG(ERROR) << "Running derive_classpath failed: " << error_msg;
+ return false;
+ }
+ std::ifstream ifs(memfd_file);
+ parse_results(ifs);
}
+
if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
return false;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 83f01de01e..c62734a925 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -275,6 +275,7 @@ static int otapreopt_chroot(const int argc, char **arg) {
static constexpr const std::string_view kRequiredApexs[] = {
"com.android.art",
"com.android.runtime",
+ "com.android.sdkext", // For derive_classpath
};
std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false };
DIR* apex_dir = opendir("/apex");
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index f67ab812fa..70820172b7 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -20,6 +20,7 @@ cc_test {
"libcutils",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
@@ -44,6 +45,7 @@ cc_test {
"server_configurable_flags",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
@@ -84,6 +86,7 @@ cc_test {
"server_configurable_flags",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
@@ -124,6 +127,7 @@ cc_test {
"server_configurable_flags",
],
static_libs: [
+ "libasync_safe",
"libdiskusage",
"libinstalld",
"liblog",
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index b429fb3440..90db5091e1 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -481,7 +481,12 @@ void ServiceManager::tryStartService(const std::string& name) {
name.c_str());
std::thread([=] {
- (void)base::SetProperty("ctl.interface_start", "aidl/" + name);
+ if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
+ LOG(INFO) << "Tried to start aidl service " << name
+ << " as a lazy service, but was unable to. Usually this happens when a "
+ "service is not installed, but if the service is intended to be used as a "
+ "lazy service, then it may be configured incorrectly.";
+ }
}).detach();
}
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 8d1bf99fe7..fb7d09c04d 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -739,6 +739,9 @@ int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder)
* skipping frames in an image with such frames may not produce the correct
* results.
*
+ * Only supported by {@link ANDROID_BITMAP_FORMAT_RGBA_8888} and
+ * {@link ANDROID_BITMAP_FORMAT_RGBA_F16}.
+ *
* @param decoder an {@link AImageDecoder} object.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure.
@@ -747,6 +750,8 @@ int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder)
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
* represents an image that is not animated (see
* {@link AImageDecoder_isAnimated}) or the AImageDecoder is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE): The requested
+ * {@link AndroidBitmapFormat} does not support animation.
* - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears
* to be truncated. The client must call {@link AImageDecoder_rewind}
* before calling {@link AImageDecoder_decodeImage} again.
@@ -832,9 +837,11 @@ void AImageDecoderFrameInfo_delete(
* is the current frame.
*
* If the image only has one frame, this will fill the {@link
- * AImageDecoderFrameInfo} with the encoded info, if any, or reasonable
+ * AImageDecoderFrameInfo} with the encoded info and reasonable
* defaults.
*
+ * If {@link AImageDecoder_advanceFrame} succeeded, this will succeed as well.
+ *
* @param decoder Opaque object representing the decoder.
* @param info Opaque object to hold frame information. On success, will be
* filled with information regarding the current frame.
@@ -856,7 +863,7 @@ int AImageDecoder_getFrameInfo(AImageDecoder* _Nonnull decoder,
* Introduced in API 31.
*
* Errors:
- * - returns 0 if |info| is null.
+ * - returns {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
*/
int64_t AImageDecoderFrameInfo_getDuration(
const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
@@ -891,19 +898,25 @@ ARect AImageDecoderFrameInfo_getFrameRect(
*
* Introduced in API 31.
*
- * Note that this may differ from whether the composed frame has
- * alpha. If this frame does not fill the entire image dimensions
- * (see {@link AImageDecoderFrameInfo_getFrameRect}) or it blends
- * with an opaque frame, for example, the composed frame’s alpha
- * may not match. It is also conservative; for example, if a color
- * index-based frame has a color with alpha but does not use it,
- * this will still return true.
+ * Unless this frame is independent (see {@link AImageDecoder_decodeImage}),
+ * a single call to {@link AImageDecoder_decodeImage} will decode an updated
+ * rectangle of pixels and then blend it with the existing pixels in the
+ * |pixels| buffer according to {@link AImageDecoderFrameInfo_getBlendOp}. This
+ * method returns whether the updated rectangle has alpha, prior to blending.
+ * The return value is conservative; for example, if a color-index-based frame
+ * has a color with alpha but does not use it, this will still return true.
*
* This, along with other information in AImageDecoderFrameInfo,
* can be useful for determining whether a frame is independent, but
* the decoder handles blending frames, so a simple
* sequential client does not need this.
*
+ * Note that this may differ from whether the composed frame (that is, the
+ * resulting image after blending) has alpha. If this frame does not fill the
+ * entire image dimensions (see {@link AImageDecoderFrameInfo_getFrameRect})
+ * or it blends with an opaque frame, for example, the composed frame’s alpha
+ * may not match.
+ *
* Errors:
* - returns false if |info| is null.
*/
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index b7eafcd6cd..988137112b 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -526,6 +526,9 @@ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
* callback timings, and changes to the time interval at which the system releases buffers back to
* the application.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* \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
@@ -534,11 +537,12 @@ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
*
* \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.
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. This parameter is ignored when frameRate is 0.
*
- * \param changeFrameRateStrategy 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. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * surface 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. See the
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. This parameter is ignored when frameRate is 0.
*
* Available since API level 31.
*/
diff --git a/include/input/Input.h b/include/input/Input.h
index bb5ca0ef04..d4defa8269 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -70,6 +70,14 @@ enum {
*/
AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+ /**
+ * This flag indicates that the event will not cause a focus change if it is directed to an
+ * unfocused window, even if it an ACTION_DOWN. This is typically used with pointer
+ * gestures to allow the user to direct gestures to an unfocused window without bringing it
+ * into focus.
+ */
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
+
/* Motion event is inconsistent with previously sent motion events. */
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
};
@@ -318,6 +326,12 @@ private:
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
+/**
+ * Invalid value for display size. Used when display size isn't available for an event or doesn't
+ * matter. This is just a constant 0 so that it has no effect if unused.
+ */
+constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0;
+
/*
* Pointer coordinate data.
*/
@@ -360,6 +374,8 @@ struct PointerCoords {
return getAxisValue(AMOTION_EVENT_AXIS_Y);
}
+ vec2 getXYValue() const { return vec2(getX(), getY()); }
+
#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
@@ -548,6 +564,8 @@ public:
void setCursorPosition(float x, float y);
+ int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
+
static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
inline nsecs_t getDownTime() const { return mDownTime; }
@@ -570,8 +588,17 @@ public:
inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
+ /**
+ * The actual raw pointer coords: whatever comes from the input device without any external
+ * transforms applied.
+ */
const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
+ /**
+ * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw"
+ * transform because many apps (incorrectly) assumed that raw == oriented-screen-space.
+ * "compat raw" is raw coordinates with screen rotation applied.
+ */
float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
inline float getRawX(size_t pointerIndex) const {
@@ -634,9 +661,18 @@ public:
return mSampleEventTimes[historicalIndex];
}
+ /**
+ * The actual raw pointer coords: whatever comes from the input device without any external
+ * transforms applied.
+ */
const PointerCoords* getHistoricalRawPointerCoords(
size_t pointerIndex, size_t historicalIndex) const;
+ /**
+ * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw"
+ * transform because many apps (incorrectly) assumed that raw == oriented-screen-space.
+ * "compat raw" is raw coordinates with screen rotation applied.
+ */
float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const;
@@ -704,9 +740,9 @@ public:
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float rawXCursorPosition,
- float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
- size_t pointerCount, const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords);
+ float rawYCursorPosition, int32_t displayWidth, int32_t displayHeight,
+ nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
void copyFrom(const MotionEvent* other, bool keepHistory);
@@ -718,10 +754,14 @@ public:
void scale(float globalScaleFactor);
- // Apply 3x3 perspective matrix transformation.
+ // Set 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
void transform(const std::array<float, 9>& matrix);
+ // Apply 3x3 perspective matrix transformation only to content (do not modify mTransform).
+ // Matrix is in row-major form and compatible with SkMatrix.
+ void applyTransform(const std::array<float, 9>& matrix);
+
#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
@@ -759,6 +799,8 @@ protected:
float mYPrecision;
float mRawXCursorPosition;
float mRawYCursorPosition;
+ int32_t mDisplayWidth;
+ int32_t mDisplayHeight;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
std::vector<nsecs_t> mSampleEventTimes;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index eef057e773..e8ffc64535 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -136,6 +136,8 @@ struct InputMessage {
float yPrecision;
float xCursorPosition;
float yCursorPosition;
+ int32_t displayWidth;
+ int32_t displayHeight;
uint32_t pointerCount;
uint32_t empty3;
/**
@@ -227,7 +229,7 @@ public:
InputChannel(const InputChannel& other)
: mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
- virtual ~InputChannel();
+ ~InputChannel() override;
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -353,8 +355,9 @@ public:
int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
- uint32_t pointerCount, const PointerProperties* pointerProperties,
+ float yCursorPosition, int32_t displayWidth, int32_t displayHeight,
+ nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
/* Publishes a focus event to the input channel.
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 36097d6d65..121be6d963 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -168,6 +168,10 @@ struct InputWindowInfo : public Parcelable {
// Transform applied to individual windows.
ui::Transform transform;
+ // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
+ int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+ int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index a97cf87de3..be260e856d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -112,7 +112,7 @@ cc_library {
"PersistableBundle.cpp",
"ProcessState.cpp",
"RpcAddress.cpp",
- "RpcConnection.cpp",
+ "RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
"Static.cpp",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d964d255fe..d5bdd1c803 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -29,6 +29,16 @@
namespace android {
+// Service implementations inherit from BBinder and IBinder, and this is frozen
+// in prebuilts.
+#ifdef __LP64__
+static_assert(sizeof(IBinder) == 24);
+static_assert(sizeof(BBinder) == 40);
+#else
+static_assert(sizeof(IBinder) == 12);
+static_assert(sizeof(BBinder) == 20);
+#endif
+
// ---------------------------------------------------------------------------
IBinder::IBinder()
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index fdcf94acfa..1dcb94c80f 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -21,7 +21,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <binder/Stability.h>
#include <cutils/compiler.h>
#include <utils/Log.h>
@@ -136,15 +136,15 @@ sp<BpBinder> BpBinder::create(int32_t handle) {
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
-sp<BpBinder> BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
- LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
+sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) {
+ LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session");
// These are not currently tracked, since there is no UID or other
// identifier to track them with. However, if similar functionality is
- // needed, connection objects keep track of all BpBinder objects on a
- // per-connection basis.
+ // needed, session objects keep track of all BpBinder objects on a
+ // per-session basis.
- return sp<BpBinder>::make(SocketHandle{connection, address});
+ return sp<BpBinder>::make(RpcHandle{session, address});
}
BpBinder::BpBinder(Handle&& handle)
@@ -165,20 +165,20 @@ BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(
IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
}
-BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) {
- LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object");
+BpBinder::BpBinder(RpcHandle&& handle) : BpBinder(Handle(handle)) {
+ LOG_ALWAYS_FATAL_IF(rpcSession() == nullptr, "BpBinder created w/o session object");
}
bool BpBinder::isRpcBinder() const {
- return std::holds_alternative<SocketHandle>(mHandle);
+ return std::holds_alternative<RpcHandle>(mHandle);
}
const RpcAddress& BpBinder::rpcAddress() const {
- return std::get<SocketHandle>(mHandle).address;
+ return std::get<RpcHandle>(mHandle).address;
}
-const sp<RpcConnection>& BpBinder::rpcConnection() const {
- return std::get<SocketHandle>(mHandle).connection;
+const sp<RpcSession>& BpBinder::rpcSession() const {
+ return std::get<RpcHandle>(mHandle).session;
}
int32_t BpBinder::binderHandle() const {
@@ -273,7 +273,7 @@ status_t BpBinder::transact(
status_t status;
if (CC_UNLIKELY(isRpcBinder())) {
- status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags);
+ status = rpcSession()->transact(rpcAddress(), code, data, reply, flags);
} else {
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
@@ -479,7 +479,7 @@ void BpBinder::onLastStrongRef(const void* /*id*/)
{
ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
if (CC_UNLIKELY(isRpcBinder())) {
- (void)rpcConnection()->sendDecStrong(rpcAddress());
+ (void)rpcSession()->sendDecStrong(rpcAddress());
return;
}
IF_ALOGV() {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6fb1227f63..ef7fd44419 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -29,8 +29,6 @@
#include <utils/SystemClock.h>
#include <utils/threads.h>
-#include <private/binder/binder_module.h>
-
#include <atomic>
#include <errno.h>
#include <inttypes.h>
@@ -43,6 +41,7 @@
#include <unistd.h>
#include "Static.h"
+#include "binder_module.h"
#if LOG_NDEBUG
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a73530938c..ee834ea43c 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -48,10 +48,10 @@
#include <utils/String8.h>
#include <utils/misc.h>
-#include <private/binder/binder_module.h>
#include "RpcState.h"
#include "Static.h"
#include "Utils.h"
+#include "binder_module.h"
#define LOG_REFS(...)
//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -78,7 +78,11 @@ static size_t pad_size(size_t s) {
namespace android {
// many things compile this into prebuilts on the stack
-static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
+#ifdef __LP64__
+static_assert(sizeof(Parcel) == 120);
+#else
+static_assert(sizeof(Parcel) == 60);
+#endif
static std::atomic<size_t> gParcelGlobalAllocCount;
static std::atomic<size_t> gParcelGlobalAllocSize;
@@ -198,7 +202,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder)
status_t status = writeInt32(1); // non-null
if (status != OK) return status;
RpcAddress address = RpcAddress::zero();
- status = mConnection->state()->onBinderLeaving(mConnection, binder, &address);
+ status = mSession->state()->onBinderLeaving(mSession, binder, &address);
if (status != OK) return status;
status = address.writeToParcel(this);
if (status != OK) return status;
@@ -269,8 +273,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder)
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
if (isForRpc()) {
- LOG_ALWAYS_FATAL_IF(mConnection == nullptr,
- "RpcConnection required to read from remote parcel");
+ LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
int32_t isNull;
status_t status = readInt32(&isNull);
@@ -282,7 +285,7 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const
auto addr = RpcAddress::zero();
status_t status = addr.readFromParcel(*this);
if (status != OK) return status;
- binder = mConnection->state()->onBinderEntering(mConnection, addr);
+ binder = mSession->state()->onBinderEntering(mSession, addr);
}
return finishUnflattenBinder(binder, out);
@@ -564,20 +567,20 @@ void Parcel::markForBinder(const sp<IBinder>& binder) {
LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
- markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
+ markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
}
}
-void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+void Parcel::markForRpc(const sp<RpcSession>& session) {
LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
"format must be set before data is written OR on IPC data");
- LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
- mConnection = connection;
+ LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
+ mSession = session;
}
bool Parcel::isForRpc() const {
- return mConnection != nullptr;
+ return mSession != nullptr;
}
void Parcel::updateWorkSourceRequestHeaderPosition() const {
@@ -2495,7 +2498,7 @@ void Parcel::initState()
mDataPos = 0;
ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
- mConnection = nullptr;
+ mSession = nullptr;
mObjects = nullptr;
mObjectsSize = 0;
mObjectsCapacity = 0;
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 6eae5efc29..670fd55da3 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -109,5 +109,10 @@ bool PermissionCache::checkPermission(
return granted;
}
+void PermissionCache::purgeCache() {
+ PermissionCache& pc(PermissionCache::getInstance());
+ pc.purge();
+}
+
// ---------------------------------------------------------------------------
} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1d3beb4736..4fd0dc7715 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -27,8 +27,8 @@
#include <utils/String8.h>
#include <utils/threads.h>
-#include <private/binder/binder_module.h>
#include "Static.h"
+#include "binder_module.h"
#include <errno.h>
#include <fcntl.h>
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
deleted file mode 100644
index 1388a801e4..0000000000
--- a/libs/binder/RpcConnection.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "RpcConnection"
-
-#include <binder/RpcConnection.h>
-
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <string_view>
-
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-#include <utils/String8.h>
-
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
-#ifdef __GLIBC__
-extern "C" pid_t gettid();
-#endif
-
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif
-
-namespace android {
-
-using base::unique_fd;
-using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>;
-
-RpcConnection::SocketAddress::~SocketAddress() {}
-
-RpcConnection::RpcConnection() {
- LOG_RPC_DETAIL("RpcConnection created %p", this);
-
- mState = std::make_unique<RpcState>();
-}
-RpcConnection::~RpcConnection() {
- LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
-
- std::lock_guard<std::mutex> _l(mSocketMutex);
- LOG_ALWAYS_FATAL_IF(mServers.size() != 0,
- "Should not be able to destroy a connection with servers in use.");
-}
-
-sp<RpcConnection> RpcConnection::make() {
- return sp<RpcConnection>::make();
-}
-
-class UnixSocketAddress : public RpcConnection::SocketAddress {
-public:
- explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
- unsigned int pathLen = strlen(path) + 1;
- LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s",
- pathLen, path);
- memcpy(mAddr.sun_path, path, pathLen);
- }
- virtual ~UnixSocketAddress() {}
- std::string toString() const override {
- return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
- mAddr.sun_path)
- .c_str();
- }
- const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
- size_t addrSize() const override { return sizeof(mAddr); }
-
-private:
- sockaddr_un mAddr;
-};
-
-bool RpcConnection::setupUnixDomainServer(const char* path) {
- return setupSocketServer(UnixSocketAddress(path));
-}
-
-bool RpcConnection::addUnixDomainClient(const char* path) {
- return addSocketClient(UnixSocketAddress(path));
-}
-
-#ifdef __BIONIC__
-
-class VsockSocketAddress : public RpcConnection::SocketAddress {
-public:
- VsockSocketAddress(unsigned int cid, unsigned int port)
- : mAddr({
- .svm_family = AF_VSOCK,
- .svm_port = port,
- .svm_cid = cid,
- }) {}
- virtual ~VsockSocketAddress() {}
- std::string toString() const override {
- return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str();
- }
- const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
- size_t addrSize() const override { return sizeof(mAddr); }
-
-private:
- sockaddr_vm mAddr;
-};
-
-bool RpcConnection::setupVsockServer(unsigned int port) {
- // realizing value w/ this type at compile time to avoid ubsan abort
- constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
-
- return setupSocketServer(VsockSocketAddress(kAnyCid, port));
-}
-
-bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
- return addSocketClient(VsockSocketAddress(cid, port));
-}
-
-#endif // __BIONIC__
-
-class SocketAddressImpl : public RpcConnection::SocketAddress {
-public:
- SocketAddressImpl(const sockaddr* addr, size_t size, const String8& desc)
- : mAddr(addr), mSize(size), mDesc(desc) {}
- [[nodiscard]] std::string toString() const override {
- return std::string(mDesc.c_str(), mDesc.size());
- }
- [[nodiscard]] const sockaddr* addr() const override { return mAddr; }
- [[nodiscard]] size_t addrSize() const override { return mSize; }
- void set(const sockaddr* addr, size_t size) {
- mAddr = addr;
- mSize = size;
- }
-
-private:
- const sockaddr* mAddr = nullptr;
- size_t mSize = 0;
- String8 mDesc;
-};
-
-AddrInfo GetAddrInfo(const char* addr, unsigned int port) {
- addrinfo hint{
- .ai_flags = 0,
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- .ai_protocol = 0,
- };
- addrinfo* aiStart = nullptr;
- if (int rc = getaddrinfo(addr, std::to_string(port).data(), &hint, &aiStart); 0 != rc) {
- ALOGE("Unable to resolve %s:%u: %s", addr, port, gai_strerror(rc));
- return AddrInfo(nullptr, nullptr);
- }
- if (aiStart == nullptr) {
- ALOGE("Unable to resolve %s:%u: getaddrinfo returns null", addr, port);
- return AddrInfo(nullptr, nullptr);
- }
- return AddrInfo(aiStart, &freeaddrinfo);
-}
-
-bool RpcConnection::setupInetServer(unsigned int port) {
- auto aiStart = GetAddrInfo("127.0.0.1", port);
- if (aiStart == nullptr) return false;
- SocketAddressImpl socketAddress(nullptr, 0, String8::format("127.0.0.1:%u", port));
- for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
- socketAddress.set(ai->ai_addr, ai->ai_addrlen);
- if (setupSocketServer(socketAddress)) return true;
- }
- ALOGE("None of the socket address resolved for 127.0.0.1:%u can be set up as inet server.",
- port);
- return false;
-}
-
-bool RpcConnection::addInetClient(const char* addr, unsigned int port) {
- auto aiStart = GetAddrInfo(addr, port);
- if (aiStart == nullptr) return false;
- SocketAddressImpl socketAddress(nullptr, 0, String8::format("%s:%u", addr, port));
- for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
- socketAddress.set(ai->ai_addr, ai->ai_addrlen);
- if (addSocketClient(socketAddress)) return true;
- }
- ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
- return false;
-}
-
-bool RpcConnection::addNullDebuggingClient() {
- unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
-
- if (serverFd == -1) {
- ALOGE("Could not connect to /dev/null: %s", strerror(errno));
- return false;
- }
-
- addClient(std::move(serverFd));
- return true;
-}
-
-sp<IBinder> RpcConnection::getRootObject() {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
- return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this));
-}
-
-status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this),
- (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
- : SocketUse::CLIENT);
- return state()->transact(socket.fd(), address, code, data,
- sp<RpcConnection>::fromExisting(this), reply, flags);
-}
-
-status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT_REFCOUNT);
- return state()->sendDecStrong(socket.fd(), address);
-}
-
-void RpcConnection::join() {
- // TODO(b/185167543): do this dynamically, instead of from a static number
- // of threads
- unique_fd clientFd(
- TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
- if (clientFd < 0) {
- // If this log becomes confusing, should save more state from setupUnixDomainServer
- // in order to output here.
- ALOGE("Could not accept4 socket: %s", strerror(errno));
- return;
- }
-
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
- // must be registered to allow arbitrary client code executing commands to
- // be able to do nested calls (we can't only read from it)
- sp<ConnectionSocket> socket = assignServerToThisThread(std::move(clientFd));
-
- while (true) {
- status_t error =
- state()->getAndExecuteCommand(socket->fd, sp<RpcConnection>::fromExisting(this));
-
- if (error != OK) {
- ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
- break;
- }
- }
-
- LOG_ALWAYS_FATAL_IF(!removeServerSocket(socket),
- "bad state: socket object guaranteed to be in list");
-}
-
-void RpcConnection::setForServer(const wp<RpcServer>& server) {
- mForServer = server;
-}
-
-wp<RpcServer> RpcConnection::server() {
- return mForServer;
-}
-
-bool RpcConnection::setupSocketServer(const SocketAddress& addr) {
- LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
-
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- ALOGE("Could not create socket: %s", strerror(errno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
- int savedErrno = errno;
- ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
- int savedErrno = errno;
- ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- mServer = std::move(serverFd);
- return true;
-}
-
-bool RpcConnection::addSocketClient(const SocketAddress& addr) {
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- int savedErrno = errno;
- ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
- int savedErrno = errno;
- ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
-
- addClient(std::move(serverFd));
- return true;
-}
-
-void RpcConnection::addClient(unique_fd&& fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
- connection->fd = std::move(fd);
- mClients.push_back(connection);
-}
-
-sp<RpcConnection::ConnectionSocket> RpcConnection::assignServerToThisThread(unique_fd&& fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
- connection->fd = std::move(fd);
- connection->exclusiveTid = gettid();
- mServers.push_back(connection);
-
- return connection;
-}
-
-bool RpcConnection::removeServerSocket(const sp<ConnectionSocket>& socket) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- if (auto it = std::find(mServers.begin(), mServers.end(), socket); it != mServers.end()) {
- mServers.erase(it);
- return true;
- }
- return false;
-}
-
-RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
- : mConnection(connection) {
- pid_t tid = gettid();
- std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
-
- mConnection->mWaitingThreads++;
- while (true) {
- sp<ConnectionSocket> exclusive;
- sp<ConnectionSocket> available;
-
- // CHECK FOR DEDICATED CLIENT SOCKET
- //
- // A server/looper should always use a dedicated connection if available
- findSocket(tid, &exclusive, &available, mConnection->mClients, mConnection->mClientsOffset);
-
- // WARNING: this assumes a server cannot request its client to send
- // a transaction, as mServers is excluded below.
- //
- // Imagine we have more than one thread in play, and a single thread
- // sends a synchronous, then an asynchronous command. Imagine the
- // asynchronous command is sent on the first client socket. Then, if
- // we naively send a synchronous command to that same socket, the
- // thread on the far side might be busy processing the asynchronous
- // command. So, we move to considering the second available thread
- // for subsequent calls.
- if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
- mConnection->mClientsOffset =
- (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
- }
-
- // USE SERVING SOCKET (for nested transaction)
- //
- // asynchronous calls cannot be nested
- if (use != SocketUse::CLIENT_ASYNC) {
- // server sockets are always assigned to a thread
- findSocket(tid, &exclusive, nullptr /*available*/, mConnection->mServers,
- 0 /* index hint */);
- }
-
- // if our thread is already using a connection, prioritize using that
- if (exclusive != nullptr) {
- mSocket = exclusive;
- mReentrant = true;
- break;
- } else if (available != nullptr) {
- mSocket = available;
- mSocket->exclusiveTid = tid;
- break;
- }
-
- // in regular binder, this would usually be a deadlock :)
- LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0,
- "Not a client of any connection. You must create a connection to an "
- "RPC server to make any non-nested (e.g. oneway or on another thread) "
- "calls.");
-
- LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...",
- mConnection->mClients.size(), mConnection->mServers.size());
- mConnection->mSocketCv.wait(_l);
- }
- mConnection->mWaitingThreads--;
-}
-
-void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
- sp<ConnectionSocket>* available,
- std::vector<sp<ConnectionSocket>>& sockets,
- size_t socketsIndexHint) {
- LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
- "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
-
- if (*exclusive != nullptr) return; // consistent with break below
-
- for (size_t i = 0; i < sockets.size(); i++) {
- sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
-
- // take first available connection (intuition = caching)
- if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
- *available = socket;
- continue;
- }
-
- // though, prefer to take connection which is already inuse by this thread
- // (nested transactions)
- if (exclusive && socket->exclusiveTid == tid) {
- *exclusive = socket;
- break; // consistent with return above
- }
- }
-}
-
-RpcConnection::ExclusiveSocket::~ExclusiveSocket() {
- // reentrant use of a connection means something less deep in the call stack
- // is using this fd, and it retains the right to it. So, we don't give up
- // exclusive ownership, and no thread is freed.
- if (!mReentrant) {
- std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
- mSocket->exclusiveTid = std::nullopt;
- if (mConnection->mWaitingThreads > 0) {
- _l.unlock();
- mConnection->mSocketCv.notify_one();
- }
- }
-}
-
-} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 1fa37bacb3..786e2db3b3 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -19,6 +19,7 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <thread>
#include <vector>
#include <binder/Parcel.h>
@@ -26,6 +27,7 @@
#include <log/log.h>
#include "RpcState.h"
+#include "RpcSocketAddress.h"
#include "RpcWireFormat.h"
namespace android {
@@ -43,22 +45,175 @@ void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction()
mAgreedExperimental = true;
}
-sp<RpcConnection> RpcServer::addClientConnection() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+bool RpcServer::setupUnixDomainServer(const char* path) {
+ return setupSocketServer(UnixSocketAddress(path));
+}
+
+#ifdef __BIONIC__
- auto connection = RpcConnection::make();
- connection->setForServer(sp<RpcServer>::fromExisting(this));
- mConnections.push_back(connection);
- return connection;
+bool RpcServer::setupVsockServer(unsigned int port) {
+ // realizing value w/ this type at compile time to avoid ubsan abort
+ constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+ return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+}
+
+#endif // __BIONIC__
+
+bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
+ const char* kAddr = "127.0.0.1";
+
+ if (assignedPort != nullptr) *assignedPort = 0;
+ auto aiStart = InetSocketAddress::getAddrInfo(kAddr, port);
+ if (aiStart == nullptr) return false;
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, kAddr, port);
+ if (!setupSocketServer(socketAddress)) {
+ continue;
+ }
+
+ LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet");
+ sockaddr_in addr{};
+ socklen_t len = sizeof(addr);
+ if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) {
+ int savedErrno = errno;
+ ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+ LOG_ALWAYS_FATAL_IF(len != sizeof(addr), "Wrong socket type: len %zu vs len %zu",
+ static_cast<size_t>(len), sizeof(addr));
+ unsigned int realPort = ntohs(addr.sin_port);
+ LOG_ALWAYS_FATAL_IF(port != 0 && realPort != port,
+ "Requesting inet server on %s but it is set up on %u.",
+ socketAddress.toString().c_str(), realPort);
+
+ if (assignedPort != nullptr) {
+ *assignedPort = realPort;
+ }
+
+ return true;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr,
+ port);
+ return false;
+}
+
+void RpcServer::setMaxThreads(size_t threads) {
+ LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads");
+ LOG_ALWAYS_FATAL_IF(mStarted, "must be called before started");
+ mMaxThreads = threads;
+}
+
+size_t RpcServer::getMaxThreads() {
+ return mMaxThreads;
}
void RpcServer::setRootObject(const sp<IBinder>& binder) {
- LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object");
+ std::lock_guard<std::mutex> _l(mLock);
mRootObject = binder;
}
sp<IBinder> RpcServer::getRootObject() {
+ std::lock_guard<std::mutex> _l(mLock);
return mRootObject;
}
+void RpcServer::join() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+
+ std::vector<std::thread> pool;
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mServer.get() == -1, "RpcServer must be setup to join.");
+ }
+
+ while (true) {
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
+
+ if (clientFd < 0) {
+ ALOGE("Could not accept4 socket: %s", strerror(errno));
+ continue;
+ }
+ LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+ // TODO(b/183988761): cannot trust this simple ID
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ int32_t id;
+ if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+ ALOGE("Could not read ID from fd %d", clientFd.get());
+ continue;
+ }
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+
+ sp<RpcSession> session;
+ if (id == RPC_SESSION_ID_NEW) {
+ // new client!
+ LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+ mSessionIdCounter++;
+
+ session = RpcSession::make();
+ session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+
+ mSessions[mSessionIdCounter] = session;
+ } else {
+ auto it = mSessions.find(id);
+ if (it == mSessions.end()) {
+ ALOGE("Cannot add thread, no record of session with ID %d", id);
+ continue;
+ }
+ session = it->second;
+ }
+
+ session->startThread(std::move(clientFd));
+ }
+ }
+}
+
+std::vector<sp<RpcSession>> RpcServer::listSessions() {
+ std::lock_guard<std::mutex> _l(mLock);
+ std::vector<sp<RpcSession>> sessions;
+ for (auto& [id, session] : mSessions) {
+ (void)id;
+ sessions.push_back(session);
+ }
+ return sessions;
+}
+
+bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
+ LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcServer can only have one server.");
+ }
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ int savedErrno = errno;
+ ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
+
+ mServer = std::move(serverFd);
+ return true;
+}
+
} // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
new file mode 100644
index 0000000000..09ec20dbf0
--- /dev/null
+++ b/libs/binder/RpcSession.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "RpcSession"
+
+#include <binder/RpcSession.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string_view>
+
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <utils/String8.h>
+
+#include "RpcSocketAddress.h"
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#ifdef __GLIBC__
+extern "C" pid_t gettid();
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcSession::RpcSession() {
+ LOG_RPC_DETAIL("RpcSession created %p", this);
+
+ mState = std::make_unique<RpcState>();
+}
+RpcSession::~RpcSession() {
+ LOG_RPC_DETAIL("RpcSession destroyed %p", this);
+
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mServers.size() != 0,
+ "Should not be able to destroy a session with servers in use.");
+}
+
+sp<RpcSession> RpcSession::make() {
+ return sp<RpcSession>::make();
+}
+
+bool RpcSession::setupUnixDomainClient(const char* path) {
+ return setupSocketClient(UnixSocketAddress(path));
+}
+
+#ifdef __BIONIC__
+
+bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
+ return setupSocketClient(VsockSocketAddress(cid, port));
+}
+
+#endif // __BIONIC__
+
+bool RpcSession::setupInetClient(const char* addr, unsigned int port) {
+ auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+ if (aiStart == nullptr) return false;
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
+ if (setupSocketClient(socketAddress)) return true;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
+ return false;
+}
+
+bool RpcSession::addNullDebuggingClient() {
+ unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
+
+ if (serverFd == -1) {
+ ALOGE("Could not connect to /dev/null: %s", strerror(errno));
+ return false;
+ }
+
+ addClient(std::move(serverFd));
+ return true;
+}
+
+sp<IBinder> RpcSession::getRootObject() {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ return state()->getRootObject(connection.fd(), sp<RpcSession>::fromExisting(this));
+}
+
+status_t RpcSession::getMaxThreads(size_t* maxThreads) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads);
+}
+
+status_t RpcSession::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+ (flags & IBinder::FLAG_ONEWAY) ? ConnectionUse::CLIENT_ASYNC
+ : ConnectionUse::CLIENT);
+ return state()->transact(connection.fd(), address, code, data,
+ sp<RpcSession>::fromExisting(this), reply, flags);
+}
+
+status_t RpcSession::sendDecStrong(const RpcAddress& address) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+ ConnectionUse::CLIENT_REFCOUNT);
+ return state()->sendDecStrong(connection.fd(), address);
+}
+
+status_t RpcSession::readId() {
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
+ }
+
+ int32_t id;
+
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ status_t status =
+ state()->getSessionId(connection.fd(), sp<RpcSession>::fromExisting(this), &id);
+ if (status != OK) return status;
+
+ LOG_RPC_DETAIL("RpcSession %p has id %d", this, id);
+ mId = id;
+ return OK;
+}
+
+void RpcSession::startThread(unique_fd client) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcSession> holdThis = sp<RpcSession>::fromExisting(this);
+ int fd = client.release();
+ auto thread = std::thread([=] {
+ holdThis->join(unique_fd(fd));
+ {
+ std::lock_guard<std::mutex> _l(holdThis->mMutex);
+ size_t erased = mThreads.erase(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(erased != 0, "Could not erase thread.");
+ }
+ });
+ mThreads[thread.get_id()] = std::move(thread);
+}
+
+void RpcSession::join(unique_fd client) {
+ // must be registered to allow arbitrary client code executing commands to
+ // be able to do nested calls (we can't only read from it)
+ sp<RpcConnection> connection = assignServerToThisThread(std::move(client));
+
+ while (true) {
+ status_t error =
+ state()->getAndExecuteCommand(connection->fd, sp<RpcSession>::fromExisting(this));
+
+ if (error != OK) {
+ ALOGI("Binder connection thread closing w/ status %s", statusToString(error).c_str());
+ break;
+ }
+ }
+
+ LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
+ "bad state: connection object guaranteed to be in list");
+}
+
+wp<RpcServer> RpcSession::server() {
+ return mForServer;
+}
+
+bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mClients.size() != 0,
+ "Must only setup session once, but already has %zu clients",
+ mClients.size());
+ }
+
+ if (!setupOneSocketClient(addr, RPC_SESSION_ID_NEW)) return false;
+
+ // TODO(b/185167543): we should add additional sessions dynamically
+ // instead of all at once.
+ // TODO(b/186470974): first risk of blocking
+ size_t numThreadsAvailable;
+ if (status_t status = getMaxThreads(&numThreadsAvailable); status != OK) {
+ ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
+ statusToString(status).c_str());
+ return false;
+ }
+
+ if (status_t status = readId(); status != OK) {
+ ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(),
+ statusToString(status).c_str());
+ return false;
+ }
+
+ // we've already setup one client
+ for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+ // TODO(b/185167543): avoid race w/ accept4 not being called on server
+ for (size_t tries = 0; tries < 5; tries++) {
+ if (setupOneSocketClient(addr, mId.value())) break;
+ usleep(10000);
+ }
+ }
+
+ return true;
+}
+
+bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
+ int savedErrno = errno;
+ ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+ addClient(std::move(serverFd));
+ return true;
+}
+
+void RpcSession::addClient(unique_fd fd) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcConnection> session = sp<RpcConnection>::make();
+ session->fd = std::move(fd);
+ mClients.push_back(session);
+}
+
+void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) {
+ mId = sessionId;
+ mForServer = server;
+}
+
+sp<RpcSession::RpcConnection> RpcSession::assignServerToThisThread(unique_fd fd) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcConnection> session = sp<RpcConnection>::make();
+ session->fd = std::move(fd);
+ session->exclusiveTid = gettid();
+ mServers.push_back(session);
+
+ return session;
+}
+
+bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ if (auto it = std::find(mServers.begin(), mServers.end(), connection); it != mServers.end()) {
+ mServers.erase(it);
+ return true;
+ }
+ return false;
+}
+
+RpcSession::ExclusiveConnection::ExclusiveConnection(const sp<RpcSession>& session,
+ ConnectionUse use)
+ : mSession(session) {
+ pid_t tid = gettid();
+ std::unique_lock<std::mutex> _l(mSession->mMutex);
+
+ mSession->mWaitingThreads++;
+ while (true) {
+ sp<RpcConnection> exclusive;
+ sp<RpcConnection> available;
+
+ // CHECK FOR DEDICATED CLIENT SOCKET
+ //
+ // A server/looper should always use a dedicated session if available
+ findConnection(tid, &exclusive, &available, mSession->mClients, mSession->mClientsOffset);
+
+ // WARNING: this assumes a server cannot request its client to send
+ // a transaction, as mServers is excluded below.
+ //
+ // Imagine we have more than one thread in play, and a single thread
+ // sends a synchronous, then an asynchronous command. Imagine the
+ // asynchronous command is sent on the first client connection. Then, if
+ // we naively send a synchronous command to that same connection, the
+ // thread on the far side might be busy processing the asynchronous
+ // command. So, we move to considering the second available thread
+ // for subsequent calls.
+ if (use == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+ mSession->mClientsOffset = (mSession->mClientsOffset + 1) % mSession->mClients.size();
+ }
+
+ // USE SERVING SOCKET (for nested transaction)
+ //
+ // asynchronous calls cannot be nested
+ if (use != ConnectionUse::CLIENT_ASYNC) {
+ // server connections are always assigned to a thread
+ findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServers,
+ 0 /* index hint */);
+ }
+
+ // if our thread is already using a session, prioritize using that
+ if (exclusive != nullptr) {
+ mConnection = exclusive;
+ mReentrant = true;
+ break;
+ } else if (available != nullptr) {
+ mConnection = available;
+ mConnection->exclusiveTid = tid;
+ break;
+ }
+
+ // in regular binder, this would usually be a deadlock :)
+ LOG_ALWAYS_FATAL_IF(mSession->mClients.size() == 0,
+ "Not a client of any session. You must create a session to an "
+ "RPC server to make any non-nested (e.g. oneway or on another thread) "
+ "calls.");
+
+ LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...",
+ mSession->mClients.size(), mSession->mServers.size());
+ mSession->mAvailableConnectionCv.wait(_l);
+ }
+ mSession->mWaitingThreads--;
+}
+
+void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ sp<RpcConnection>* available,
+ std::vector<sp<RpcConnection>>& sockets,
+ size_t socketsIndexHint) {
+ LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
+ "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
+
+ if (*exclusive != nullptr) return; // consistent with break below
+
+ for (size_t i = 0; i < sockets.size(); i++) {
+ sp<RpcConnection>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
+
+ // take first available session (intuition = caching)
+ if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
+ *available = socket;
+ continue;
+ }
+
+ // though, prefer to take session which is already inuse by this thread
+ // (nested transactions)
+ if (exclusive && socket->exclusiveTid == tid) {
+ *exclusive = socket;
+ break; // consistent with return above
+ }
+ }
+}
+
+RpcSession::ExclusiveConnection::~ExclusiveConnection() {
+ // reentrant use of a session means something less deep in the call stack
+ // is using this fd, and it retains the right to it. So, we don't give up
+ // exclusive ownership, and no thread is freed.
+ if (!mReentrant) {
+ std::unique_lock<std::mutex> _l(mSession->mMutex);
+ mConnection->exclusiveTid = std::nullopt;
+ if (mSession->mWaitingThreads > 0) {
+ _l.unlock();
+ mSession->mAvailableConnectionCv.notify_one();
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h
new file mode 100644
index 0000000000..c6a06cf817
--- /dev/null
+++ b/libs/binder/RpcSocketAddress.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 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 <string>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif
+
+namespace android {
+
+class RpcSocketAddress {
+public:
+ virtual ~RpcSocketAddress() {}
+ virtual std::string toString() const = 0;
+ virtual const sockaddr* addr() const = 0;
+ virtual size_t addrSize() const = 0;
+};
+
+class UnixSocketAddress : public RpcSocketAddress {
+public:
+ explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s",
+ pathLen, path);
+ memcpy(mAddr.sun_path, path, pathLen);
+ }
+ virtual ~UnixSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+ mAddr.sun_path)
+ .c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_un mAddr;
+};
+
+#ifdef __BIONIC__
+
+class VsockSocketAddress : public RpcSocketAddress {
+public:
+ VsockSocketAddress(unsigned int cid, unsigned int port)
+ : mAddr({
+ .svm_family = AF_VSOCK,
+ .svm_port = port,
+ .svm_cid = cid,
+ }) {}
+ virtual ~VsockSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_vm mAddr;
+};
+
+#endif // __BIONIC__
+
+class InetSocketAddress : public RpcSocketAddress {
+public:
+ InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port)
+ : mSockAddr(sockAddr), mSize(size), mAddr(addr), mPort(port) {}
+ [[nodiscard]] std::string toString() const override {
+ return String8::format("%s:%u", mAddr, mPort).c_str();
+ }
+ [[nodiscard]] const sockaddr* addr() const override { return mSockAddr; }
+ [[nodiscard]] size_t addrSize() const override { return mSize; }
+
+ using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>;
+ static AddrInfo getAddrInfo(const char* addr, unsigned int port) {
+ addrinfo hint{
+ .ai_flags = 0,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = 0,
+ };
+ addrinfo* aiStart = nullptr;
+ if (int rc = getaddrinfo(addr, std::to_string(port).data(), &hint, &aiStart); 0 != rc) {
+ ALOGE("Unable to resolve %s:%u: %s", addr, port, gai_strerror(rc));
+ return AddrInfo(nullptr, nullptr);
+ }
+ if (aiStart == nullptr) {
+ ALOGE("Unable to resolve %s:%u: getaddrinfo returns null", addr, port);
+ return AddrInfo(nullptr, nullptr);
+ }
+ return AddrInfo(aiStart, &freeaddrinfo);
+ }
+
+private:
+ const sockaddr* mSockAddr;
+ size_t mSize;
+ const char* mAddr;
+ unsigned int mPort;
+};
+
+} // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index d9341369fa..96190dc03c 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -31,16 +31,16 @@ namespace android {
RpcState::RpcState() {}
RpcState::~RpcState() {}
-status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder,
+status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
RpcAddress* outAddress) {
bool isRemote = binder->remoteBinder();
bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
- if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) {
+ if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) {
// We need to be able to send instructions over the socket for how to
// connect to a different server, and we also need to let the host
// process know that this is happening.
- ALOGE("Cannot send binder from unrelated binder RPC connection.");
+ ALOGE("Cannot send binder from unrelated binder RPC session.");
return INVALID_OPERATION;
}
@@ -91,8 +91,7 @@ status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp
return OK;
}
-sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
- const RpcAddress& address) {
+sp<IBinder> RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address) {
std::unique_lock<std::mutex> _l(mNodeMutex);
if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -106,7 +105,7 @@ sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
// We have timesRecd RPC refcounts, but we only need to hold on to one
// when we keep the object. All additional dec strongs are sent
// immediately, we wait to send the last one in BpBinder::onLastDecStrong.
- (void)connection->sendDecStrong(address);
+ (void)session->sendDecStrong(address);
return binder;
}
@@ -114,9 +113,9 @@ sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");
- // Currently, all binders are assumed to be part of the same connection (no
+ // Currently, all binders are assumed to be part of the same session (no
// device global binders in the RPC world).
- sp<IBinder> binder = BpBinder::create(connection, it->first);
+ sp<IBinder> binder = BpBinder::create(session, it->first);
it->second.binder = binder;
it->second.timesRecd = 1;
return binder;
@@ -232,14 +231,13 @@ bool RpcState::rpcRec(const base::unique_fd& fd, const char* what, void* data, s
return true;
}
-sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
- const sp<RpcConnection>& connection) {
+sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session) {
Parcel data;
- data.markForRpc(connection);
+ data.markForRpc(session);
Parcel reply;
- status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
- connection, &reply, 0);
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, session,
+ &reply, 0);
if (status != OK) {
ALOGE("Error getting root object: %s", statusToString(status).c_str());
return nullptr;
@@ -248,8 +246,54 @@ sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
return reply.readStrongBinder();
}
+status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
+ size_t* maxThreadsOut) {
+ Parcel data;
+ data.markForRpc(session);
+ Parcel reply;
+
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
+ session, &reply, 0);
+ if (status != OK) {
+ ALOGE("Error getting max threads: %s", statusToString(status).c_str());
+ return status;
+ }
+
+ int32_t maxThreads;
+ status = reply.readInt32(&maxThreads);
+ if (status != OK) return status;
+ if (maxThreads <= 0) {
+ ALOGE("Error invalid max maxThreads: %d", maxThreads);
+ return BAD_VALUE;
+ }
+
+ *maxThreadsOut = maxThreads;
+ return OK;
+}
+
+status_t RpcState::getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+ int32_t* sessionIdOut) {
+ Parcel data;
+ data.markForRpc(session);
+ Parcel reply;
+
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
+ session, &reply, 0);
+ if (status != OK) {
+ ALOGE("Error getting session ID: %s", statusToString(status).c_str());
+ return status;
+ }
+
+ int32_t sessionId;
+ status = reply.readInt32(&sessionId);
+ if (status != OK) return status;
+
+ *sessionIdOut = sessionId;
+ return OK;
+}
+
status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
- const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
+ const Parcel& data, const sp<RpcSession>& session, Parcel* reply,
uint32_t flags) {
uint64_t asyncNumber = 0;
@@ -309,7 +353,7 @@ status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address
LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
- return waitForReply(fd, connection, reply);
+ return waitForReply(fd, session, reply);
}
static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -321,7 +365,7 @@ static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
LOG_ALWAYS_FATAL_IF(objectsCount, 0);
}
-status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
Parcel* reply) {
RpcWireHeader command;
while (true) {
@@ -331,7 +375,7 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnectio
if (command.command == RPC_COMMAND_REPLY) break;
- status_t status = processServerCommand(fd, connection, command);
+ status_t status = processServerCommand(fd, session, command);
if (status != OK) return status;
}
@@ -353,7 +397,7 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnectio
reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
nullptr, 0, cleanup_reply_data);
- reply->markForRpc(connection);
+ reply->markForRpc(session);
return OK;
}
@@ -384,8 +428,7 @@ status_t RpcState::sendDecStrong(const base::unique_fd& fd, const RpcAddress& ad
return OK;
}
-status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection) {
+status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, const sp<RpcSession>& session) {
LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
RpcWireHeader command;
@@ -393,15 +436,14 @@ status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
return DEAD_OBJECT;
}
- return processServerCommand(fd, connection, command);
+ return processServerCommand(fd, session, command);
}
-status_t RpcState::processServerCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
switch (command.command) {
case RPC_COMMAND_TRANSACT:
- return processTransact(fd, connection, command);
+ return processTransact(fd, session, command);
case RPC_COMMAND_DEC_STRONG:
return processDecStrong(fd, command);
}
@@ -410,12 +452,12 @@ status_t RpcState::processServerCommand(const base::unique_fd& fd,
// RPC-binder-level wire protocol is not self synchronizing, we have no way
// to understand where the current command ends and the next one begins. We
// also can't consider it a fatal error because this would allow any client
- // to kill us, so ending the connection for misbehaving client.
- ALOGE("Unknown RPC command %d - terminating connection", command.command);
+ // to kill us, so ending the session for misbehaving client.
+ ALOGE("Unknown RPC command %d - terminating session", command.command);
terminate();
return DEAD_OBJECT;
}
-status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
@@ -424,7 +466,7 @@ status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnec
return DEAD_OBJECT;
}
- return processTransactInternal(fd, connection, std::move(transactionData));
+ return processTransactInternal(fd, session, std::move(transactionData));
}
static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -436,8 +478,7 @@ static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t d
(void)objectsCount;
}
-status_t RpcState::processTransactInternal(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
std::vector<uint8_t>&& transactionData) {
if (transactionData.size() < sizeof(RpcWireTransaction)) {
ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
@@ -469,7 +510,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd,
// However, for local binders, it indicates a misbehaving client
// (any binder which is being transacted on should be holding a
// strong ref count), so in either case, terminating the
- // connection.
+ // session.
ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
addr.toString().c_str());
terminate();
@@ -499,7 +540,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd,
}
Parcel reply;
- reply.markForRpc(connection);
+ reply.markForRpc(session);
if (replyStatus == OK) {
Parcel data;
@@ -510,29 +551,41 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd,
transactionData.size() - offsetof(RpcWireTransaction, data),
nullptr /*object*/, 0 /*objectCount*/,
do_nothing_to_transact_data);
- data.markForRpc(connection);
+ data.markForRpc(session);
if (target) {
replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
} else {
LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
- // special case for 'zero' address (special server commands)
- switch (transaction->code) {
- case RPC_SPECIAL_TRANSACT_GET_ROOT: {
- sp<IBinder> root;
- sp<RpcServer> server = connection->server().promote();
- if (server) {
- root = server->getRootObject();
- } else {
- ALOGE("Root object requested, but no server attached.");
- }
- replyStatus = reply.writeStrongBinder(root);
- break;
- }
- default: {
- replyStatus = UNKNOWN_TRANSACTION;
+ sp<RpcServer> server = session->server().promote();
+ if (server) {
+ // special case for 'zero' address (special server commands)
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+ replyStatus = reply.writeStrongBinder(server->getRootObject());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+ replyStatus = reply.writeInt32(server->getMaxThreads());
+ break;
+ }
+ case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+ // only sessions w/ services can be the source of a
+ // session ID (so still guarded by non-null server)
+ //
+ // sessions associated with servers must have an ID
+ // (hence abort)
+ int32_t id = session->getPrivateAccessorForId().get().value();
+ replyStatus = reply.writeInt32(id);
+ break;
+ }
+ default: {
+ replyStatus = UNKNOWN_TRANSACTION;
+ }
}
+ } else {
+ ALOGE("Special command sent, but no server object attached.");
}
}
}
@@ -581,7 +634,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd,
const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
it->second.asyncTodo.pop();
_l.unlock();
- return processTransactInternal(fd, connection, std::move(data));
+ return processTransactInternal(fd, session, std::move(data));
}
}
return OK;
@@ -670,7 +723,7 @@ status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHead
}
_l.unlock();
- tempHold = nullptr; // destructor may make binder calls on this connection
+ tempHold = nullptr; // destructor may make binder calls on this session
return OK;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index f4f5151c73..3f3eb1c2ed 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -18,7 +18,7 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <map>
#include <queue>
@@ -43,55 +43,59 @@ struct RpcWireHeader;
/**
* Abstracts away management of ref counts and the wire format from
- * RpcConnection
+ * RpcSession
*/
class RpcState {
public:
RpcState();
~RpcState();
- sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
+ // TODO(b/182940634): combine some special transactions into one "getServerInfo" call?
+ sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session);
+ status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
+ size_t* maxThreadsOut);
+ status_t getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+ int32_t* sessionIdOut);
[[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
uint32_t code, const Parcel& data,
- const sp<RpcConnection>& connection, Parcel* reply,
- uint32_t flags);
+ const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
[[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
[[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection);
+ const sp<RpcSession>& session);
/**
* Called by Parcel for outgoing binders. This implies one refcount of
* ownership to the outgoing binder.
*/
- [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection,
- const sp<IBinder>& binder, RpcAddress* outAddress);
+ [[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
+ RpcAddress* outAddress);
/**
* Called by Parcel for incoming binders. This either returns the refcount
* to the process, if this process already has one, or it takes ownership of
* that refcount
*/
- sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address);
+ sp<IBinder> onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address);
size_t countBinders();
void dump();
private:
/**
- * Called when reading or writing data to a connection fails to clean up
- * data associated with the connection in order to cleanup binders.
+ * Called when reading or writing data to a session fails to clean up
+ * data associated with the session in order to cleanup binders.
* Specifically, we have a strong dependency cycle, since BpBinder is
* OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true).
*
- * BpBinder -> RpcConnection -> RpcState
+ * BpBinder -> RpcSession -> RpcState
* ^-----------------------------/
*
* In the success case, eventually all refcounts should be propagated over
- * the connection, though this could also be called to eagerly cleanup
- * the connection.
+ * the session, though this could also be called to eagerly cleanup
+ * the session.
*
- * WARNING: RpcState is responsible for calling this when the connection is
+ * WARNING: RpcState is responsible for calling this when the session is
* no longer recoverable.
*/
void terminate();
@@ -100,16 +104,15 @@ private:
size_t size);
[[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
- [[nodiscard]] status_t waitForReply(const base::unique_fd& fd,
- const sp<RpcConnection>& connection, Parcel* reply);
+ [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
+ Parcel* reply);
[[nodiscard]] status_t processServerCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ const sp<RpcSession>& session,
const RpcWireHeader& command);
- [[nodiscard]] status_t processTransact(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ [[nodiscard]] status_t processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command);
[[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ const sp<RpcSession>& session,
std::vector<uint8_t>&& transactionData);
[[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
const RpcWireHeader& command);
@@ -163,7 +166,7 @@ private:
std::mutex mNodeMutex;
bool mTerminated = false;
- // binders known by both sides of a connection
+ // binders known by both sides of a session
std::map<RpcAddress, BinderNode> mNodeForAddress;
};
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 60ec6c91bf..c5fa008308 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -47,8 +47,12 @@ enum : uint32_t {
*/
enum : uint32_t {
RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
+ RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1,
+ RPC_SPECIAL_TRANSACT_GET_SESSION_ID = 2,
};
+constexpr int32_t RPC_SESSION_ID_NEW = -1;
+
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/binder_module.h
index 151235c16b..9dea3b448b 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -29,14 +29,14 @@
#undef B_PACK_CHARS
#endif
-#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#include <sys/ioctl.h>
#ifndef BR_FROZEN_REPLY
// Temporary definition of BR_FROZEN_REPLY. For production
// this will come from UAPI binder.h
#define BR_FROZEN_REPLY _IO('r', 18)
-#endif //BR_FROZEN_REPLY
+#endif // BR_FROZEN_REPLY
#ifndef BINDER_FREEZE
/*
@@ -49,46 +49,46 @@ struct binder_freeze_info {
//
// Group-leader PID of process to be frozen
//
- uint32_t pid;
+ uint32_t pid;
//
// Enable(1) / Disable(0) freeze for given PID
//
- uint32_t enable;
+ uint32_t enable;
//
// Timeout to wait for transactions to drain.
// 0: don't wait (ioctl will return EAGAIN if not drained)
// N: number of ms to wait
- uint32_t timeout_ms;
+ uint32_t timeout_ms;
};
-#endif //BINDER_FREEZE
+#endif // BINDER_FREEZE
#ifndef BINDER_GET_FROZEN_INFO
-#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
+#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
struct binder_frozen_status_info {
//
// Group-leader PID of process to be queried
//
- __u32 pid;
+ __u32 pid;
//
// Indicates whether the process has received any sync calls since last
// freeze (cleared at freeze/unfreeze)
//
- __u32 sync_recv;
+ __u32 sync_recv;
//
// Indicates whether the process has received any async calls since last
// freeze (cleared at freeze/unfreeze)
//
- __u32 async_recv;
+ __u32 async_recv;
};
-#endif //BINDER_GET_FROZEN_INFO
+#endif // BINDER_GET_FROZEN_INFO
#ifndef BR_ONEWAY_SPAM_SUSPECT
// Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production
// this will come from UAPI binder.h
#define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19)
-#endif //BR_ONEWAY_SPAM_SUSPECT
+#endif // BR_ONEWAY_SPAM_SUSPECT
#ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION
/*
@@ -96,6 +96,6 @@ struct binder_frozen_status_info {
* these will be defined in the UAPI binder.h file from upstream kernel.
*/
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
-#endif //BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index ad618f9de4..61bf018e43 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -28,7 +28,7 @@
// ---------------------------------------------------------------------------
namespace android {
-class RpcConnection;
+class RpcSession;
class RpcState;
namespace internal {
class Stability;
@@ -41,11 +41,11 @@ class BpBinder : public IBinder
{
public:
static sp<BpBinder> create(int32_t handle);
- static sp<BpBinder> create(const sp<RpcConnection>& connection, const RpcAddress& address);
+ static sp<BpBinder> create(const sp<RpcSession>& session, const RpcAddress& address);
/**
* Return value:
- * true - this is associated with a socket RpcConnection
+ * true - this is associated with a socket RpcSession
* false - (usual) binder over e.g. /dev/binder
*/
bool isRpcBinder() const;
@@ -133,7 +133,7 @@ public:
// valid if isRpcBinder
const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
- const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); }
+ const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
const BpBinder* mBinder;
};
@@ -148,19 +148,19 @@ private:
struct BinderHandle {
int32_t handle;
};
- struct SocketHandle {
- sp<RpcConnection> connection;
+ struct RpcHandle {
+ sp<RpcSession> session;
RpcAddress address;
};
- using Handle = std::variant<BinderHandle, SocketHandle>;
+ using Handle = std::variant<BinderHandle, RpcHandle>;
int32_t binderHandle() const;
const RpcAddress& rpcAddress() const;
- const sp<RpcConnection>& rpcConnection() const;
+ const sp<RpcSession>& rpcSession() const;
explicit BpBinder(Handle&& handle);
BpBinder(BinderHandle&& handle, int32_t trackedUid);
- explicit BpBinder(SocketHandle&& handle);
+ explicit BpBinder(RpcHandle&& handle);
virtual ~BpBinder();
virtual void onFirstRef();
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 31f63c86b8..97c826ce76 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/unique_fd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -49,39 +50,39 @@ class [[clang::lto_visibility_public]] IBinder : public virtual RefBase
{
public:
enum {
- FIRST_CALL_TRANSACTION = 0x00000001,
- LAST_CALL_TRANSACTION = 0x00ffffff,
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff,
- PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
- DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'),
- SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
- INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
- SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
- EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
- DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
+ PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'),
+ DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'),
+ SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'),
+ INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
+ SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
+ EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
+ DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
// See android.os.IBinder.TWEET_TRANSACTION
// Most importantly, messages can be anything not exceeding 130 UTF-8
// characters, and callees should exclaim "jolly good message old boy!"
- TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'),
+ TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'),
// See android.os.IBinder.LIKE_TRANSACTION
// Improve binder self-esteem.
- LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'),
+ LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'),
// Corresponds to TF_ONE_WAY -- an asynchronous call.
- FLAG_ONEWAY = 0x00000001,
+ FLAG_ONEWAY = 0x00000001,
// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
// is made
- FLAG_CLEAR_BUF = 0x00000020,
+ FLAG_CLEAR_BUF = 0x00000020,
// Private userspace flag for transaction which is being requested from
// a vendor context.
- FLAG_PRIVATE_VENDOR = 0x10000000,
+ FLAG_PRIVATE_VENDOR = 0x10000000,
};
- IBinder();
+ IBinder();
/**
* Check if this IBinder implements the interface named by
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 957837233b..5aaaa0c3d2 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -50,7 +50,7 @@ template <typename T> class LightFlattenable;
class IBinder;
class IPCThreadState;
class ProcessState;
-class RpcConnection;
+class RpcSession;
class String8;
class TextOutput;
@@ -103,7 +103,7 @@ public:
// Whenever possible, markForBinder should be preferred. This method is
// called automatically on reply Parcels for RPC transactions.
- void markForRpc(const sp<RpcConnection>& connection);
+ void markForRpc(const sp<RpcSession>& session);
// Whether this Parcel is written for RPC transactions (after calls to
// markForBinder or markForRpc).
@@ -1136,7 +1136,7 @@ private:
release_func mOwner;
- sp<RpcConnection> mConnection;
+ sp<RpcSession> mSession;
class Blob {
public:
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index 835a3a8eb8..21aa705ff7 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -73,6 +73,8 @@ public:
static bool checkPermission(const String16& permission,
pid_t pid, uid_t uid);
+
+ static void purgeCache();
};
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index d29b651f0b..c98151d293 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -17,57 +17,95 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <mutex>
+
// WARNING: This is a feature which is still in development, and it is subject
// to radical change. Any production use of this may subject your code to any
// number of problems.
namespace android {
+class RpcSocketAddress;
+
/**
* This represents a server of an interface, which may be connected to by any
* number of clients over sockets.
*
- * This object is not (currently) thread safe. All calls to it are expected to
- * happen at process startup.
+ * Usage:
+ * auto server = RpcServer::make();
+ * // only supports one now
+ * if (!server->setup*Server(...)) {
+ * :(
+ * }
+ * server->join();
*/
class RpcServer final : public virtual RefBase {
public:
static sp<RpcServer> make();
- void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ /**
+ * This represents a session for responses, e.g.:
+ *
+ * process A serves binder a
+ * process B opens a session to process A
+ * process B makes binder b and sends it to A
+ * A uses this 'back session' to send things back to B
+ */
+ [[nodiscard]] bool setupUnixDomainServer(const char* path);
+#ifdef __BIONIC__
/**
- * Setup a static connection, when the number of clients are known.
+ * Creates an RPC server at the current port.
+ */
+ [[nodiscard]] bool setupVsockServer(unsigned int port);
+#endif // __BIONIC__
+
+ /**
+ * Creates an RPC server at the current port using IPv4.
*
- * Each call to this function corresponds to a different client, and clients
- * each have their own threadpools.
+ * TODO(b/182914638): IPv6 support
*
- * TODO(b/167966510): support dynamic creation of connections/threads
+ * Set |port| to 0 to pick an ephemeral port; see discussion of
+ * /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort|
+ * will be set to the picked port number, if it is not null.
*/
- sp<RpcConnection> addClientConnection();
+ [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+
+ void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
/**
- * Allowing a server to explicitly drop clients would be easy to add here,
- * but it is not currently implemented, since users of this functionality
- * could not use similar functionality if they are running under real
- * binder.
+ * This must be called before adding a client session.
+ *
+ * If this is not specified, this will be a single-threaded server.
+ *
+ * TODO(b/185167543): these are currently created per client, but these
+ * should be shared.
*/
- // void drop(const sp<RpcConnection>& connection);
+ void setMaxThreads(size_t threads);
+ size_t getMaxThreads();
/**
* The root object can be retrieved by any client, without any
- * authentication.
+ * authentication. TODO(b/183988761)
*/
void setRootObject(const sp<IBinder>& binder);
+ sp<IBinder> getRootObject();
/**
- * Root object set with setRootObject
+ * You must have at least one client session before calling this.
+ *
+ * TODO(b/185167543): way to shut down?
*/
- sp<IBinder> getRootObject();
+ void join();
+
+ /**
+ * For debugging!
+ */
+ std::vector<sp<RpcSession>> listSessions();
~RpcServer();
@@ -75,11 +113,17 @@ private:
friend sp<RpcServer>;
RpcServer();
+ bool setupSocketServer(const RpcSocketAddress& address);
+
bool mAgreedExperimental = false;
+ bool mStarted = false; // TODO(b/185167543): support dynamically added clients
+ size_t mMaxThreads = 1;
+ base::unique_fd mServer; // socket we are accepting sessions on
+ std::mutex mLock; // for below
sp<IBinder> mRootObject;
-
- std::vector<sp<RpcConnection>> mConnections; // per-client
+ std::map<int32_t, sp<RpcSession>> mSessions;
+ int32_t mSessionIdCounter = 0;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcSession.h
index 2395e78de2..3f58b2ce8e 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -21,7 +21,9 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <map>
#include <optional>
+#include <thread>
#include <vector>
// WARNING: This is a feature which is still in development, and it is subject
@@ -32,103 +34,92 @@ namespace android {
class Parcel;
class RpcServer;
+class RpcSocketAddress;
class RpcState;
/**
- * This represents a multi-threaded/multi-socket connection between a client
- * and a server.
+ * This represents a session (group of connections) between a client
+ * and a server. Multiple connections are needed for multiple parallel "binder"
+ * calls which may also have nested calls.
*/
-class RpcConnection final : public virtual RefBase {
+class RpcSession final : public virtual RefBase {
public:
- static sp<RpcConnection> make();
-
- /**
- * This represents a connection for responses, e.g.:
- *
- * process A serves binder a
- * process B opens a connection to process A
- * process B makes binder b and sends it to A
- * A uses this 'back connection' to send things back to B
- *
- * This should be called once, and then a call should be made to join per
- * connection thread.
- */
- [[nodiscard]] bool setupUnixDomainServer(const char* path);
+ static sp<RpcSession> make();
/**
* This should be called once per thread, matching 'join' in the remote
* process.
*/
- [[nodiscard]] bool addUnixDomainClient(const char* path);
+ [[nodiscard]] bool setupUnixDomainClient(const char* path);
#ifdef __BIONIC__
/**
- * Creates an RPC server at the current port.
- */
- [[nodiscard]] bool setupVsockServer(unsigned int port);
-
- /**
* Connects to an RPC server at the CVD & port.
*/
- [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
+ [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
#endif // __BIONIC__
/**
- * Creates an RPC server at the current port.
- */
- [[nodiscard]] bool setupInetServer(unsigned int port);
-
- /**
* Connects to an RPC server at the given address and port.
*/
- [[nodiscard]] bool addInetClient(const char* addr, unsigned int port);
+ [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
/**
* For debugging!
*
- * Sets up an empty socket. All queries to this socket which require a
+ * Sets up an empty connection. All queries to this connection which require a
* response will never be satisfied. All data sent here will be
* unceremoniously cast down the bottomless pit, /dev/null.
*/
[[nodiscard]] bool addNullDebuggingClient();
/**
- * Query the other side of the connection for the root object hosted by that
+ * Query the other side of the session for the root object hosted by that
* process's RpcServer (if one exists)
*/
sp<IBinder> getRootObject();
+ /**
+ * Query the other side of the session for the maximum number of threads
+ * it supports (maximum number of concurrent non-nested synchronous transactions)
+ */
+ status_t getMaxThreads(size_t* maxThreads);
+
[[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
[[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
- /**
- * Adds a server thread accepting connections. Must be called after
- * setup*Server.
- */
- void join();
-
- ~RpcConnection();
+ ~RpcSession();
- void setForServer(const wp<RpcServer>& server);
wp<RpcServer> server();
// internal only
const std::unique_ptr<RpcState>& state() { return mState; }
- class SocketAddress {
- public:
- virtual ~SocketAddress();
- virtual std::string toString() const = 0;
- virtual const sockaddr* addr() const = 0;
- virtual size_t addrSize() const = 0;
+ class PrivateAccessorForId {
+ private:
+ friend class RpcSession;
+ friend class RpcState;
+ explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {}
+
+ const std::optional<int32_t> get() { return mSession->mId; }
+
+ const RpcSession* mSession;
};
+ PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); }
private:
- friend sp<RpcConnection>;
- RpcConnection();
+ friend PrivateAccessorForId;
+ friend sp<RpcSession>;
+ friend RpcServer;
+ RpcSession();
- struct ConnectionSocket : public RefBase {
+ status_t readId();
+
+ void startThread(base::unique_fd client);
+ void join(base::unique_fd client);
+
+ struct RpcConnection : public RefBase {
base::unique_fd fd;
// whether this or another thread is currently using this fd to make
@@ -136,32 +127,34 @@ private:
std::optional<pid_t> exclusiveTid;
};
- bool setupSocketServer(const SocketAddress& address);
- bool addSocketClient(const SocketAddress& address);
- void addClient(base::unique_fd&& fd);
- sp<ConnectionSocket> assignServerToThisThread(base::unique_fd&& fd);
- bool removeServerSocket(const sp<ConnectionSocket>& socket);
+ bool setupSocketClient(const RpcSocketAddress& address);
+ bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId);
+ void addClient(base::unique_fd fd);
+ void setForServer(const wp<RpcServer>& server, int32_t sessionId);
+ sp<RpcConnection> assignServerToThisThread(base::unique_fd fd);
+ bool removeServerConnection(const sp<RpcConnection>& connection);
- enum class SocketUse {
+ enum class ConnectionUse {
CLIENT,
CLIENT_ASYNC,
CLIENT_REFCOUNT,
};
- // RAII object for connection socket
- class ExclusiveSocket {
+ // RAII object for session connection
+ class ExclusiveConnection {
public:
- explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use);
- ~ExclusiveSocket();
- const base::unique_fd& fd() { return mSocket->fd; }
+ explicit ExclusiveConnection(const sp<RpcSession>& session, ConnectionUse use);
+ ~ExclusiveConnection();
+ const base::unique_fd& fd() { return mConnection->fd; }
private:
- static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
- sp<ConnectionSocket>* available,
- std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint);
+ static void findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ sp<RpcConnection>* available,
+ std::vector<sp<RpcConnection>>& sockets,
+ size_t socketsIndexHint);
- sp<RpcConnection> mConnection; // avoid deallocation
- sp<ConnectionSocket> mSocket;
+ sp<RpcSession> mSession; // avoid deallocation
+ sp<RpcConnection> mConnection;
// whether this is being used for a nested transaction (being on the same
// thread guarantees we won't write in the middle of a message, the way
@@ -169,10 +162,10 @@ private:
bool mReentrant = false;
};
- // On the other side of a connection, for each of mClients here, there should
+ // On the other side of a session, for each of mClients here, there should
// be one of mServers on the other side (and vice versa).
//
- // For the simplest connection, a single server with one client, you would
+ // For the simplest session, a single server with one client, you would
// have:
// - the server has a single 'mServers' and a thread listening on this
// - the client has a single 'mClients' and makes calls to this
@@ -183,18 +176,26 @@ private:
// For a more complicated case, the client might itself open up a thread to
// serve calls to the server at all times (e.g. if it hosts a callback)
- wp<RpcServer> mForServer; // maybe null, for client connections
+ wp<RpcServer> mForServer; // maybe null, for client sessions
+
+ // TODO(b/183988761): this shouldn't be guessable
+ std::optional<int32_t> mId;
std::unique_ptr<RpcState> mState;
- base::unique_fd mServer; // socket we are accepting connections on
+ std::mutex mMutex; // for all below
- std::mutex mSocketMutex; // for all below
- std::condition_variable mSocketCv; // for mWaitingThreads
+ std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
size_t mWaitingThreads = 0;
size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
- std::vector<sp<ConnectionSocket>> mClients;
- std::vector<sp<ConnectionSocket>> mServers;
+ std::vector<sp<RpcConnection>> mClients;
+ std::vector<sp<RpcConnection>> mServers;
+
+ // TODO(b/185167543): use for reverse sessions (allow client to also
+ // serve calls on a session).
+ // TODO(b/185167543): allow sharing between different sessions in a
+ // process? (or combine with mServers)
+ std::map<std::thread::id, std::thread> mThreads;
};
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index eb103d3d77..b03e24cd1a 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -55,7 +55,9 @@ cc_library {
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
- llndk_stubs: "libbinder_ndk.llndk",
+ llndk: {
+ symbol_file: "libbinder_ndk.map.txt",
+ },
export_include_dirs: [
"include_cpp",
@@ -192,13 +194,3 @@ ndk_library {
symbol_file: "libbinder_ndk.map.txt",
first_version: "29",
}
-
-llndk_library {
- name: "libbinder_ndk.llndk",
- symbol_file: "libbinder_ndk.map.txt",
- export_include_dirs: [
- "include_cpp",
- "include_ndk",
- "include_platform",
- ],
-}
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
index 332e2ada52..a47b753497 100644
--- a/libs/binder/parcel_fuzzer/main.cpp
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -23,7 +23,7 @@
#include <iostream>
#include <android-base/logging.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
@@ -33,7 +33,7 @@
#include <sys/time.h>
using android::fillRandomParcel;
-using android::RpcConnection;
+using android::RpcSession;
using android::sp;
void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
@@ -61,9 +61,9 @@ void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
P p;
if constexpr (std::is_same_v<P, android::Parcel>) {
if (provider.ConsumeBool()) {
- auto connection = sp<RpcConnection>::make();
- CHECK(connection->addNullDebuggingClient());
- p.markForRpc(connection);
+ auto session = sp<RpcSession>::make();
+ CHECK(session->addNullDebuggingClient());
+ p.markForRpc(session);
fillRandomParcelData(&p, std::move(provider));
} else {
fillRandomParcel(&p, std::move(provider));
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 57c9013f60..49d3401a4f 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -50,6 +50,8 @@ rust_library {
"//apex_available:platform",
"com.android.virt",
],
+ lints: "none",
+ clippy_lints: "none",
}
rust_bindgen {
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 9095af29e0..1d1a295221 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -16,14 +16,6 @@
//! Generated Rust bindings to libbinder_ndk
-#![allow(
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unused,
- improper_ctypes,
- missing_docs
-)]
use std::error::Error;
use std::fmt;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index f303b7c1e3..c0f7c99564 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -35,6 +35,7 @@ cc_test {
name: "binderDriverInterfaceTest_IPC_32",
defaults: ["binder_test_defaults"],
srcs: ["binderDriverInterfaceTest.cpp"],
+ header_libs: ["libbinder_headers"],
compile_multilib: "32",
multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -49,7 +50,7 @@ cc_test {
cflags: ["-DBINDER_IPC_32BIT=1"],
},
},
-
+ header_libs: ["libbinder_headers"],
srcs: ["binderDriverInterfaceTest.cpp"],
test_suites: ["device-tests", "vts"],
}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 2bdb26483f..ef4198d8f2 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -18,8 +18,8 @@ interface IBinderRpcTest {
oneway void sendString(@utf8InCpp String str);
@utf8InCpp String doubleString(@utf8InCpp String str);
- // number of known RPC binders to process, RpcState::countBinders
- int countBinders();
+ // number of known RPC binders to process, RpcState::countBinders by session
+ int[] countBinders();
// Caller sends server, callee pings caller's server and returns error code.
int pingMe(IBinder binder);
@@ -36,7 +36,7 @@ interface IBinderRpcTest {
// should always return the same binder
IBinder alwaysGiveMeTheSameBinder();
- // Idea is that the server will not hold onto the session, the remote connection
+ // Idea is that the server will not hold onto the session, the remote session
// object must. This is to test lifetimes of binder objects, and consequently, also
// identity (since by assigning sessions names, we can make sure a section always
// references the session it was originally opened with).
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index dc8c0f157e..5676bd1939 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -31,11 +31,11 @@
#include <binder/IServiceManager.h>
#include <binder/ParcelRef.h>
-#include <private/binder/binder_module.h>
#include <linux/sched.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
+#include "../binder_module.h"
#include "binderAbiHelper.h"
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 7c82226aef..a457e67735 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,8 +18,8 @@
#include <android-base/logging.h>
#include <benchmark/benchmark.h>
#include <binder/Binder.h>
-#include <binder/RpcConnection.h>
#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <thread>
@@ -30,8 +30,8 @@ using android::BBinder;
using android::IBinder;
using android::interface_cast;
using android::OK;
-using android::RpcConnection;
using android::RpcServer;
+using android::RpcSession;
using android::sp;
using android::binder::Status;
@@ -46,17 +46,17 @@ class MyBinderRpcBenchmark : public BnBinderRpcBenchmark {
}
};
-static sp<RpcConnection> gConnection = RpcConnection::make();
+static sp<RpcSession> gSession = RpcSession::make();
void BM_getRootObject(benchmark::State& state) {
while (state.KeepRunning()) {
- CHECK(gConnection->getRootObject() != nullptr);
+ CHECK(gSession->getRootObject() != nullptr);
}
}
BENCHMARK(BM_getRootObject);
void BM_pingTransaction(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
while (state.KeepRunning()) {
@@ -66,7 +66,7 @@ void BM_pingTransaction(benchmark::State& state) {
BENCHMARK(BM_pingTransaction);
void BM_repeatString(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -95,7 +95,7 @@ void BM_repeatString(benchmark::State& state) {
BENCHMARK(BM_repeatString);
void BM_repeatBinder(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -121,21 +121,18 @@ int main(int argc, char** argv) {
std::thread([addr]() {
sp<RpcServer> server = RpcServer::make();
server->setRootObject(sp<MyBinderRpcBenchmark>::make());
-
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
- sp<RpcConnection> connection = server->addClientConnection();
- CHECK(connection->setupUnixDomainServer(addr.c_str()));
-
- connection->join();
+ CHECK(server->setupUnixDomainServer(addr.c_str()));
+ server->join();
}).detach();
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
- if (gConnection->addUnixDomainClient(addr.c_str())) goto success;
+ if (gSession->setupUnixDomainClient(addr.c_str())) goto success;
}
LOG(FATAL) << "Could not connect.";
success:
::benchmark::RunSpecifiedBenchmarks();
+ return 0;
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index dd68fdbc6d..8d10727c5b 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -17,6 +17,7 @@
#include <BnBinderRpcSession.h>
#include <BnBinderRpcTest.h>
#include <aidl/IBinderRpcTest.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
@@ -24,8 +25,8 @@
#include <binder/BpBinder.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
-#include <binder/RpcConnection.h>
#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -77,7 +78,7 @@ std::atomic<int32_t> MyBinderRpcSession::gNum;
class MyBinderRpcTest : public BnBinderRpcTest {
public:
- sp<RpcConnection> connection;
+ wp<RpcServer> server;
Status sendString(const std::string& str) override {
(void)str;
@@ -87,13 +88,20 @@ public:
*strstr = str + str;
return Status::ok();
}
- Status countBinders(int32_t* out) override {
- if (connection == nullptr) {
+ Status countBinders(std::vector<int32_t>* out) override {
+ sp<RpcServer> spServer = server.promote();
+ if (spServer == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
- *out = connection->state()->countBinders();
- if (*out != 1) {
- connection->state()->dump();
+ out->clear();
+ for (auto session : spServer->listSessions()) {
+ size_t count = session->state()->countBinders();
+ if (count != 1) {
+ // this is called when there is only one binder held remaining,
+ // so to aid debugging
+ session->state()->dump();
+ }
+ out->push_back(count);
}
return Status::ok();
}
@@ -176,14 +184,27 @@ public:
};
sp<IBinder> MyBinderRpcTest::mHeldBinder;
+class Pipe {
+public:
+ Pipe() { CHECK(android::base::Pipe(&mRead, &mWrite)); }
+ Pipe(Pipe&&) = default;
+ android::base::borrowed_fd readEnd() { return mRead; }
+ android::base::borrowed_fd writeEnd() { return mWrite; }
+
+private:
+ android::base::unique_fd mRead;
+ android::base::unique_fd mWrite;
+};
+
class Process {
public:
- Process(const std::function<void()>& f) {
+ Process(Process&&) = default;
+ Process(const std::function<void(Pipe*)>& f) {
if (0 == (mPid = fork())) {
// racey: assume parent doesn't crash before this is set
prctl(PR_SET_PDEATHSIG, SIGHUP);
- f();
+ f(&mPipe);
}
}
~Process() {
@@ -191,9 +212,11 @@ public:
kill(mPid, SIGKILL);
}
}
+ Pipe* getPipe() { return &mPipe; }
private:
pid_t mPid = 0;
+ Pipe mPipe;
};
static std::string allocateSocketAddress() {
@@ -202,48 +225,63 @@ static std::string allocateSocketAddress() {
return temp + "/binderRpcTest_" + std::to_string(id++);
};
-struct ProcessConnection {
+struct ProcessSession {
// reference to process hosting a socket server
Process host;
- // client connection object associated with other process
- sp<RpcConnection> connection;
+ struct SessionInfo {
+ sp<RpcSession> session;
+ sp<IBinder> root;
+ };
- // pre-fetched root object
- sp<IBinder> rootBinder;
+ // client session objects associated with other process
+ // each one represents a separate session
+ std::vector<SessionInfo> sessions;
- // whether connection should be invalidated by end of run
- bool expectInvalid = false;
+ ProcessSession(ProcessSession&&) = default;
+ ~ProcessSession() {
+ for (auto& session : sessions) {
+ session.root = nullptr;
+ }
- ~ProcessConnection() {
- rootBinder = nullptr;
- EXPECT_NE(nullptr, connection);
- EXPECT_NE(nullptr, connection->state());
- EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:");
+ for (auto& info : sessions) {
+ sp<RpcSession>& session = info.session;
- wp<RpcConnection> weakConnection = connection;
- connection = nullptr;
- EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+ EXPECT_NE(nullptr, session);
+ EXPECT_NE(nullptr, session->state());
+ EXPECT_EQ(0, session->state()->countBinders()) << (session->state()->dump(), "dump:");
+
+ wp<RpcSession> weakSession = session;
+ session = nullptr;
+ EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
+ }
}
};
-// Process connection where the process hosts IBinderRpcTest, the server used
+// Process session where the process hosts IBinderRpcTest, the server used
// for most testing here
-struct BinderRpcTestProcessConnection {
- ProcessConnection proc;
+struct BinderRpcTestProcessSession {
+ ProcessSession proc;
- // pre-fetched root object
+ // pre-fetched root object (for first session)
sp<IBinder> rootBinder;
- // pre-casted root object
+ // pre-casted root object (for first session)
sp<IBinderRpcTest> rootIface;
- ~BinderRpcTestProcessConnection() {
- if (!proc.expectInvalid) {
- int32_t remoteBinders = 0;
- EXPECT_OK(rootIface->countBinders(&remoteBinders));
- // should only be the root binder object, iface
- EXPECT_EQ(remoteBinders, 1);
+ // whether session should be invalidated by end of run
+ bool expectInvalid = false;
+
+ BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
+ ~BinderRpcTestProcessSession() {
+ if (!expectInvalid) {
+ std::vector<int32_t> remoteCounts;
+ // calling over any sessions counts across all sessions
+ EXPECT_OK(rootIface->countBinders(&remoteCounts));
+ EXPECT_EQ(remoteCounts.size(), proc.sessions.size());
+ for (auto remoteCount : remoteCounts) {
+ EXPECT_EQ(remoteCount, 1);
+ }
}
rootIface = nullptr;
@@ -277,98 +315,99 @@ class BinderRpc : public ::testing::TestWithParam<SocketType> {
public:
// This creates a new process serving an interface on a certain number of
// threads.
- ProcessConnection createRpcTestSocketServerProcess(
- size_t numThreads,
- const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
- CHECK_GT(numThreads, 0);
+ ProcessSession createRpcTestSocketServerProcess(
+ size_t numThreads, size_t numSessions,
+ const std::function<void(const sp<RpcServer>&)>& configure) {
+ CHECK_GE(numSessions, 1) << "Must have at least one session to a server";
SocketType socketType = GetParam();
std::string addr = allocateSocketAddress();
unlink(addr.c_str());
- static unsigned int port = 3456;
- port++;
+ static unsigned int vsockPort = 3456;
+ vsockPort++;
- auto ret = ProcessConnection{
- .host = Process([&] {
+ auto ret = ProcessSession{
+ .host = Process([&](Pipe* pipe) {
sp<RpcServer> server = RpcServer::make();
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
- // server supporting one client on one socket
- sp<RpcConnection> connection = server->addClientConnection();
+ server->setMaxThreads(numThreads);
switch (socketType) {
case SocketType::UNIX:
- CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+ CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
break;
#ifdef __BIONIC__
case SocketType::VSOCK:
- CHECK(connection->setupVsockServer(port));
+ CHECK(server->setupVsockServer(vsockPort));
break;
#endif // __BIONIC__
- case SocketType::INET:
- CHECK(connection->setupInetServer(port));
+ case SocketType::INET: {
+ unsigned int outPort = 0;
+ CHECK(server->setupInetServer(0, &outPort));
+ CHECK_NE(0, outPort);
+ CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort,
+ sizeof(outPort)));
break;
+ }
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
- configure(server, connection);
+ configure(server);
- // accept 'numThreads' connections
- std::vector<std::thread> pool;
- for (size_t i = 0; i + 1 < numThreads; i++) {
- pool.push_back(std::thread([=] { connection->join(); }));
- }
- connection->join();
- for (auto& t : pool) t.join();
+ server->join();
}),
- .connection = RpcConnection::make(),
};
- // create remainder of connections
- for (size_t i = 0; i < numThreads; i++) {
- for (size_t tries = 0; tries < 5; tries++) {
+ unsigned int inetPort = 0;
+ if (socketType == SocketType::INET) {
+ CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &inetPort,
+ sizeof(inetPort)));
+ CHECK_NE(0, inetPort);
+ }
+
+ for (size_t i = 0; i < numSessions; i++) {
+ sp<RpcSession> session = RpcSession::make();
+ for (size_t tries = 0; tries < 10; tries++) {
usleep(10000);
switch (socketType) {
case SocketType::UNIX:
- if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
+ if (session->setupUnixDomainClient(addr.c_str())) goto success;
break;
#ifdef __BIONIC__
case SocketType::VSOCK:
- if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
+ if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
break;
#endif // __BIONIC__
case SocketType::INET:
- if (ret.connection->addInetClient("127.0.0.1", port)) goto success;
+ if (session->setupInetClient("127.0.0.1", inetPort)) goto success;
break;
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
}
LOG_ALWAYS_FATAL("Could not connect");
- success:;
+ success:
+ ret.sessions.push_back({session, session->getRootObject()});
}
-
- ret.rootBinder = ret.connection->getRootObject();
return ret;
}
- BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
- BinderRpcTestProcessConnection ret{
- .proc = createRpcTestSocketServerProcess(numThreads,
- [&](const sp<RpcServer>& server,
- const sp<RpcConnection>& connection) {
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads,
+ size_t numSessions = 1) {
+ BinderRpcTestProcessSession ret{
+ .proc = createRpcTestSocketServerProcess(numThreads, numSessions,
+ [&](const sp<RpcServer>& server) {
sp<MyBinderRpcTest> service =
new MyBinderRpcTest;
server->setRootObject(service);
- service->connection =
- connection; // for testing only
+ service->server = server;
}),
};
- ret.rootBinder = ret.proc.rootBinder;
+ ret.rootBinder = ret.proc.sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
@@ -376,18 +415,12 @@ public:
};
TEST_P(BinderRpc, RootObjectIsNull) {
- auto proc = createRpcTestSocketServerProcess(1,
- [](const sp<RpcServer>& server,
- const sp<RpcConnection>&) {
- // this is the default, but to be explicit
- server->setRootObject(nullptr);
- });
-
- // retrieved by getRootObject when process is created above
- EXPECT_EQ(nullptr, proc.rootBinder);
+ auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) {
+ // this is the default, but to be explicit
+ server->setRootObject(nullptr);
+ });
- // make sure we can retrieve it again (process doesn't crash)
- EXPECT_EQ(nullptr, proc.connection->getRootObject());
+ EXPECT_EQ(nullptr, proc.sessions.at(0).root);
}
TEST_P(BinderRpc, Ping) {
@@ -402,6 +435,14 @@ TEST_P(BinderRpc, GetInterfaceDescriptor) {
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
+TEST_P(BinderRpc, MultipleSessions) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*sessions*/);
+ for (auto session : proc.proc.sessions) {
+ ASSERT_NE(nullptr, session.root);
+ EXPECT_EQ(OK, session.root->pingBinder());
+ }
+}
+
TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
auto proc = createRpcTestSocketServerProcess(1);
Parcel data;
@@ -540,7 +581,7 @@ TEST_P(BinderRpc, HoldBinder) {
// These are behavioral differences form regular binder, where certain usecases
// aren't supported.
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
auto proc1 = createRpcTestSocketServerProcess(1);
auto proc2 = createRpcTestSocketServerProcess(1);
@@ -549,6 +590,15 @@ TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*sessions*/);
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder)
+ .transactionError());
+}
+
TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
auto proc = createRpcTestSocketServerProcess(1);
@@ -833,7 +883,7 @@ TEST_P(BinderRpc, Die) {
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
- proc.proc.expectInvalid = true;
+ proc.expectInvalid = true;
}
}
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 8a82c2abdf..29c788bca3 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -47,6 +47,7 @@ static const char* native_processes_to_dump[] = {
// Native processes to dump on debuggable builds.
static const char* debuggable_native_processes_to_dump[] = {
+ "/system/bin/keystore2",
"/system/bin/vold",
NULL,
};
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3d854c2b92..37fb8448ee 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -666,12 +666,12 @@ private:
void run() {
std::unique_lock<std::mutex> lock(mMutex);
while (!mDone) {
- mCv.wait(lock);
while (!mRunnables.empty()) {
std::function<void()> runnable = mRunnables.front();
mRunnables.pop_front();
runnable();
}
+ mCv.wait(lock);
}
}
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index e707684618..0827bbe3ee 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -119,9 +119,11 @@ std::string to_string(const LayerDebugInfo& info) {
info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
if (info.mStretchEffect.hasEffect()) {
const auto& se = info.mStretchEffect;
- StringAppendF(&result, " StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n",
- se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX,
- se.vectorY, se.maxAmount);
+ StringAppendF(&result,
+ " StretchEffect width = %f, height = %f vec=(%f, %f) "
+ "maxAmount=(%f, %f)\n",
+ se.width, se.height,
+ se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY);
}
StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 517b49e6b5..267db7686a 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -98,7 +98,6 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.write, transparentRegion);
SAFE_PARCEL(output.writeUint32, transform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
- SAFE_PARCEL(output.write, crop);
SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
if (buffer) {
@@ -167,6 +166,7 @@ status_t layer_state_t::write(Parcel& output) const
}
SAFE_PARCEL(output.write, stretchEffect);
+ SAFE_PARCEL(output.write, bufferCrop);
return NO_ERROR;
}
@@ -209,7 +209,6 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, transparentRegion);
SAFE_PARCEL(input.readUint32, &transform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
- SAFE_PARCEL(input.read, crop);
SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
bool tmpBool = false;
@@ -296,6 +295,7 @@ status_t layer_state_t::read(const Parcel& input)
}
SAFE_PARCEL(input.read, stretchEffect);
+ SAFE_PARCEL(input.read, bufferCrop);
return NO_ERROR;
}
@@ -539,6 +539,10 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eStretchChanged;
stretchEffect = other.stretchEffect;
}
+ if (other.what & eBufferCropChanged) {
+ what |= eBufferCropChanged;
+ bufferCrop = other.bufferCrop;
+ }
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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5db0eae416..11b8ebac81 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1648,8 +1648,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApply
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
- const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
- float vecY, float maxAmount) {
+ const sp<SurfaceControl>& sc, const StretchEffect& stretchEffect) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1657,10 +1656,22 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStret
}
s->what |= layer_state_t::eStretchChanged;
- s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
- .vectorX = vecX,
- .vectorY = vecY,
- .maxAmount = maxAmount};
+ s->stretchEffect = stretchEffect;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop(
+ const sp<SurfaceControl>& sc, const Rect& bufferCrop) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBufferCropChanged;
+ s->bufferCrop = bufferCrop;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 85ae9cb2bc..ce9716f1fe 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -42,6 +42,10 @@ enum JankType {
BufferStuffing = 0x40,
// Jank due to unknown reasons.
Unknown = 0x80,
+ // SF is said to be stuffed if the previous frame ran longer than expected resulting in the case
+ // where the previous frame was presented in the current frame's expected vsync. This pushes the
+ // current frame to the next vsync. The behavior is similar to BufferStuffing.
+ SurfaceFlingerStuffing = 0x100,
};
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index b4f62f2206..3947f22462 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -85,7 +85,7 @@ struct layer_state_t {
eReleaseBufferListenerChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
eLayerCreated = 0x00001000,
- /* was eDetachChildren, now available 0x00002000, */
+ eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
eColorChanged = 0x00010000,
@@ -227,6 +227,8 @@ struct layer_state_t {
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
+ Rect bufferCrop;
+
// Listens to when the buffer is safe to be released. This is used for blast
// layers only. The callback includes a release fence as well as the graphic
// buffer id to identify the buffer.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5bbd8e3d85..35757be988 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -538,9 +538,22 @@ public:
// transactions from blocking each other.
Transaction& setApplyToken(const sp<IBinder>& token);
- Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
- float right, float bottom, float vecX, float vecY,
- float maxAmount);
+ /**
+ * Provides the stretch effect configured on a container that the
+ * surface is rendered within.
+ * @param sc target surface the stretch should be applied to
+ * @param stretchEffect the corresponding stretch effect to be applied
+ * to the surface. This can be directly on the surface itself or
+ * configured from a parent of the surface in which case the
+ * StretchEffect provided has parameters mapping the position of
+ * the surface within the container that has the stretch configured
+ * on it
+ * @return The transaction being constructed
+ */
+ Transaction& setStretchEffect(const sp<SurfaceControl>& sc,
+ const StretchEffect& stretchEffect);
+
+ Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 5600eb3f5e..649a14087e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -340,7 +340,8 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3
int32_t flags, int32_t edgeFlags, int32_t metaState,
int32_t buttonState, MotionClassification classification,
const ui::Transform& transform, float xPrecision, float yPrecision,
- float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+ float rawXCursorPosition, float rawYCursorPosition,
+ int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
@@ -357,6 +358,8 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
mRawYCursorPosition = rawYCursorPosition;
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
mDownTime = downTime;
mPointerProperties.clear();
mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -380,6 +383,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
mRawYCursorPosition = other->mRawYCursorPosition;
+ mDisplayWidth = other->mDisplayWidth;
+ mDisplayHeight = other->mDisplayHeight;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
@@ -426,7 +431,7 @@ const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const
}
float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
- return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
+ return getHistoricalRawAxisValue(axis, pointerIndex, getHistorySize());
}
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
@@ -440,7 +445,32 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+ // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags)
+ static const int ALL_ROTATIONS_MASK = 0x7;
+ uint32_t orientation = (mTransform.getOrientation() & ALL_ROTATIONS_MASK);
+ if (orientation == ui::Transform::ROT_0) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+
+ // For compatibility, convert raw coordinates into "oriented screen space". Once app developers
+ // are educated about getRaw, we can consider removing this.
+ vec2 xy = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue();
+ const float unrotatedX = xy.x;
+ if (orientation == ui::Transform::ROT_90) {
+ xy.x = mDisplayHeight - xy.y;
+ xy.y = unrotatedX;
+ } else if (orientation == ui::Transform::ROT_180) {
+ xy.x = mDisplayWidth - xy.x;
+ xy.y = mDisplayHeight - xy.y;
+ } else if (orientation == ui::Transform::ROT_270) {
+ xy.x = xy.y;
+ xy.y = mDisplayWidth - unrotatedX;
+ }
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return xy[axis];
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
@@ -449,19 +479,10 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
}
- float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
- float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
- vec2 vals = mTransform.transform(rawX, rawY);
-
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return vals.x;
- case AMOTION_EVENT_AXIS_Y:
- return vals.y;
- }
-
- // This should never happen
- return 0;
+ vec2 vals = mTransform.transform(
+ getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue());
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return vals[axis];
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -548,6 +569,24 @@ void MotionEvent::transform(const std::array<float, 9>& matrix) {
}
}
+void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
+ // Determine how the origin is transformed by the matrix so that we
+ // can transform orientation vectors.
+ vec2 origin = transformPoint(matrix, 0, 0);
+
+ // Apply the transformation to all samples.
+ size_t numSamples = mSamplePointerCoords.size();
+ for (size_t i = 0; i < numSamples; i++) {
+ PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+ float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ transformAngle(matrix, orientation, origin.x, origin.y));
+ vec2 xy = transformPoint(matrix, c.getX(), c.getY());
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+ }
+}
+
#ifdef __linux__
static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
float dsdx, dtdx, tx, dtdy, dsdy, ty;
@@ -606,6 +645,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) {
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
mRawYCursorPosition = parcel->readFloat();
+ mDisplayWidth = parcel->readInt32();
+ mDisplayHeight = parcel->readInt32();
mDownTime = parcel->readInt64();
mPointerProperties.clear();
@@ -665,6 +706,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const {
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
parcel->writeFloat(mRawYCursorPosition);
+ parcel->writeInt32(mDisplayWidth);
+ parcel->writeInt32(mDisplayHeight);
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 31027b6476..61d72adb33 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -87,8 +87,10 @@ std::string getInputDeviceConfigurationFilePathByName(
// Search system repository.
std::string path;
- // Treblized input device config files will be located /odm/usr or /vendor/usr.
- const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+ // Treblized input device config files will be located /product/usr, /system_ext/usr,
+ // /odm/usr or /vendor/usr.
+ const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor",
+ getenv("ANDROID_ROOT")};
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 3657fcfdfe..97f4e66e38 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -96,28 +96,42 @@ inline static const char* toString(bool value) {
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
- if (size() == actualSize) {
- switch (header.type) {
- case Type::KEY:
- return true;
- case Type::MOTION:
- return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
- case Type::FINISHED:
- return true;
- case Type::FOCUS:
- return true;
- case Type::CAPTURE:
- return true;
- case Type::DRAG:
- return true;
- case Type::TIMELINE:
- const nsecs_t gpuCompletedTime =
- body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
- const nsecs_t presentTime =
- body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
- return presentTime > gpuCompletedTime;
+ if (size() != actualSize) {
+ ALOGE("Received message of incorrect size %zu (expected %zu)", actualSize, size());
+ return false;
+ }
+
+ switch (header.type) {
+ case Type::KEY:
+ return true;
+ case Type::MOTION: {
+ const bool valid =
+ body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
+ if (!valid) {
+ ALOGE("Received invalid MOTION: pointerCount = %" PRIu32, body.motion.pointerCount);
+ }
+ return valid;
+ }
+ case Type::FINISHED:
+ case Type::FOCUS:
+ case Type::CAPTURE:
+ case Type::DRAG:
+ return true;
+ case Type::TIMELINE: {
+ const nsecs_t gpuCompletedTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ const bool valid = presentTime > gpuCompletedTime;
+ if (!valid) {
+ ALOGE("Received invalid TIMELINE: gpuCompletedTime = %" PRId64
+ " presentTime = %" PRId64,
+ gpuCompletedTime, presentTime);
+ }
+ return valid;
}
}
+ ALOGE("Invalid message type: %" PRIu32, header.type);
return false;
}
@@ -228,6 +242,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const {
msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
// float yCursorPosition
msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
+ // int32_t displayW
+ msg->body.motion.displayWidth = body.motion.displayWidth;
+ // int32_t displayH
+ msg->body.motion.displayHeight = body.motion.displayHeight;
// uint32_t pointerCount
msg->body.motion.pointerCount = body.motion.pointerCount;
//struct Pointer pointers[MAX_POINTERS]
@@ -400,9 +418,7 @@ status_t InputChannel::receiveMessage(InputMessage* msg) {
}
if (!msg->isValid(nRead)) {
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ received invalid message", mName.c_str());
-#endif
+ ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead);
return BAD_VALUE;
}
@@ -517,9 +533,9 @@ status_t InputPublisher::publishMotionEvent(
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords) {
+ float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth,
+ int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
"publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -577,6 +593,8 @@ status_t InputPublisher::publishMotionEvent(
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
msg.body.motion.yCursorPosition = yCursorPosition;
+ msg.body.motion.displayWidth = displayWidth;
+ msg.body.motion.displayHeight = displayHeight;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
@@ -1354,6 +1372,7 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage
msg->body.motion.buttonState, msg->body.motion.classification, transform,
msg->body.motion.xPrecision, msg->body.motion.yPrecision,
msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+ msg->body.motion.displayWidth, msg->body.motion.displayHeight,
msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
pointerProperties, pointerCoords);
}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index fe8a5677d9..767878b7c3 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -49,8 +49,8 @@ cc_test {
cc_library_static {
name: "StructLayout_test",
srcs: ["StructLayout_test.cpp"],
+ compile_multilib: "both",
cflags: [
- "-O0",
"-Wall",
"-Werror",
"-Wextra",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 601d8dabf7..32b72ba616 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -271,6 +271,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
pointerCoords);
@@ -592,6 +593,7 @@ TEST_F(MotionEventTest, Transform) {
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
pointerCoords);
float originalRawX = 0 + 3;
@@ -634,6 +636,97 @@ TEST_F(MotionEventTest, Transform) {
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
+MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) {
+ std::vector<PointerProperties> pointerProperties;
+ pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.emplace_back().clear();
+ pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ MotionEvent event;
+ event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
+ /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ /* buttonState */ 0, MotionClassification::NONE, transform,
+ /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
+ /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
+ pointerProperties.data(), pointerCoords.data());
+ return event;
+}
+
+TEST_F(MotionEventTest, ApplyTransform) {
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform identity;
+ ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(700, event.getRawX(0));
+ ASSERT_EQ(60, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+
+ MotionEvent changedEvent = createTouchDownEvent(60, 100, identity);
+ const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
+ xform[0][1], xform[1][1], xform[2][1],
+ xform[0][2], xform[1][2], xform[2][2]};
+ changedEvent.applyTransform(rowMajor);
+
+ // transformContent effectively rotates the raw coordinates, so those should now include
+ // both rotation AND offset
+ ASSERT_EQ(720, changedEvent.getRawX(0));
+ ASSERT_EQ(100, changedEvent.getRawY(0));
+
+ // The transformed output should be the same then
+ ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001);
+ ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001);
+}
+
+TEST_F(MotionEventTest, RawCompatTransform) {
+ {
+ // Make sure raw is raw regardless of transform translation.
+ ui::Transform xform;
+ xform.set(20, 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(60, event.getRawX(0));
+ ASSERT_EQ(100, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+ }
+
+ // Next check that getRaw contains rotation (for compatibility) but otherwise is still
+ // "Screen-space". The following tests check all 3 rotations.
+ {
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(700, event.getRawX(0));
+ ASSERT_EQ(60, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+ }
+
+ {
+ // Same as above, but check rotate-180.
+ ui::Transform xform(ui::Transform::ROT_180, 400, 800);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(340, event.getRawX(0));
+ ASSERT_EQ(700, event.getRawY(0));
+ }
+
+ {
+ // Same as above, but check rotate-270.
+ ui::Transform xform(ui::Transform::ROT_270, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(100, event.getRawX(0));
+ ASSERT_EQ(340, event.getRawY(0));
+ }
+}
+
TEST_F(MotionEventTest, Initialize_SetsClassification) {
std::array<MotionClassification, 3> classifications = {
MotionClassification::NONE,
@@ -657,7 +750,8 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) {
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
@@ -678,8 +772,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) {
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
- 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index db36b353b1..2e85d24712 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -166,6 +166,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
+ constexpr int32_t displayWidth = 1000;
+ constexpr int32_t displayHeight = 2000;
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
@@ -194,8 +196,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ xCursorPosition, yCursorPosition, displayWidth,
+ displayHeight, downTime, eventTime, pointerCount,
+ pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -236,6 +239,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
+ EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -475,7 +480,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer
status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
0, 0, 0, MotionClassification::NONE, identityTransform,
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
@@ -491,7 +496,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
0, 0, 0, MotionClassification::NONE, identityTransform,
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
@@ -512,7 +517,7 @@ TEST_F(InputPublisherAndConsumerTest,
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
0, 0, 0, MotionClassification::NONE, identityTransform,
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 585779e472..5861d55156 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -74,9 +74,11 @@ void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
- CHECK_OFFSET(InputMessage::Body::Motion, empty3, 140);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 140);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, empty3, 148);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index d049d05ac5..aefc2ec47d 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -183,7 +183,8 @@ static std::vector<MotionEvent> createMotionEventStream(
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 36f87b8a6a..f79098c63c 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -46,8 +46,10 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) {
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
- 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ 100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
return event;
}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 8675439938..928600999c 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -60,7 +60,13 @@ ndk_library {
cc_library {
name: "libnativewindow",
- llndk_stubs: "libnativewindow.llndk",
+ llndk: {
+ symbol_file: "libnativewindow.map.txt",
+ unversioned: true,
+ override_export_include_dirs: [
+ "include"
+ ],
+ },
export_include_dirs: [
"include",
"include-private",
@@ -115,11 +121,4 @@ cc_library {
},
}
-llndk_library {
- name: "libnativewindow.llndk",
- symbol_file: "libnativewindow.map.txt",
- unversioned: true,
- export_include_dirs: ["include"],
-}
-
subdirs = ["tests"]
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 61b3f94aab..f0e1c4d749 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -300,8 +300,13 @@ enum ANativeWindow_ChangeFrameRateStrategy {
* this ANativeWindow is consumed by something other than the system compositor,
* e.g. a media codec, this call has no effect.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* Available since API level 31.
*
+ * \param window pointer to an ANativeWindow object.
+ *
* \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
@@ -309,15 +314,16 @@ enum ANativeWindow_ChangeFrameRateStrategy {
* 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 window pointer to an ANativeWindow object.
- *
* \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.
+ * This parameter is ignored when frameRate is 0.
*
- * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * window 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. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * This parameter is ignored when frameRate is 0.
*
* \return 0 for success, -EINVAL if the window, frame rate, or compatibility
* value are invalid.
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index a5712b319f..3243a6b5f4 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -7,17 +7,43 @@ package {
default_applicable_licenses: ["frameworks_native_license"],
}
+aidl_interface {
+ name: "framework-permission-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ backend: {
+ ndk: {
+ enabled: false
+ }
+ },
+ srcs: [
+ "aidl/android/content/AttributionSourceState.aidl",
+ "aidl/android/permission/IPermissionChecker.aidl",
+ ],
+}
+
cc_library_shared {
name: "libpermission",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
srcs: [
"AppOpsManager.cpp",
"IAppOpsCallback.cpp",
"IAppOpsService.cpp",
+ "android/permission/PermissionChecker.cpp",
],
export_include_dirs: ["include"],
shared_libs: [
+ "libutils",
"libbinder",
+ "libcutils",
"liblog",
- "libutils",
],
+ static_libs: [
+ "framework-permission-aidl-cpp",
+ ],
+ export_static_lib_headers: ["framework-permission-aidl-cpp"],
}
diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl
new file mode 100644
index 0000000000..b6e54bf153
--- /dev/null
+++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Payload for the {@link AttributionSource} class needed to interoperate
+ * with different languages.
+ *
+ * {@hide}
+ */
+parcelable AttributionSourceState {
+ /** The UID that is accessing the permission protected data. */
+ int uid;
+ /** The package that is accessing the permission protected data. */
+ @nullable @utf8InCpp String packageName;
+ /** The attribution tag of the app accessing the permission protected data. */
+ @nullable @utf8InCpp String attributionTag;
+ /** Unique token for that source. */
+ @nullable IBinder token;
+ /** Permissions that should be considered revoked regardless if granted. */
+ @nullable @utf8InCpp String[] renouncedPermissions;
+ /** The next app to receive the permission protected data. */
+ // TODO: We use an array as a workaround - the C++ backend doesn't
+ // support referring to the parcelable as it expects ctor/dtor
+ @nullable AttributionSourceState[] next;
+}
diff --git a/libs/permission/aidl/android/permission/IPermissionChecker.aidl b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
new file mode 100644
index 0000000000..1f0e32d248
--- /dev/null
+++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.content.AttributionSourceState;
+
+/**
+ * Interface to communicate directly with the permission checker service.
+ */
+interface IPermissionChecker {
+ const int PERMISSION_GRANTED = 0;
+ const int PERMISSION_SOFT_DENIED = 1;
+ const int PERMISSION_HARD_DENIED = 2;
+
+ int checkPermission(String permission, in AttributionSourceState attributionSource,
+ @nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+ boolean fromDatasource);
+
+ void finishDataDelivery(String op, in AttributionSourceState attributionSource);
+
+ int checkOp(int op, in AttributionSourceState attributionSource,
+ String message, boolean forDataDelivery, boolean startDataDelivery);
+}
diff --git a/libs/permission/android/permission/PermissionChecker.cpp b/libs/permission/android/permission/PermissionChecker.cpp
new file mode 100644
index 0000000000..a8083ee410
--- /dev/null
+++ b/libs/permission/android/permission/PermissionChecker.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 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 <mutex>
+#include <include/android/permission/PermissionChecker.h>
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/SystemClock.h>
+
+#include <sys/types.h>
+#include <private/android_filesystem_config.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "PermissionChecker"
+
+namespace android {
+
+using android::content::AttributionSourceState;
+
+PermissionChecker::PermissionChecker()
+{
+}
+
+sp<IPermissionChecker> PermissionChecker::getService()
+{
+ static String16 permission_checker("permission_checker");
+
+ std::lock_guard<Mutex> scoped_lock(mLock);
+ int64_t startTime = 0;
+ sp<IPermissionChecker> service = mService;
+ while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(permission_checker);
+ if (binder == nullptr) {
+ // Wait for the permission checker service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGW("Waiting for permission checker service");
+ } else if ((uptimeMillis() - startTime) > 10000) {
+ ALOGE("Waiting too long for permission checker service, giving up");
+ service = nullptr;
+ break;
+ }
+ sleep(1);
+ } else {
+ mService = interface_cast<IPermissionChecker>(binder);
+ }
+ }
+ return mService;
+}
+
+PermissionChecker::PermissionResult
+ PermissionChecker::checkPermissionForDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message)
+{
+ return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
+ /*forDataDelivery*/ true, /*startDataDelivery*/ false,/*fromDatasource*/ true));
+}
+
+PermissionChecker::PermissionResult
+ PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message)
+{
+ return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
+ /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true));
+}
+
+void PermissionChecker::finishDataDelivery(const String16& op,
+ AttributionSourceState& attributionSource)
+{
+ sp<IPermissionChecker> service = getService();
+ if (service != nullptr) {
+ binder::Status status = service->finishDataDelivery(op, attributionSource);
+ if (!status.isOk()) {
+ ALOGE("finishDataDelivery failed: %s", status.exceptionMessage().c_str());
+ }
+ }
+}
+
+int32_t PermissionChecker::checkPermission(const String16& permission,
+ AttributionSourceState& attributionSource, const String16& message,
+ bool forDataDelivery, bool startDataDelivery, bool fromDatasource)
+{
+ sp<IPermissionChecker> service = getService();
+ if (service != nullptr) {
+ int32_t result;
+ binder::Status status = service->checkPermission(permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource, &result);
+ if (status.isOk()) {
+ return result;
+ }
+ ALOGE("checkPermission failed: %s", status.exceptionMessage().c_str());
+ }
+ return PERMISSION_DENIED;
+}
+
+} // namespace android
diff --git a/libs/permission/include/android/permission/PermissionChecker.h b/libs/permission/include/android/permission/PermissionChecker.h
new file mode 100644
index 0000000000..20ab51fc8a
--- /dev/null
+++ b/libs/permission/include/android/permission/PermissionChecker.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 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 <android/content/AttributionSourceState.h>
+#include <android/permission/IPermissionChecker.h>
+
+#include <utils/threads.h>
+
+#include <optional>
+
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+using android::content::AttributionSourceState;
+using android::permission::IPermissionChecker;
+
+class PermissionChecker
+{
+public:
+
+ enum PermissionResult {
+
+ /**
+ * The permission is granted.
+ */
+ PERMISSION_GRANTED = IPermissionChecker::PERMISSION_GRANTED,
+
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ *
+ * Returned when:
+ * - the runtime permission is granted, but the corresponding app op is denied
+ * for runtime permissions.
+ * - the app ops is ignored for app op permissions.
+ *
+ */
+ PERMISSION_SOFT_DENIED = IPermissionChecker::PERMISSION_SOFT_DENIED,
+
+ /**
+ * The permission is denied.
+ *
+ * Returned when:
+ * - the permission is denied for non app op permissions.
+ * - the app op is denied or app op is AppOpsManager#MODE_DEFAULT and permission is denied.
+ */
+ PERMISSION_HARD_DENIED = IPermissionChecker::PERMISSION_HARD_DENIED
+ };
+
+ PermissionChecker();
+
+ /**
+ * Checks whether a given data access chain described by the given attribution source
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. Call this method if you are the datasource which would not blame you for
+ * access to the data since you are the data. Note that the attribution source chain
+ *
+ * NOTE: The attribution source should be for yourself with its next attribution
+ * source being the app that would receive the data from you.
+ *
+ * NOTE: Use this method only for permission checks at the point where you will deliver
+ * the permission protected data to clients.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message A message describing the reason the permission was checked.
+ * @return The permission check result which is either PERMISSION_GRANTED,
+ * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+ */
+ PermissionChecker::PermissionResult checkPermissionForDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message);
+
+ /**
+ * Checks whether a given data access chain described by the given attribution source
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. The app ops are also marked as started. This is useful for long running
+ * permissions like camera and microphone.
+ *
+ * NOTE: The attribution source should be for yourself with its next attribution
+ * source being the app that would receive the data from you.
+ *
+ * NOTE: Use this method only for permission checks at the point where you will deliver
+ * the permission protected data to clients.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message A message describing the reason the permission was checked.
+ * @return The permission check result which is either PERMISSION_GRANTED,
+ * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+ */
+ PermissionResult checkPermissionForStartDataDeliveryFromDatasource(
+ const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message);
+
+ /**
+ * Finishes an ongoing op for data access chain described by the given
+ * attribution source.
+ *
+ * @param op The op to finish.
+ * @param attributionSource The attribution chain for which to finish data delivery.
+ */
+ void finishDataDelivery(const String16& op, AttributionSourceState& attributionSource);
+
+private:
+ Mutex mLock;
+ sp<IPermissionChecker> mService;
+ sp<IPermissionChecker> getService();
+
+ int32_t checkPermission(const String16& permission, AttributionSourceState& attributionSource,
+ const String16& message, bool forDataDelivery, bool startDataDelivery,
+ bool fromDatasource);
+};
+
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index c048cbed37..e3d705fa35 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -146,7 +146,8 @@ public:
OP_UWB_RANGING = 112,
OP_ACTIVITY_RECOGNITION_SOURCE = 113,
OP_BLUETOOTH_ADVERTISE = 114,
- _NUM_OP = 115
+ OP_RECORD_INCOMING_PHONE_AUDIO = 115,
+ _NUM_OP = 116
};
AppOpsManager();
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index f395ab43d8..570c7bc08d 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -95,6 +95,7 @@ filegroup {
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/StretchShaderFactory.cpp"
],
}
@@ -117,7 +118,7 @@ cc_library_static {
include_dirs: [
"external/skia/src/gpu",
],
- whole_static_libs: ["libskia"],
+ whole_static_libs: ["libskia_renderengine"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d87315fdc2..940003750e 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -249,7 +249,7 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre
// initialize EGL for the default display
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, nullptr, nullptr)) {
- LOG_ALWAYS_FATAL("failed to initialize EGL");
+ LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError());
}
const auto eglVersion = eglQueryString(display, EGL_VERSION);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index c54c5ba047..6e983524ce 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -156,6 +156,10 @@ struct LayerSettings {
std::vector<BlurRegion> blurRegions;
+ // Transform matrix used to convert the blurRegions geometry into the same
+ // coordinate space as LayerSettings.geometry
+ mat4 blurRegionTransform = mat4();
+
StretchEffect stretchEffect;
// Name associated with the layer for debugging purposes.
@@ -218,7 +222,9 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
- lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+ lhs.blurRegionTransform == rhs.blurRegionTransform &&
+ lhs.stretchEffect == rhs.stretchEffect;
}
// Defining PrintTo helps with Google Tests.
@@ -268,6 +274,21 @@ static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
*os << "\n}";
}
+static inline void PrintTo(const StretchEffect& effect, ::std::ostream* os) {
+ *os << "StretchEffect {";
+ *os << "\n .width = " << effect.width;
+ *os << "\n .height = " << effect.height;
+ *os << "\n .vectorX = " << effect.vectorX;
+ *os << "\n .vectorY = " << effect.vectorY;
+ *os << "\n .maxAmountX = " << effect.maxAmountX;
+ *os << "\n .maxAmountY = " << effect.maxAmountY;
+ *os << "\n .mappedLeft = " << effect.mappedChildBounds.left;
+ *os << "\n .mappedTop = " << effect.mappedChildBounds.top;
+ *os << "\n .mappedRight = " << effect.mappedChildBounds.right;
+ *os << "\n .mappedBottom = " << effect.mappedChildBounds.bottom;
+ *os << "\n}";
+}
+
static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
*os << "LayerSettings {";
*os << "\n .geometry = ";
@@ -286,6 +307,8 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
}
*os << "\n .shadow = ";
PrintTo(settings.shadow, os);
+ *os << "\n .stretchEffect = ";
+ PrintTo(settings.stretchEffect, os);
*os << "\n}";
}
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 1c2b2fc3ca..48d76cc475 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -37,12 +37,15 @@ const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f,
0.f, 0.7f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
67.3f, 52.2f, 0.f, 1.f);
+const auto kScaleYOnly = mat4(1.f, 0.f, 0.f, 0.f,
+ 0.f, 0.7f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);
// clang-format on
-// When choosing dataspaces below, whether the match the destination or not determined whether
-// a color correction effect is added to the shader. There may be other additional shader details
-// for particular color spaces.
-// TODO(b/184842383) figure out which color related shaders are necessary
+// When setting layer.sourceDataspace, whether it matches the destination or not determines whether
+// a color correction effect is added to the shader.
constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
} // namespace
static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -115,20 +118,24 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting
// Test both drawRect and drawRRect
auto layers = std::vector<const LayerSettings*>{&layer};
- for (bool identity : {true, false}) {
- layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
- // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
- // due to animating corner radius.
- // For the non-idenity matrix, only the large corner radius will create a new shader.
- for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
- // roundedCornersCrop is always set, but it is this radius that triggers the behavior
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
- for (bool isOpaque : {true, false}) {
- layer.source.buffer.isOpaque = isOpaque;
- for (auto alpha : {half(.23999f), half(1.0f)}) {
- layer.alpha = alpha;
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
+ layer.sourceDataspace = dataspace;
+ for (bool identity : {true, false}) {
+ layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
+ // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
+ // due to animating corner radius.
+ // For the non-idenity matrix, only the large corner radius will create a new shader.
+ for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
+ // roundedCornersCrop is always set, but it is this radius that triggers the
+ // behavior
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ for (bool isOpaque : {true, false}) {
+ layer.source.buffer.isOpaque = isOpaque;
+ for (auto alpha : {half(.23999f), half(1.0f)}) {
+ layer.alpha = alpha;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
}
}
}
@@ -182,6 +189,37 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings
}
}
+static void drawTextureScaleLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ .positionTransform = kScaleAndTranslate,
+ .roundedCornersRadius = 300,
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .maxMasteringLuminance = 1000.f,
+ .maxContentLuminance = 1000.f,
+ .textureTransform = kScaleYOnly,
+ }},
+ .sourceDataspace = kOtherDataSpace,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (float alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha,
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+}
+
//
// The collection of shaders cached here were found by using perfetto to record shader compiles
// during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -200,60 +238,70 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
if (previousCount) {
ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
}
- const nsecs_t timeBefore = systemTime();
- // The dimensions should not matter, so long as we draw inside them.
- const Rect displayRect(0, 0, 1080, 2340);
- DisplaySettings display{
- .physicalDisplay = displayRect,
- .clip = displayRect,
- .maxLuminance = 500,
- .outputDataspace = kDestDataSpace,
- };
- const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ // The loop is beneficial for debugging and should otherwise be optimized out by the compiler.
+ // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer
+ // does not impact the shader compilation counts by triggering different behaviors in RE/Skia.
+ for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) {
+ const nsecs_t timeBefore = systemTime();
+ // The dimensions should not matter, so long as we draw inside them.
+ const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight);
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = kDestDataSpace,
+ };
- sp<GraphicBuffer> dstBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usage, "primeShaderCache_dst");
+ const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
- ExternalTexture::Usage::WRITEABLE);
- // This buffer will be the source for the call to drawImageLayers. Draw
- // something to it as a placeholder for what an app draws. We should draw
- // something, but the details are not important. Make use of the shadow layer drawing step
- // to populate it.
- sp<GraphicBuffer> srcBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usage, "drawImageLayer_src");
+ sp<GraphicBuffer> dstBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usage, "primeShaderCache_dst");
- const auto srcTexture =
- std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
- ExternalTexture::Usage::READABLE |
- ExternalTexture::Usage::WRITEABLE);
+ const auto dstTexture =
+ std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
+ ExternalTexture::Usage::WRITEABLE);
+ // This buffer will be the source for the call to drawImageLayers. Draw
+ // something to it as a placeholder for what an app draws. We should draw
+ // something, but the details are not important. Make use of the shadow layer drawing step
+ // to populate it.
+ sp<GraphicBuffer> srcBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usage, "drawImageLayer_src");
- drawSolidLayers(renderengine, display, dstTexture);
- drawShadowLayers(renderengine, display, srcTexture);
- drawBlurLayers(renderengine, display, dstTexture);
- // The majority of shaders are related to sampling images.
- drawImageLayers(renderengine, display, dstTexture, srcTexture);
+ const auto srcTexture =
+ std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE |
+ ExternalTexture::Usage::WRITEABLE);
- // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
- const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
+ drawSolidLayers(renderengine, display, dstTexture);
+ drawShadowLayers(renderengine, display, srcTexture);
+ drawBlurLayers(renderengine, display, dstTexture);
+ // The majority of shaders are related to sampling images.
+ drawImageLayers(renderengine, display, dstTexture, srcTexture);
- sp<GraphicBuffer> externalBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usageExternal, "primeShaderCache_external");
- const auto externalTexture =
- std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
- ExternalTexture::Usage::READABLE);
- // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
- // between 6 and 8 will occur in real uses.
- drawImageLayers(renderengine, display, dstTexture, externalTexture);
+ // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
- const nsecs_t timeAfter = systemTime();
- const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- const int shadersCompiled = renderengine->reportShadersCompiled();
- ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+ sp<GraphicBuffer> externalBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usageExternal, "primeShaderCache_external");
+ const auto externalTexture =
+ std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE);
+ // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
+ // between 6 and 8 will occur in real uses.
+ drawImageLayers(renderengine, display, dstTexture, externalTexture);
+
+ // Draw layers for b/185569240.
+ drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ const int shadersCompiled = renderengine->reportShadersCompiled();
+ ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+ }
}
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 377b6f89ad..c5ad64ea22 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -56,6 +56,7 @@
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaMemoryReporter.h"
+#include "skia/filters/StretchShaderFactory.h"
#include "system/graphics-base-v1.0.h"
namespace {
@@ -365,6 +366,10 @@ bool SkiaGLRenderEngine::supportsProtectedContent() const {
return mProtectedEGLContext != EGL_NO_CONTEXT;
}
+GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
+ return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+}
+
bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
if (useProtectedContext == mInProtectedContext) {
return true;
@@ -372,6 +377,12 @@ bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
if (useProtectedContext && !supportsProtectedContent()) {
return false;
}
+
+ // release any scratch resources before switching into a new mode
+ if (getActiveGrContext()) {
+ getActiveGrContext()->purgeUnlockedResources(true);
+ }
+
const EGLSurface surface =
useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
@@ -379,6 +390,11 @@ bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
if (success) {
mInProtectedContext = useProtectedContext;
+ // given that we are sharing the same thread between two GrContexts we need to
+ // make sure that the thread state is reset when switching between the two.
+ if (getActiveGrContext()) {
+ getActiveGrContext()->resetContext();
+ }
}
return success;
}
@@ -489,18 +505,18 @@ void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffe
if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
return;
}
- ATRACE_CALL();
-
- // We need to switch the currently bound context if the buffer is protected but the current
- // context is not. The current state must then be restored after the buffer is cached.
- const bool protectedContextState = mInProtectedContext;
- if (!useProtectedContext(protectedContextState ||
- (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
- ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
+ // we currently don't attempt to map a buffer if the buffer contains protected content
+ // because GPU resources for protected buffers is much more limited.
+ const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+ if (isProtectedBuffer) {
return;
}
+ ATRACE_CALL();
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ // If we were to support caching protected buffers then we will need to switch the currently
+ // bound context if we are not already using the protected context (and subsequently switch
+ // back after the buffer is cached).
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::lock_guard<std::mutex> lock(mRenderingMutex);
@@ -508,13 +524,11 @@ void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffe
if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
isRenderable);
cache.insert({buffer->getId(), imageTextureRef});
}
- // restore the original state of the protected context if necessary
- useProtectedContext(protectedContextState);
}
void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
@@ -541,14 +555,19 @@ void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buf
}
}
-sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings* layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- if (layer->stretchEffect.hasEffect()) {
- // TODO: Implement
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
+ sk_sp<SkShader> shader,
+ const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
+ bool requiresLinearEffect) {
+ const auto stretchEffect = layer->stretchEffect;
+ if (stretchEffect.hasEffect()) {
+ const auto targetBuffer = layer->source.buffer.buffer;
+ const auto graphicsBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+ if (graphicsBuffer && shader) {
+ shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
+ }
}
+
if (requiresLinearEffect) {
const ui::Dataspace inputDataspace =
mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
@@ -634,6 +653,44 @@ private:
int mSaveCount;
};
+void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect,
+ SkCanvas* canvas, const SkPaint& paint) {
+ float top = bounds.top();
+ float left = bounds.left();
+ float bottom = bounds.bottom();
+ float right = bounds.right();
+ // Adjust the drawing bounds based on the stretch itself.
+ float stretchOffsetX =
+ round(bounds.width() * stretchEffect.getStretchWidthMultiplier());
+ float stretchOffsetY =
+ round(bounds.height() * stretchEffect.getStretchHeightMultiplier());
+ if (stretchEffect.vectorY < 0.f) {
+ top -= stretchOffsetY;
+ } else if (stretchEffect.vectorY > 0.f){
+ bottom += stretchOffsetY;
+ }
+
+ if (stretchEffect.vectorX < 0.f) {
+ left -= stretchOffsetX;
+ } else if (stretchEffect.vectorX > 0.f) {
+ right += stretchOffsetX;
+ }
+
+ auto stretchBounds = SkRect::MakeLTRB(left, top, right, bottom);
+ canvas->drawRect(stretchBounds, paint);
+}
+
+static SkRRect getBlurRRect(const BlurRegion& region) {
+ const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
+ const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
+ SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
+ SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
+ SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ return roundedRect;
+}
+
status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
@@ -662,7 +719,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
validateOutputBufferUsage(buffer->getBuffer());
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
@@ -670,7 +727,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
surfaceTextureRef = it->second;
} else {
surfaceTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->getBuffer()
->toAHardwareBuffer(),
true);
@@ -678,8 +735,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const ui::Dataspace dstDataspace =
mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
- sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext.get());
+ sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
@@ -809,7 +865,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// Layers have a local transform that should be applied to them
canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
- const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto [bounds, roundRectClip] = getBoundsAndClip(layer);
if (mBlurFilter && layerHasBlur(layer)) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
@@ -819,30 +875,40 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
blurInput = activeSurface->makeImageSnapshot();
}
// rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+ const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+
+ // if the clip needs to be applied then apply it now and make sure
+ // it is restored before we attempt to draw any shadows.
+ SkAutoCanvasRestore acr(canvas, true);
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
// TODO(b/182216890): Filter out empty layers earlier
if (blurRect.width() > 0 && blurRect.height() > 0) {
if (layer->backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
auto blurredImage =
- mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
- blurInput, blurRect);
+ mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
+ blurRect);
cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
- mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect,
- blurredImage, blurInput);
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+ blurRect, blurredImage, blurInput);
}
+
+ canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
for (auto region : layer->blurRegions) {
if (cachedBlurs[region.blurRadius] == nullptr) {
ATRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
- mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+ mBlurFilter->generate(grContext, region.blurRadius, blurInput,
blurRect);
}
- mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+ mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+ region.alpha, blurRect,
cachedBlurs[region.blurRadius], blurInput);
}
}
@@ -855,7 +921,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
if (layer->shadow.length > 0) {
const auto rect = layer->geometry.roundedCornersRadius > 0
? getSkRect(layer->geometry.roundedCornersCrop)
- : bounds;
+ : bounds.rect();
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
@@ -895,7 +961,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// we didn't find anything in the cache then we intentionally did not cache this
// buffer's resources.
imageTextureRef = std::make_shared<
- AutoBackendTexture::LocalRef>(grContext.get(),
+ AutoBackendTexture::LocalRef>(grContext,
item.buffer->getBuffer()->toAHardwareBuffer(),
false);
}
@@ -904,7 +970,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
imageTextureRef->makeImage(layerDataspace,
item.usePremultipliedAlpha ? kPremul_SkAlphaType
: kUnpremul_SkAlphaType,
- grContext.get());
+ grContext);
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -920,7 +986,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// The shader does not respect the translation, so we add it to the texture
// transform for the SkImage. This will make sure that the correct layer contents
// are drawn in the correct part of the screen.
- matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top);
+ matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
sk_sp<SkShader> shader;
@@ -982,11 +1048,25 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
paint.setColorFilter(displayColorTransform);
- if (layer->geometry.roundedCornersRadius > 0) {
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
+
+ if (!bounds.isRect()) {
paint.setAntiAlias(true);
- canvas->drawRRect(getRoundedRect(layer), paint);
+ canvas->drawRRect(bounds, paint);
} else {
- canvas->drawRect(bounds, paint);
+ auto& stretchEffect = layer->stretchEffect;
+ // TODO (njawad) temporarily disable manipulation of geometry
+ // the layer bounds will be updated in HWUI instead of RenderEngine
+ // in a subsequent CL
+ // Keep the method call in a dead code path to make -Werror happy
+ // with unused methods
+ if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) {
+ drawStretch(bounds.rect(), stretchEffect, canvas, paint);
+ } else {
+ canvas->drawRect(bounds.rect(), paint);
+ }
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
@@ -1034,25 +1114,76 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
-inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(
+ const LayerSettings* layer) {
+ const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto crop = getSkRect(layer->geometry.roundedCornersCrop);
const auto cornerRadius = layer->geometry.roundedCornersRadius;
- 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)};
+ SkRRect clip;
+ if (cornerRadius > 0) {
+ // it the crop and the bounds are equivalent then we don't need a clip
+ if (bounds == crop) {
+ return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ }
+
+ // This makes an effort to speed up common, simple bounds + clip combinations by
+ // converting them to a single RRect draw. It is possible there are other cases
+ // that can be converted.
+ if (crop.contains(bounds)) {
+ bool intersectionIsRoundRect = true;
+ // check each cropped corner to ensure that it exactly matches the crop or is full
+ SkVector radii[4];
+
+ const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+
+ // compute the UpperLeft corner radius
+ if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) {
+ radii[0].set(cornerRadius, cornerRadius);
+ } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) {
+ radii[0].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the UpperRight corner radius
+ if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) {
+ radii[1].set(cornerRadius, cornerRadius);
+ } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) {
+ radii[1].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomRight corner radius
+ if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) {
+ radii[2].set(cornerRadius, cornerRadius);
+ } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) {
+ radii[2].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomLeft corner radius
+ if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) {
+ radii[3].set(cornerRadius, cornerRadius);
+ } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.fBottom) {
+ radii[3].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+
+ if (intersectionIsRoundRect) {
+ SkRRect intersectionBounds;
+ intersectionBounds.setRectRadii(bounds, radii);
+ return {intersectionBounds, clip};
+ }
+ }
+
+ // we didn't it any of our fast paths so set the clip to the cropRect
+ clip.setRectXY(crop, cornerRadius, cornerRadius);
+ }
+
+ // if we hit this point then we either don't have rounded corners or we are going to rely
+ // on the clip to round the corners for us
+ return {SkRRect::MakeRect(bounds), clip};
}
inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
@@ -1224,13 +1355,11 @@ void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
// start by resizing the current context
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// if it is possible to switch contexts then we will resize the other context
if (useProtectedContext(!mInProtectedContext)) {
- grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// reset back to the initial context that was active when this method was called
useProtectedContext(!mInProtectedContext);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e71c560a1f..d9caf35ad3 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -39,6 +39,7 @@
#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
#include "filters/LinearEffect.h"
+#include "filters/StretchShaderFactory.h"
namespace android {
namespace renderengine {
@@ -87,12 +88,12 @@ private:
int hwcFormat, Protection protection);
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 std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer);
inline bool layerHasBlur(const LayerSettings* layer);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkPoint3 getSkPoint3(const vec3& vector);
+ inline GrDirectContext* getActiveGrContext() const;
base::unique_fd flush();
bool waitFence(base::unique_fd fenceFd);
@@ -101,7 +102,8 @@ private:
const ShadowSettings& shadowSettings);
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
- sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
+ sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
+ const LayerSettings* layer,
const DisplaySettings& display,
bool undoPremultipliedAlpha,
bool requiresLinearEffect);
@@ -116,14 +118,20 @@ private:
const PixelFormat mDefaultPixelFormat;
const bool mUseColorManagement;
+ // Identifier used or various mappings of layers to various
+ // textures or shaders
+ using LayerId = uint64_t;
+
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
- std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
+ std::unordered_map<LayerId, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
// Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
- std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
+ std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>>
mProtectedTextureCache GUARDED_BY(mRenderingMutex);
std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+
+ StretchShaderFactory mStretchShaderFactory;
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh
index bc406d97a8..25c8cefd7d 100755
--- a/libs/renderengine/skia/debug/record.sh
+++ b/libs/renderengine/skia/debug/record.sh
@@ -31,7 +31,7 @@ adb shell setprop debug.renderengine.capture_skia_ms $1
# give the device time to both record, and starting writing the file.
# Total time needed to write the file depends on how much data was recorded.
# the loop at the end waits for this.
-sleep $(($1 / 1000 + 2));
+sleep $(($1 / 1000 + 4));
# There is no guarantee that at least one frame passed through renderengine during that time
# but as far as I know it always at least writes a 0-byte file with a new name, unless it crashes
@@ -54,7 +54,7 @@ adb_filesize() {
mskp_size=$(adb_filesize "/data/user/$name")
if [[ $mskp_size = "0" ]]; then
- echo "Empty file, probably no RenderEngine activity during recording period."
+ echo "File opened, but remains empty after recording period + wait. Either there was no RenderEngine activity during recording period, or recording process is still working. Check /data/user/$name manually later."
exit 1
fi
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 6dd416101d..4ad6e94edf 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -53,7 +53,7 @@ BlurFilter::BlurFilter() {
}
)");
- auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
if (!blurEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
}
@@ -65,11 +65,11 @@ BlurFilter::BlurFilter() {
uniform float mixFactor;
half4 main(float2 xy) {
- return half4(mix(sample(originalInput), sample(blurredInput), mixFactor));
+ return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
}
)");
- auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString);
+ auto [mixEffect, mixError] = SkRuntimeEffect::MakeForShader(mixString);
if (!mixEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
}
@@ -86,8 +86,8 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t
float radiusByPasses = tmpRadius / (float)numberOfPasses;
// create blur surface with the bit depth and colorspace of the original surface
- SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
- blurRect.height() * kInputScale);
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
const float stepX = radiusByPasses;
const float stepY = radiusByPasses;
@@ -105,7 +105,7 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+ SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
@@ -116,7 +116,7 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t
tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+ SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
}
@@ -139,23 +139,21 @@ static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRec
return matrix;
}
-void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
sk_sp<SkImage> input) {
ATRACE_CALL();
SkPaint paint;
- paint.setAlphaf(effectRegion.alpha);
- if (effectRegion.alpha == 1.0f) {
- paint.setBlendMode(SkBlendMode::kSrc);
- }
+ paint.setAlphaf(blurAlpha);
const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < kMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -168,30 +166,21 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
paint.setShader(blurShader);
}
- // TODO we should AA at least the drawRoundRect which would mean no SRC blending
- // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
- auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
- effectRegion.bottom);
-
- if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
- effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
- const SkVector radii[4] =
- {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
- SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
- SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
- SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
- SkRRect roundedRect;
- roundedRect.setRectRadii(rect, radii);
- canvas->drawRRect(roundedRect, paint);
+ if (effectRegion.isRect()) {
+ if (blurAlpha == 1.0f) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+ canvas->drawRect(effectRegion.rect(), paint);
} else {
- canvas->drawRect(rect, paint);
+ paint.setAntiAlias(true);
+ canvas->drawRRect(effectRegion, paint);
}
}
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 731ba11483..a8e6dd7579 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,7 +20,6 @@
#include <SkImage.h>
#include <SkRuntimeEffect.h>
#include <SkSurface.h>
-#include <ui/BlurRegion.h>
using namespace std;
@@ -52,8 +51,19 @@ public:
sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
- void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
- sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
+ /**
+ * Draw the blurred content (from the generate method) into the canvas.
+ * @param canvas is the destination/output for the blur
+ * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage
+ * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect
+ * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn
+ * @param blurRect bounds of the blurredImage translated into canvas coordinates
+ * @param blurredImage down-sampled blurred content that was produced by the generate() method
+ * @param input original unblurred input that is used to crossfade with the blurredImage
+ */
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
+ const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
private:
sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 8e8e42e75a..0fbd6698d8 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -438,7 +438,7 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
generateOETF(linearEffect.outputDataspace, shaderString);
generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
- auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+ auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
if (!shader) {
LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
}
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
new file mode 100644
index 0000000000..9b62789159
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2021 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 "StretchShaderFactory.h"
+#include <SkImageFilter.h>
+#include <SkRefCnt.h>
+#include <SkRuntimeEffect.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include "log/log.h"
+#include <memory>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString stretchShader = SkString(R"(
+ uniform shader uContentTexture;
+
+ // multiplier to apply to scale effect
+ uniform float uMaxStretchIntensity;
+
+ // Maximum percentage to stretch beyond bounds of target
+ uniform float uStretchAffectedDistX;
+ uniform float uStretchAffectedDistY;
+
+ // Distance stretched as a function of the normalized overscroll times
+ // scale intensity
+ uniform float uDistanceStretchedX;
+ uniform float uDistanceStretchedY;
+ uniform float uInverseDistanceStretchedX;
+ uniform float uInverseDistanceStretchedY;
+ uniform float uDistDiffX;
+
+ // Difference between the peak stretch amount and overscroll amount normalized
+ uniform float uDistDiffY;
+
+ // Horizontal offset represented as a ratio of pixels divided by the target width
+ uniform float uScrollX;
+ // Vertical offset represented as a ratio of pixels divided by the target height
+ uniform float uScrollY;
+
+ // Normalized overscroll amount in the horizontal direction
+ uniform float uOverscrollX;
+
+ // Normalized overscroll amount in the vertical direction
+ uniform float uOverscrollY;
+ uniform float viewportWidth; // target height in pixels
+ uniform float viewportHeight; // target width in pixels
+
+ // uInterpolationStrength is the intensity of the interpolation.
+ // if uInterpolationStrength is 0, then the stretch is constant for all the
+ // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch intensity
+ // is interpolated based on the pixel position in the uStretchAffectedDist area;
+ // The closer we are from the scroll anchor point, the more it stretches,
+ // and the other way around.
+ uniform float uInterpolationStrength;
+
+ float easeInCubic(float t, float d) {
+ float tmp = t * d;
+ return tmp * tmp * tmp;
+ }
+
+ float computeOverscrollStart(
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float interpolationStrength
+ ) {
+ float offsetPos = uStretchAffectedDist - inPos;
+ float posBasedVariation = mix(
+ 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ float stretchIntensity = overscroll * posBasedVariation;
+ return distanceStretched - (offsetPos / (1. + stretchIntensity));
+ }
+
+ float computeOverscrollEnd(
+ float inPos,
+ float overscroll,
+ float reverseStretchDist,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float interpolationStrength
+ ) {
+ float offsetPos = inPos - reverseStretchDist;
+ float posBasedVariation = mix(
+ 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ float stretchIntensity = (-overscroll) * posBasedVariation;
+ return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+ }
+
+ // Prefer usage of return values over out parameters as it enables
+ // SKSL to properly inline method calls and works around potential GPU
+ // driver issues on Wembly. See b/182566543 for details
+ float computeOverscroll(
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float distanceDiff,
+ float interpolationStrength
+ ) {
+ float outPos = inPos;
+ // overscroll is provided via uniform so there is no concern
+ // for potential incoherent branches
+ if (overscroll > 0) {
+ if (inPos <= uStretchAffectedDist) {
+ outPos = computeOverscrollStart(
+ inPos,
+ overscroll,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else if (inPos >= distanceStretched) {
+ outPos = distanceDiff + inPos;
+ }
+ }
+ if (overscroll < 0) {
+ float stretchAffectedDist = 1. - uStretchAffectedDist;
+ if (inPos >= stretchAffectedDist) {
+ outPos = computeOverscrollEnd(
+ inPos,
+ overscroll,
+ stretchAffectedDist,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else if (inPos < stretchAffectedDist) {
+ outPos = -distanceDiff + inPos;
+ }
+ }
+ return outPos;
+ }
+
+ vec4 main(vec2 coord) {
+ // Normalize SKSL pixel coordinate into a unit vector
+ float inU = coord.x / viewportWidth;
+ float inV = coord.y / viewportHeight;
+ float outU;
+ float outV;
+ float stretchIntensity;
+ // Add the normalized scroll position within scrolling list
+ inU += uScrollX;
+ inV += uScrollY;
+ outU = inU;
+ outV = inV;
+ outU = computeOverscroll(
+ inU,
+ uOverscrollX,
+ uStretchAffectedDistX,
+ uInverseDistanceStretchedX,
+ uDistanceStretchedX,
+ uDistDiffX,
+ uInterpolationStrength
+ );
+ outV = computeOverscroll(
+ inV,
+ uOverscrollY,
+ uStretchAffectedDistY,
+ uInverseDistanceStretchedY,
+ uDistanceStretchedY,
+ uDistDiffY,
+ uInterpolationStrength
+ );
+ coord.x = (outU - uScrollX) * viewportWidth;
+ coord.y = (outV - uScrollY) * viewportHeight;
+ return sample(uContentTexture, coord);
+ })");
+
+const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+
+sk_sp<SkShader> StretchShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
+ const StretchEffect& stretchEffect) {
+ if (!stretchEffect.hasEffect()) {
+ return nullptr;
+ }
+
+ float viewportWidth = stretchEffect.width;
+ float viewportHeight = stretchEffect.height;
+ float normOverScrollDistX = stretchEffect.vectorX;
+ float normOverScrollDistY = stretchEffect.vectorY;
+ float distanceStretchedX =
+ StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
+ float distanceStretchedY =
+ StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY));
+ float inverseDistanceStretchedX =
+ 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float inverseDistanceStretchedY =
+ 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float diffX =
+ distanceStretchedX - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float diffY =
+ distanceStretchedY - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ auto& srcBounds = stretchEffect.mappedChildBounds;
+ float normalizedScrollX = srcBounds.left / viewportWidth;
+ float normalizedScrollY = srcBounds.top / viewportHeight;
+
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance =
+ SkRuntimeEffect::MakeForShader(stretchShader);
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+
+ mBuilder->child("uContentTexture") = inputShader;
+ mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
+ mBuilder->uniform("uStretchAffectedDistX").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uStretchAffectedDistY").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
+ mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
+ mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
+ mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1);
+ mBuilder->uniform("uDistDiffX").set(&diffX, 1);
+ mBuilder->uniform("uDistDiffY").set(&diffY, 1);
+ mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
+ mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
+ mBuilder->uniform("uScrollX").set(&normalizedScrollX, 1);
+ mBuilder->uniform("uScrollY").set(&normalizedScrollY, 1);
+ mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
+ mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+
+ return mBuilder->makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android \ No newline at end of file
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.h b/libs/renderengine/skia/filters/StretchShaderFactory.h
new file mode 100644
index 0000000000..9c3ab7cb4f
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 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 <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <ui/StretchEffect.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+class StretchShaderFactory {
+public:
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader,
+ const StretchEffect& stretchEffect);
+
+private:
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android \ No newline at end of file
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 34ef0a4f08..72e32ed9fd 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -263,6 +263,11 @@ public:
}
}
+ void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance);
+ }
+
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
@@ -487,6 +492,9 @@ public:
void fillBufferAndBlurBackground();
template <typename SourceVariant>
+ void fillSmallLayerAndBlurBackground();
+
+ template <typename SourceVariant>
void overlayCorners();
void fillRedBufferTextureTransform();
@@ -966,11 +974,44 @@ void RenderEngineTest::fillBufferAndBlurBackground() {
if (mRE->supportsBackgroundBlur()) {
// blurred color (downsampling should result in the center color being close to 128)
expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
- 10 /* tolerance */);
+ 50 /* tolerance */);
}
}
template <typename SourceVariant>
+void RenderEngineTest::fillSmallLayerAndBlurBackground() {
+ auto blurRadius = 50;
+ renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings backgroundLayer;
+ backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this);
+ backgroundLayer.alpha = 1.0f;
+ layers.push_back(&backgroundLayer);
+
+ renderengine::LayerSettings blurLayer;
+ blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ blurLayer.geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f);
+ blurLayer.backgroundBlurRadius = blurRadius;
+ SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
+ blurLayer.alpha = 0;
+ layers.push_back(&blurLayer);
+
+ invokeDraw(settings, layers);
+
+ // Give a generous tolerance - the blur rectangle is very small and this test is
+ // mainly concerned with ensuring that there's no device failure.
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255,
+ 40 /* tolerance */);
+}
+
+template <typename SourceVariant>
void RenderEngineTest::overlayCorners() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1402,12 +1443,16 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_color
fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
}
-// TODO(b/186010146): reenable once swiftshader is happy with this test
-TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
initializeRenderEngine();
fillBufferAndBlurBackground<ColorSourceVariant>();
}
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) {
+ initializeRenderEngine();
+ fillSmallLayerAndBlurBackground<ColorSourceVariant>();
+}
+
TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
initializeRenderEngine();
overlayCorners<ColorSourceVariant>();
@@ -1478,12 +1523,16 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqu
fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-// TODO(b/186010146): reenable once swiftshader is happy with this test
-TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) {
+ initializeRenderEngine();
+ fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
initializeRenderEngine();
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1554,12 +1603,16 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_buffe
fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-// TODO(b/186010146): reenable once swiftshader is happy with this test
-TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) {
+ initializeRenderEngine();
+ fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
initializeRenderEngine();
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1839,6 +1892,51 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) {
0, 255, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(&redLayer);
+
+ // Green layer with 1/2 size with parent crop rect.
+ renderengine::LayerSettings greenLayer = redLayer;
+ greenLayer.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
+ greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
+
+ layers.push_back(&greenLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // top middle should be green and the bottom middle red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+
+ // the bottom edge of the green layer should not be rounded
+ expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
index 0803df3828..cf08acb359 100644
--- a/libs/ui/include/ui/StretchEffect.h
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -25,31 +25,49 @@
namespace android {
struct StretchEffect : public LightFlattenablePod<StretchEffect> {
- FloatRect area = {0, 0, 0, 0};
- float vectorX = 0;
- float vectorY = 0;
- float maxAmount = 0;
-
- bool operator==(const StretchEffect& other) const {
- return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
- maxAmount == other.maxAmount;
- }
+ constexpr static const float CONTENT_DISTANCE_STRETCHED = 1.f;
- static bool isZero(float value) {
- constexpr float NON_ZERO_EPSILON = 0.001f;
- return fabsf(value) <= NON_ZERO_EPSILON;
- }
+ float width = 0;
+ float height = 0;
+ float vectorX = 0;
+ float vectorY = 0;
+ float maxAmountX = 0;
+ float maxAmountY = 0;
+ FloatRect mappedChildBounds = {0, 0, 0, 0};
+
+ bool operator==(const StretchEffect& other) const {
+ return width == other.width && height == other.height &&
+ vectorX == other.vectorX &&
+ vectorY == other.vectorY &&
+ maxAmountX == other.maxAmountX &&
+ maxAmountY == other.maxAmountY &&
+ mappedChildBounds == other.mappedChildBounds;
+ }
- bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+ static bool isZero(float value) {
+ constexpr float NON_ZERO_EPSILON = 0.001f;
+ return fabsf(value) <= NON_ZERO_EPSILON;
+ }
- bool hasEffect() const { return !isNoOp(); }
+ bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
- void sanitize() {
- // If the area is empty, or the max amount is zero, then reset back to defaults
- if (area.isEmpty() || isZero(maxAmount)) {
- *this = StretchEffect{};
- }
+ bool hasEffect() const { return !isNoOp(); }
+
+ void sanitize() {
+ // If the area is empty, or the max amount is zero, then reset back to defaults
+ if (width == 0.f || height == 0.f || isZero(maxAmountX) ||
+ isZero(maxAmountY)) {
+ *this = StretchEffect{};
}
+ }
+
+ float getStretchWidthMultiplier() const {
+ return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorX));
+ }
+
+ float getStretchHeightMultiplier() const {
+ return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorY));
+ }
};
static_assert(std::is_trivially_copyable<StretchEffect>::value,
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 3878cb1899..b15694bf50 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -69,11 +69,9 @@ cc_library_headers {
host_supported: true,
vendor_available: true,
export_include_dirs: ["include"],
-}
-
-llndk_headers {
- name: "gl_llndk_headers",
- export_include_dirs: ["include"],
+ llndk: {
+ llndk_headers: true,
+ },
}
subdirs = [
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 7861d62311..c9fce8ad52 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -137,7 +137,12 @@ cc_library_static {
cc_library_shared {
name: "libEGL",
defaults: ["egl_libs_defaults"],
- llndk_stubs: "libEGL.llndk",
+ llndk: {
+ symbol_file: "libEGL.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: [
"EGL/egl_tls.cpp",
"EGL/egl_cache.cpp",
@@ -203,7 +208,12 @@ cc_defaults {
cc_library_shared {
name: "libGLESv1_CM",
defaults: ["gles_libs_defaults"],
- llndk_stubs: "libGLESv1_CM.llndk",
+ llndk: {
+ symbol_file: "libGLESv1_CM.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: ["GLES_CM/gl.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv1\""],
version_script: "libGLESv1_CM.map.txt",
@@ -215,7 +225,12 @@ cc_library_shared {
cc_library_shared {
name: "libGLESv2",
defaults: ["gles_libs_defaults"],
- llndk_stubs: "libGLESv2.llndk",
+ llndk: {
+ symbol_file: "libGLESv2.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv2\""],
@@ -230,31 +245,12 @@ cc_library_shared {
cc_library_shared {
name: "libGLESv3",
defaults: ["gles_libs_defaults"],
- llndk_stubs: "libGLESv3.llndk",
+ llndk: {
+ symbol_file: "libGLESv3.map.txt",
+ export_llndk_headers: ["gl_headers"],
+ // Don't export EGL/include from the LLNDK variant.
+ override_export_include_dirs: [],
+ },
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv3\""],
}
-
-llndk_library {
- name: "libEGL.llndk",
- symbol_file: "libEGL.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
- name: "libGLESv1_CM.llndk",
- symbol_file: "libGLESv1_CM.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
- name: "libGLESv2.llndk",
- symbol_file: "libGLESv2.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
- name: "libGLESv3.llndk",
- symbol_file: "libGLESv3.map.txt",
- export_llndk_headers: ["gl_llndk_headers"],
-}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 7bd0c6b1b8..1b5f1ab76e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -232,7 +232,8 @@ static MotionEvent generateMotionEvent() {
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
return event;
}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 5270b8a107..6ed959306b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -296,12 +296,13 @@ std::string SensorEntry::getDescription() const {
volatile int32_t DispatchEntry::sNextSeqAtomic;
DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor)
+ ui::Transform transform, float globalScaleFactor, vec2 displaySize)
: seq(nextSeq()),
eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
transform(transform),
globalScaleFactor(globalScaleFactor),
+ displaySize(displaySize),
deliveryTime(0),
resolvedAction(0),
resolvedFlags(0) {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index f3ef64ba8e..45c5e24395 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -215,6 +215,7 @@ struct DispatchEntry {
int32_t targetFlags;
ui::Transform transform;
float globalScaleFactor;
+ vec2 displaySize;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -227,7 +228,7 @@ struct DispatchEntry {
int32_t resolvedFlags;
DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor);
+ ui::Transform transform, float globalScaleFactor, vec2 displaySize);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index edbf21aef4..446c913ab9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -283,27 +283,6 @@ static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
return it != map.end() ? it->second : V{};
}
-/**
- * Find the entry in std::unordered_map by value, and remove it.
- * If more than one entry has the same value, then all matching
- * key-value pairs will be removed.
- *
- * Return true if at least one value has been removed.
- */
-template <typename K, typename V>
-static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
- bool removed = false;
- for (auto it = map.begin(); it != map.end();) {
- if (it->second == value) {
- it = map.erase(it);
- removed = true;
- } else {
- it++;
- }
- }
- return removed;
-}
-
static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
if (first == second) {
return true;
@@ -339,14 +318,16 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp
// values do not represent on-screen coordinates, so they should not have any window
// transformations applied to them.
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
- 1.0f /*globalScaleFactor*/);
+ 1.0f /*globalScaleFactor*/,
+ inputTarget.displaySize);
}
}
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.globalScaleFactor);
+ inputTarget.globalScaleFactor,
+ inputTarget.displaySize);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -397,7 +378,8 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
- firstPointerTransform, inputTarget.globalScaleFactor);
+ firstPointerTransform, inputTarget.globalScaleFactor,
+ inputTarget.displaySize);
return dispatchEntry;
}
@@ -504,8 +486,8 @@ InputDispatcher::~InputDispatcher() {
drainInboundQueueLocked();
}
- while (!mConnectionsByFd.empty()) {
- sp<Connection> connection = mConnectionsByFd.begin()->second;
+ while (!mConnectionsByToken.empty()) {
+ sp<Connection> connection = mConnectionsByToken.begin()->second;
removeInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -2402,6 +2384,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH
inputTarget.inputChannel = inputChannel;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTarget.displaySize =
+ vec2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
}
@@ -2902,6 +2886,12 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio
ATRACE_NAME(message.c_str());
}
+ if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
+ (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
+ // Skip reporting pointer down outside focus to the policy.
+ break;
+ }
+
dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
inputTarget.inputChannel->getConnectionToken());
@@ -2967,7 +2957,7 @@ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry,
return; // Not a key or a motion
}
- std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens;
+ std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
std::vector<sp<Connection>> newConnections;
for (const InputTarget& target : targets) {
if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
@@ -3107,6 +3097,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
motionEntry.xPrecision, motionEntry.yPrecision,
motionEntry.xCursorPosition,
motionEntry.yCursorPosition,
+ dispatchEntry->displaySize.x,
+ dispatchEntry->displaySize.y,
motionEntry.downTime, motionEntry.eventTime,
motionEntry.pointerCount,
motionEntry.pointerProperties, usingCoords);
@@ -3290,86 +3282,78 @@ void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
delete dispatchEntry;
}
-int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
- InputDispatcher* d = static_cast<InputDispatcher*>(data);
-
- { // acquire lock
- std::scoped_lock _l(d->mLock);
-
- if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
- ALOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x",
- fd, events);
- return 0; // remove the callback
- }
-
- bool notify;
- sp<Connection> connection = d->mConnectionsByFd[fd];
- if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
- if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x",
- connection->getInputChannelName().c_str(), events);
- return 1;
- }
+int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
+ std::scoped_lock _l(mLock);
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",
+ connectionToken.get(), events);
+ return 0; // remove the callback
+ }
- nsecs_t currentTime = now();
- bool gotOne = false;
- status_t status = OK;
- for (;;) {
- Result<InputPublisher::ConsumerResponse> result =
- connection->inputPublisher.receiveConsumerResponse();
- if (!result.ok()) {
- status = result.error().code();
- break;
- }
+ bool notify;
+ if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
+ if (!(events & ALOOPER_EVENT_INPUT)) {
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ connection->getInputChannelName().c_str(), events);
+ return 1;
+ }
- if (std::holds_alternative<InputPublisher::Finished>(*result)) {
- const InputPublisher::Finished& finish =
- std::get<InputPublisher::Finished>(*result);
- d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
- finish.handled, finish.consumeTime);
- } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
- // TODO(b/167947340): Report this data to LatencyTracker
- }
- gotOne = true;
- }
- if (gotOne) {
- d->runCommandsLockedInterruptible();
- if (status == WOULD_BLOCK) {
- return 1;
- }
+ nsecs_t currentTime = now();
+ bool gotOne = false;
+ status_t status = OK;
+ for (;;) {
+ Result<InputPublisher::ConsumerResponse> result =
+ connection->inputPublisher.receiveConsumerResponse();
+ if (!result.ok()) {
+ status = result.error().code();
+ break;
}
- notify = status != DEAD_OBJECT || !connection->monitor;
- if (notify) {
- ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
- connection->getInputChannelName().c_str(), statusToString(status).c_str(),
- status);
+ if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+ const InputPublisher::Finished& finish =
+ std::get<InputPublisher::Finished>(*result);
+ finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
+ finish.consumeTime);
+ } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+ // TODO(b/167947340): Report this data to LatencyTracker
}
- } else {
- // Monitor channels are never explicitly unregistered.
- // We do it automatically when the remote endpoint is closed so don't warn about them.
- const bool stillHaveWindowHandle =
- d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
- nullptr;
- notify = !connection->monitor && stillHaveWindowHandle;
- if (notify) {
- ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x",
- connection->getInputChannelName().c_str(), events);
+ gotOne = true;
+ }
+ if (gotOne) {
+ runCommandsLockedInterruptible();
+ if (status == WOULD_BLOCK) {
+ return 1;
}
}
- // Remove the channel.
- d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
- return 0; // remove the callback
- } // release lock
+ notify = status != DEAD_OBJECT || !connection->monitor;
+ if (notify) {
+ ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
+ connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+ status);
+ }
+ } else {
+ // Monitor channels are never explicitly unregistered.
+ // We do it automatically when the remote endpoint is closed so don't warn about them.
+ const bool stillHaveWindowHandle =
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr;
+ notify = !connection->monitor && stillHaveWindowHandle;
+ if (notify) {
+ ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
+ connection->getInputChannelName().c_str(), events);
+ }
+ }
+
+ // Remove the channel.
+ removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
+ return 0; // remove the callback
}
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
- for (const auto& [fd, connection] : mConnectionsByFd) {
+ for (const auto& [token, connection] : mConnectionsByToken) {
synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
@@ -3819,7 +3803,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
args->action, args->actionButton, args->flags, args->edgeFlags,
args->metaState, args->buttonState, args->classification, transform,
args->xPrecision, args->yPrecision, args->xCursorPosition,
- args->yCursorPosition, args->downTime, args->eventTime,
+ args->yCursorPosition, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
@@ -4284,12 +4269,8 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>&
return nullptr;
}
-sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
- sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
- return getWindowHandleLocked(focusedToken, displayId);
-}
-
-bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
+ const sp<InputWindowHandle>& windowHandle) const {
for (auto& it : mWindowHandlesByDisplay) {
const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
for (const sp<InputWindowHandle>& handle : windowHandles) {
@@ -4301,11 +4282,16 @@ bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowH
windowHandle->getName().c_str(), it.first,
windowHandle->getInfo()->displayId);
}
- return true;
+ return handle;
}
}
}
- return false;
+ return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
+ return getWindowHandleLocked(focusedToken, displayId);
}
bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
@@ -4333,11 +4319,11 @@ bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHan
std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
const sp<IBinder>& token) const {
- size_t count = mInputChannelsByToken.count(token);
- if (count == 0) {
+ auto connectionIt = mConnectionsByToken.find(token);
+ if (connectionIt == mConnectionsByToken.end()) {
return nullptr;
}
- return mInputChannelsByToken.at(token);
+ return connectionIt->second->inputChannel;
}
void InputDispatcher::updateWindowHandlesForDisplayLocked(
@@ -4461,7 +4447,7 @@ void InputDispatcher::setInputWindowsLocked(
TouchState& state = stateIt->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
- if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
+ if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Touched window was removed: %s in display %" PRId32,
touchedWindow.windowHandle->getName().c_str(), displayId);
@@ -4493,7 +4479,7 @@ void InputDispatcher::setInputWindowsLocked(
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
- if (!hasWindowHandleLocked(oldWindowHandle)) {
+ if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
}
@@ -4987,13 +4973,13 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
dump += INDENT "ReplacedKeys: <empty>\n";
}
- if (!mConnectionsByFd.empty()) {
+ if (!mConnectionsByToken.empty()) {
dump += INDENT "Connections:\n";
- for (const auto& pair : mConnectionsByFd) {
- const sp<Connection>& connection = pair.second;
+ for (const auto& [token, connection] : mConnectionsByToken) {
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
"status=%s, monitor=%s, responsive=%s\n",
- pair.first, connection->getInputChannelName().c_str(),
+ connection->inputChannel->getFd().get(),
+ connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(), connection->getStatusLabel(),
toString(connection->monitor), toString(connection->responsive));
@@ -5041,14 +5027,23 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>
}
}
+class LooperEventCallback : public LooperCallback {
+public:
+ LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
+ int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); }
+
+private:
+ std::function<int(int events)> mCallback;
+};
+
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATION
ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
- std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
- status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+ status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
return base::Error(result) << "Failed to open input channel pair with name " << name;
@@ -5056,13 +5051,20 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
-
+ const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
- mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+ sp<Connection> connection =
+ new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
+
+ if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mConnectionsByToken.emplace(token, connection);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+ this, std::placeholders::_1, token);
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock
// Wake the looper because some connections have changed.
@@ -5090,18 +5092,21 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_
}
sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
-
+ const sp<IBinder>& token = serverChannel->getConnectionToken();
const int fd = serverChannel->getFd();
- mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+
+ if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mConnectionsByToken.emplace(token, connection);
+ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+ this, std::placeholders::_1, token);
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(),
- displayId, toString(isGestureMonitor), pid);
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
}
// Wake the looper because some connections have changed.
@@ -5134,7 +5139,6 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection
}
removeConnectionLocked(connection);
- mInputChannelsByToken.erase(connectionToken);
if (connection->monitor) {
removeMonitorChannelLocked(connectionToken);
@@ -5292,9 +5296,8 @@ sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConn
return nullptr;
}
- for (const auto& pair : mConnectionsByFd) {
- const sp<Connection>& connection = pair.second;
- if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
+ for (const auto& [token, connection] : mConnectionsByToken) {
+ if (token == inputConnectionToken) {
return connection;
}
}
@@ -5312,7 +5315,7 @@ std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connecti
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- removeByValue(mConnectionsByFd, connection);
+ mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
}
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5708face4b..7ba03e8063 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -211,9 +211,6 @@ private:
bool addPortalWindows = false,
bool ignoreDragWindow = false) REQUIRES(mLock);
- // All registered connections mapped by channel file descriptor.
- std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
-
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
@@ -221,13 +218,14 @@ private:
void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& b) const {
- return std::hash<IBinder*>{}(b.get());
- }
+ template <typename T>
+ struct StrongPointerHash {
+ std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
};
- std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash>
- mInputChannelsByToken GUARDED_BY(mLock);
+
+ // All registered connections mapped by input channel token.
+ std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
+ GUARDED_BY(mLock);
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
@@ -327,10 +325,11 @@ private:
// to loop through all displays.
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
int displayId) const REQUIRES(mLock);
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const
+ REQUIRES(mLock);
std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
REQUIRES(mLock);
sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
/*
@@ -371,7 +370,8 @@ private:
std::string mLastAnrState GUARDED_BY(mLock);
// The connection tokens of the channels that the user last interacted, for debugging
- std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock);
+ std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens
+ GUARDED_BY(mLock);
void updateInteractionTokensLocked(const EventEntry& entry,
const std::vector<InputTarget>& targets) REQUIRES(mLock);
@@ -543,7 +543,7 @@ private:
bool notify) REQUIRES(mLock);
void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
- static int handleReceiveCallback(int fd, int events, void* data);
+ int handleReceiveCallback(int events, sp<IBinder> connectionToken);
// The action sent should only be of type AMOTION_EVENT_*
void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index debf8058b3..2543852788 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -100,6 +100,9 @@ struct InputTarget {
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
+ // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
+ vec2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 13712eee53..fb654847ed 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2456,6 +2456,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
+ uint32_t flags = 0;
+
+ if (!PointerGesture::canGestureAffectWindowFocus(mPointerGesture.currentGestureMode)) {
+ flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+
// Update last coordinates of pointers that have moved so that we observe the new
// pointer positions at the same time as other pointers that have just gone up.
bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
@@ -2485,8 +2491,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
mPointerGesture.downTime);
@@ -2504,7 +2510,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
uint32_t id = upGestureIdBits.clearFirstMarkedBit();
dispatchMotion(when, readTime, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2517,7 +2523,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
// Send motion events for all pointers that moved.
if (moveNeeded) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
@@ -2538,7 +2544,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
}
dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
- 0, 0, metaState, buttonState, 0,
+ 0, flags, metaState, buttonState, 0,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2548,8 +2554,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
@@ -2573,7 +2579,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u
const int32_t displayId = mPointerController->getDisplayId();
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 5146299bd5..920f8428f3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -590,6 +590,27 @@ private:
QUIET,
};
+ // When a gesture is sent to an unfocused window, return true if it can bring that window
+ // into focus, false otherwise.
+ static bool canGestureAffectWindowFocus(Mode mode) {
+ switch (mode) {
+ case Mode::TAP:
+ case Mode::TAP_DRAG:
+ case Mode::BUTTON_CLICK_OR_DRAG:
+ // Taps can affect window focus.
+ return true;
+ case Mode::FREEFORM:
+ case Mode::HOVER:
+ case Mode::NEUTRAL:
+ case Mode::PRESS:
+ case Mode::QUIET:
+ case Mode::SWIPE:
+ // Most gestures can be performed on an unfocused window, so they should not
+ // not affect window focus.
+ return false;
+ }
+ }
+
// Time the first finger went down.
nsecs_t firstTouchTime;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ea7a162a1a..f021376e26 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -526,7 +526,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
/*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -539,6 +540,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -551,6 +553,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -564,6 +567,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -576,6 +580,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -587,7 +592,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -597,7 +603,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -609,7 +616,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -620,7 +628,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -633,7 +642,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+ AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -1218,6 +1228,11 @@ public:
return *this;
}
+ MotionEventBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
MotionEvent build() {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
@@ -1237,11 +1252,11 @@ public:
MotionEvent event;
ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
- mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
mButtonState, MotionClassification::NONE, identityTransform,
/* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
- mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
- pointerProperties.data(), pointerCoords.data());
+ mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -1253,8 +1268,11 @@ private:
int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
int32_t mActionButton{0};
int32_t mButtonState{0};
+ int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+ int32_t mDisplayHeight{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
std::vector<PointerBuilder> mPointers;
};
@@ -2032,6 +2050,19 @@ public:
expectedDisplayId, expectedFlags);
}
+ MotionEvent* consumeMotion() {
+ InputEvent* event = mInputReceiver->consume();
+ if (!event) {
+ ADD_FAILURE() << "No event was produced";
+ return nullptr;
+ }
+ if (event->getType() != AINPUT_EVENT_TYPE_MOTION) {
+ ADD_FAILURE() << "Received event of type " << event->getType() << " instead of motion";
+ return nullptr;
+ }
+ return static_cast<MotionEvent*>(event);
+ }
+
void assertNoEvents() { mInputReceiver->assertNoEvents(); }
private:
@@ -2118,6 +2149,27 @@ TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
}
+// Tests for gesture monitors
+TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ window->setWindowOffset(20, 40);
+ window->setWindowTransform(0, 1, -1, 0);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ MotionEvent* event = monitor.consumeMotion();
+ // Even though window has transform, gesture monitor must not.
+ ASSERT_EQ(ui::Transform(), event->getTransform());
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -3062,6 +3114,25 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlr
mFakePolicy->assertOnPointerDownWasNotCalled();
}
+// Have two windows, one with focus. Injecting a trusted DOWN MotionEvent with the flag
+// NO_FOCUS_CHANGE on the unfocused window should not call the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, NoFocusChangeFlag) {
+ const MotionEvent event =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(20).y(20))
+ .addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
+ // Ensure that the unfocused window did not receive any FOCUS events.
+ mUnfocusedWindow->assertNoEvents();
+}
+
// These tests ensures we can send touch events to a single client when there are multiple input
// windows that point to the same client token.
class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index d8e8b52ded..5d6f8c773c 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -132,8 +132,6 @@ SensorDevice::SensorDevice()
}
void SensorDevice::initializeSensorList() {
- float minPowerMa = 0.001; // 1 microAmp
-
checkReturn(mSensors->getSensorsList(
[&](const auto &list) {
const size_t count = list.size();
@@ -151,13 +149,18 @@ void SensorDevice::initializeSensorList() {
// Don't crash in this case since CTS will verify that devices don't go to
// production with a resolution of 0.
if (sensor.resolution != 0) {
- double promotedResolution = sensor.resolution;
- double promotedMaxRange = sensor.maxRange;
- if (fmod(promotedMaxRange, promotedResolution) != 0) {
- ALOGW("%s's max range %f is not a multiple of the resolution %f",
- sensor.name, sensor.maxRange, sensor.resolution);
- SensorDeviceUtils::quantizeValue(
- &sensor.maxRange, promotedResolution);
+ float quantizedRange = sensor.maxRange;
+ SensorDeviceUtils::quantizeValue(
+ &quantizedRange, sensor.resolution, /*factor=*/ 1);
+ // Only rewrite maxRange if the requantization produced a "significant"
+ // change, which is fairly arbitrarily defined as resolution / 8.
+ // Smaller deltas are permitted, as they may simply be due to floating
+ // point representation error, etc.
+ if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
+ ALOGW("%s's max range %.12f is not a multiple of the resolution "
+ "%.12f - updated to %.12f", sensor.name, sensor.maxRange,
+ sensor.resolution, quantizedRange);
+ sensor.maxRange = quantizedRange;
}
} else {
// Don't crash here or the device will go into a crashloop.
@@ -166,10 +169,11 @@ void SensorDevice::initializeSensorList() {
}
// Sanity check and clamp power if it is 0 (or close)
- if (sensor.power < minPowerMa) {
- ALOGI("Reported power %f not deemed sane, clamping to %f",
- sensor.power, minPowerMa);
- sensor.power = minPowerMa;
+ constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
+ if (sensor.power < MIN_POWER_MA) {
+ ALOGI("%s's reported power %f invalid, clamped to %f",
+ sensor.name, sensor.power, MIN_POWER_MA);
+ sensor.power = MIN_POWER_MA;
}
mSensorList.push_back(sensor);
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index 1309971953..255f7e107c 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -32,16 +32,15 @@ using ::android::hidl::manager::V1_0::IServiceNotification;
namespace android {
namespace SensorDeviceUtils {
-// Quantizes a single value using a sensor's resolution.
-inline void quantizeValue(float *value, double resolution) {
+// Quantizes a single value to (a fractional factor of) a sensor's resolution. Typically we
+// increase the value of the sensor's nominal resolution to ensure that sensor accuracy
+// improvements, like runtime calibration, are not masked during requantization.
+inline void quantizeValue(float *value, double resolution, double factor = 0.125) {
if (resolution == 0) {
return;
}
- // Increase the value of the sensor's nominal resolution to ensure that
- // sensor accuracy improvements, like runtime calibration, are not masked
- // during requantization.
- double incRes = 0.125 * resolution;
+ double incRes = factor * resolution;
*value = round(static_cast<double>(*value) / incRes) * incRes;
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 747e2f4c52..a6e5c770a7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -81,7 +81,7 @@ cc_defaults {
name: "libsurfaceflinger_defaults",
defaults: [
"surfaceflinger_defaults",
- "skia_deps",
+ "skia_renderengine_deps",
"libdisplayconfig_defaults",
"unfieddraw_defaults",
],
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 471a551432..f6a602c732 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -314,6 +314,17 @@ bool BufferStateLayer::setCrop(const Rect& crop) {
return true;
}
+bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) {
+ if (mCurrentState.bufferCrop == bufferCrop) return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.bufferCrop = bufferCrop;
+
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
if (mCurrentState.transform.dsdx() == matrix.dsdx &&
@@ -853,10 +864,15 @@ uint32_t BufferStateLayer::getEffectiveScalingMode() const {
}
Rect BufferStateLayer::computeBufferCrop(const State& s) {
- if (s.buffer) {
+ if (s.buffer && !s.bufferCrop.isEmpty()) {
+ Rect bufferCrop;
+ s.buffer->getBuffer()->getBounds().intersect(s.bufferCrop, &bufferCrop);
+ return bufferCrop;
+ } else if (s.buffer) {
return s.buffer->getBuffer()->getBounds();
+ } else {
+ return s.bufferCrop;
}
- return Rect::INVALID_RECT;
}
sp<Layer> BufferStateLayer::createClone() {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 46dfd7f6f6..c4a1377a44 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -51,9 +51,6 @@ public:
return flags;
}
- uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
- uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
- ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
@@ -88,6 +85,8 @@ public:
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
void setAutoRefresh(bool autoRefresh) override;
+ bool setBufferCrop(const Rect& bufferCrop) override;
+
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/Clock.h b/services/surfaceflinger/Clock.h
new file mode 100644
index 0000000000..3f23c6d115
--- /dev/null
+++ b/services/surfaceflinger/Clock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 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 <chrono>
+
+namespace android {
+
+// Abstract interface for timekeeping which can be injected for unit tests.
+class Clock {
+public:
+ Clock() = default;
+ virtual ~Clock() = default;
+
+ // Returns the current time
+ virtual std::chrono::steady_clock::time_point now() const = 0;
+};
+
+class SteadyClock : public Clock {
+public:
+ virtual ~SteadyClock() = default;
+
+ std::chrono::steady_clock::time_point now() const override {
+ return std::chrono::steady_clock::now();
+ }
+};
+
+} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 7ca91d86eb..58bb41adbb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -96,6 +96,11 @@ struct ProjectionSpace {
// physical translation and finally rotate to the physical orientation.
return rotationTransform * destTranslation * scale * sourceTranslation;
}
+
+ bool operator==(const ProjectionSpace& other) const {
+ return bounds == other.bounds && content == other.content &&
+ orientation == other.orientation;
+ }
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index db874875c4..f84ddd3225 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -57,11 +57,15 @@ public:
void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer);
+ // Special caching slot for the layer caching feature.
+ static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+
private:
// an array where the index corresponds to a slot and the value corresponds to a (counter,
// buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
// or used and allows us to keep track of the least-recently used buffer.
- wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+ static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+ wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
uint32_t mNextSlot = 0;
bool mReduceSlotsForWideVideo = false;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 356965cf48..b98043b92e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -102,7 +102,7 @@ struct OutputLayerCompositionState {
// behind the OutputLayer represented by this CompositionState and will
// be visible through it. Unowned - the OutputLayer's lifetime will
// outlast this.)
- OutputLayer* peekThroughLayer = nullptr;
+ compositionengine::OutputLayer* peekThroughLayer = nullptr;
} overrideInfo;
/*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index a6c4eafbab..06f26eb8d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -60,7 +60,6 @@ public:
void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
- NonBufferHash getFingerprint() const { return mFingerprint; }
size_t getLayerCount() const { return mLayers.size(); }
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
@@ -121,7 +120,7 @@ public:
void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
// Retrieve the layer that will be drawn behind this one.
- OutputLayer* getHolePunchLayer() const;
+ compositionengine::OutputLayer* getHolePunchLayer() const;
private:
CachedSet() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 942592a9eb..b09f1d1a09 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -49,6 +49,7 @@ public:
const OutputCompositionState& outputState);
void dump(std::string& result) const;
+ void dumpLayers(std::string& result) const;
private:
size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index 2d9f01b9fd..b1ee3fbf5d 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -36,7 +36,8 @@ inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs,
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
- lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+ lhs.stretchEffect == rhs.stretchEffect;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 0cc2c6e637..5565396922 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -101,9 +101,9 @@ void dumpVal(std::string& out, const char* name, const mat4& tr) {
}
void dumpVal(std::string& out, const char* name, const StretchEffect& effect) {
- StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name,
- effect.area.left, effect.area.top, effect.area.right, effect.area.bottom,
- effect.vectorX, effect.vectorY, effect.maxAmount);
+ StringAppendF(&out, "%s={ width =%f, height = %f, vec=(%f, %f), max=(%f, %f) } ", name,
+ effect.width, effect.height,
+ effect.vectorX, effect.vectorY, effect.maxAmountX, effect.maxAmountY);
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 675c77b33c..ee442eed16 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -92,7 +92,7 @@ void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uin
format = buffer->getPixelFormat();
}
bool widevideo = false;
- uint32_t numSlots = BufferQueue::NUM_BUFFER_SLOTS;
+ uint32_t numSlots = kMaxLayerBufferCount;
// Workaround to reduce slots for 8k buffers
if ((width * height > MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT) && mReduceSlotsForWideVideo &&
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 23b3ec4b49..9eea9fc029 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -725,7 +725,7 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr
editState().earliestPresentTime = refreshArgs.earliestPresentTime;
- OutputLayer* peekThroughLayer = nullptr;
+ compositionengine::OutputLayer* peekThroughLayer = nullptr;
bool hasSecureCamera = false;
bool hasSecureDisplay = false;
bool needsProtected = false;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 7b1376c0b7..6fc19e8dbc 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -18,6 +18,7 @@
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
+#include <compositionengine/impl/HwcBufferCache.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -559,9 +560,11 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
sp<GraphicBuffer> buffer = outputIndependentState.buffer;
sp<Fence> acquireFence = outputIndependentState.acquireFence;
+ int slot = outputIndependentState.bufferSlot;
if (getState().overrideInfo.buffer != nullptr) {
buffer = getState().overrideInfo.buffer->getBuffer();
acquireFence = getState().overrideInfo.acquireFence;
+ slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
}
ALOGV("Writing buffer %p", buffer.get());
@@ -570,8 +573,7 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
sp<GraphicBuffer> hwcBuffer;
// We need access to the output-dependent state for the buffer cache there,
// though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
- &hwcSlot, &hwcBuffer);
+ editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
error != hal::Error::NONE) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index b31e89e247..67854cf31f 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -158,8 +158,12 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine,
const ui::Dataspace& outputDataspace = outputState.dataspace;
const ui::Transform::RotationFlags orientation =
ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+
renderengine::DisplaySettings displaySettings{
- .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+ .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
+ -mBounds.top + outputState.framebufferSpace.content.top,
+ -mBounds.left + outputState.framebufferSpace.content.right,
+ -mBounds.top + outputState.framebufferSpace.content.bottom),
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
@@ -246,9 +250,7 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine,
if (result == NO_ERROR) {
mDrawFence = new Fence(drawFence.release());
- mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
- outputState.framebufferSpace.bounds.getHeight()),
- mBounds);
+ mOutputSpace = outputState.framebufferSpace;
mTexture = std::move(texture);
mOutputSpace.orientation = outputState.framebufferSpace.orientation;
mOutputDataspace = outputDataspace;
@@ -304,7 +306,7 @@ void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, boo
}
}
-OutputLayer* CachedSet::getHolePunchLayer() const {
+compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index b4e761004f..a63f21f41a 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -45,7 +45,10 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers,
}
for (size_t i = 0; i < incomingLayers.size(); i++) {
- if (incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
+ // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try
+ // to access destroyed OutputLayers later on.
+ if (incomingLayers[i]->getId() != existingLayers[i]->getId() ||
+ incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
return false;
}
}
@@ -96,6 +99,14 @@ void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
mNewCachedSet->render(renderEngine, outputState);
}
+void Flattener::dumpLayers(std::string& result) const {
+ result.append(" Current layers:");
+ for (const CachedSet& layer : mLayers) {
+ result.append("\n");
+ layer.dump(result);
+ }
+}
+
void Flattener::dump(std::string& result) const {
const auto now = std::chrono::steady_clock::now();
@@ -140,11 +151,7 @@ void Flattener::dump(std::string& result) const {
base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
durationString(lastUpdate).c_str());
- result.append(" Current layers:");
- for (const CachedSet& layer : mLayers) {
- result.append("\n");
- layer.dump(result);
- }
+ dumpLayers(result);
}
size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
@@ -232,7 +239,8 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers
auto currentLayerIter = mLayers.begin();
auto incomingLayerIter = layers.begin();
while (incomingLayerIter != layers.end()) {
- if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
+ if (mNewCachedSet &&
+ mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
if (mNewCachedSet->hasBufferUpdate()) {
ALOGV("[%s] Dropping new cached set", __func__);
++mInvalidatedCachedSetAges[0];
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 7d2bf06133..7e85dcaee0 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -233,6 +233,8 @@ void Planner::dump(const Vector<String16>& args, std::string& result) {
}
mPredictor.listSimilarStacks(*plan, result);
+ } else if (command == "--layers" || command == "-l") {
+ mFlattener.dumpLayers(result);
} else {
base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
dumpUsage(result);
@@ -268,6 +270,9 @@ void Planner::dumpUsage(std::string& result) const {
result.append("[--similar|-s] <plan>\n");
result.append(" Prints the example layer names for similar stacks matching <plan>\n");
+
+ result.append("[--layers|-l]\n");
+ result.append(" Prints the current layers\n");
}
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index fb8ffcedf3..3adfe405ff 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <compositionengine/impl/HwcBufferCache.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
@@ -702,6 +703,7 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
+ static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
@@ -824,10 +826,11 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
}
- void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+ void expectSetHdrMetadataAndBufferCalls(uint32_t hwcSlot = kExpectedHwcSlot,
+ sp<GraphicBuffer> buffer = kBuffer,
sp<Fence> fence = kFence) {
EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
- EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
+ EXPECT_CALL(*mHwcLayer, setBuffer(hwcSlot, buffer, fence));
}
void expectGenericLayerMetadataCalls() {
@@ -1060,15 +1063,75 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
kOverrideSurfaceDamage);
- expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+ expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
+ auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
+ OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
+
+ mOutputLayer.mState.overrideInfo.peekThroughLayer = &peekThroughLayer;
+
+ expectGeometryCommonCalls(kDisplayFrame, kSourceCrop, kBufferTransform,
+ Hwc2::IComposerClient::BlendMode::PREMULTIPLIED);
+ expectPerFrameCommonCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ true);
+ EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/
+ false);
+ EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/
+ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) {
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/
+ true);
+ EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE,
+ mOutputLayer.getState().hwc->hwcCompositionType);
+}
+
/*
* OutputLayer::writeCursorPositionToHWC()
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 7b71957021..27980a01ac 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -879,6 +879,64 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLa
mOutput->writeCompositionState(args);
}
+TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) {
+ renderengine::mock::RenderEngine renderEngine;
+ InjectedLayer layer0;
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
+
+ InSequence seq;
+ EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+
+ uint32_t z = 0;
+ EXPECT_CALL(*layer0.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+
+ // After calling planComposition (which clears overrideInfo), this test sets
+ // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
+ // comes first, setting isPeekingThrough to true and zIsOverridden to true
+ // for it and the following layers.
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/
+ true));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+
+ injectOutputLayer(layer0);
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = true;
+ args.devOptForceClientComposition = false;
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+
+ std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ layer1.outputLayerState.overrideInfo.buffer = buffer;
+ layer2.outputLayerState.overrideInfo.buffer = buffer;
+ layer1.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
+ layer2.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
+
+ mOutput->writeCompositionState(args);
+}
+
/*
* Output::prepareFrame()
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 283c69270f..a39331c02d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -19,8 +19,12 @@
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
+#include <gmock/gmock-actions.h>
#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+#include <ui/GraphicTypes.h>
+#include <memory>
namespace android::compositionengine {
using namespace std::chrono_literals;
@@ -105,7 +109,6 @@ void CachedSetTest::TearDown() {
}
void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) {
- EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion()));
@@ -154,7 +157,6 @@ TEST_F(CachedSetTest, addLayer) {
CachedSet cachedSet(layer1);
cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
- EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
Region expectedRegion;
@@ -243,7 +245,6 @@ TEST_F(CachedSetTest, append) {
cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
cachedSet1.append(cachedSet2);
- EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
Region expectedRegion;
@@ -287,10 +288,11 @@ TEST_F(CachedSetTest, updateAge_BufferUpdate) {
}
TEST_F(CachedSetTest, render) {
- CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
- sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
- CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
- sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
CachedSet cachedSet(layer1);
cachedSet.append(CachedSet(layer2));
@@ -307,7 +309,7 @@ TEST_F(CachedSetTest, render) {
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
displaySettings.orientation);
@@ -324,15 +326,246 @@ TEST_F(CachedSetTest, render) {
cachedSet.render(mRenderEngine, mOutputState);
expectReadyBuffer(cachedSet);
- EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content);
- EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(),
- mOutputState.framebufferSpace.bounds.getHeight()),
- cachedSet.getOutputSpace().bounds);
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
// Now check that appending a new cached set properly cleans up RenderEngine resources.
CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
cachedSet.append(CachedSet(layer3));
}
+TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ clientCompList1[0].alpha = 0.5f;
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ clientCompList2[0].alpha = 0.75f;
+
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ displaySettings.orientation);
+ EXPECT_EQ(0.5f, layers[0]->alpha);
+ EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, holePunch_requiresBuffer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ EXPECT_CALL(*layerFE1, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+
+ CachedSet cachedSet(layer1);
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresSingleLayer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(layer2);
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, requiresHolePunch) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+ CachedSet cachedSet(layer);
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_TRUE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresDeviceComposition) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+ auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
+ layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ layerFECompositionState.forceClientComposition = true;
+
+ CachedSet cachedSet(layer);
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, addHolePunch_requiresOverlap) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_requiresOpaque) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = false;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+ ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_opaque) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = true;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_firstLayer) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = false;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
+ clientCompList3.push_back({});
+
+ clientCompList3[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
+ renderengine::ExternalTexture::READABLE);
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ // If the highlight layer is enabled, it will increase the size by 1.
+ // We're interested in the third layer either way.
+ EXPECT_GE(layers.size(), 3u);
+ const auto* holePunchSettings = layers[2];
+ EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
+ EXPECT_TRUE(holePunchSettings->disableBlending);
+ EXPECT_EQ(0.0f, holePunchSettings->alpha);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mOutputState);
+}
+
+TEST_F(CachedSetTest, decompose_removesHolePunch) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+
+ std::vector<CachedSet> decomposed = cachedSet.decompose();
+ EXPECT_EQ(2u, decomposed.size());
+ for (const auto& set : decomposed) {
+ EXPECT_EQ(nullptr, set.getHolePunchLayer());
+ }
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index c5280877d5..71757f69ea 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -22,6 +22,8 @@
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
#include <renderengine/mock/RenderEngine.h>
namespace android::compositionengine {
@@ -45,7 +47,7 @@ namespace {
class FlattenerTest : public testing::Test {
public:
- FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
+ FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor, true)) {}
void SetUp() override;
protected:
@@ -275,11 +277,7 @@ TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsProjectionSpace) {
mTime += 200ms;
expectAllLayersFlattened(layers);
- EXPECT_EQ(overrideDisplaySpace.bounds,
- Rect(mOutputState.framebufferSpace.bounds.getWidth(),
- mOutputState.framebufferSpace.bounds.getHeight()));
- EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2));
- EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation);
+ EXPECT_EQ(overrideDisplaySpace, mOutputState.framebufferSpace);
}
TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
@@ -532,5 +530,123 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
EXPECT_EQ(overrideBuffer4, overrideBuffer5);
}
+// Tests for a PIP
+TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // 3 has a buffer update, so it will not be merged, but it has no round
+ // corners, so it is not a PIP.
+ mTime += 200ms;
+ layerState3->resetFramesSinceBufferUpdate();
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_pip) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+ clientCompositionList[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
+ EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(clientCompositionList));
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // 3 has a buffer update, so it will not be merged, and it has round
+ // corners, so it is a PIP.
+ mTime += 200ms;
+ layerState3->resetFramesSinceBufferUpdate();
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ const auto* peekThroughLayer1 =
+ layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ const auto* peekThroughLayer2 =
+ layerState2->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ EXPECT_EQ(&mTestLayers[2]->outputLayer, peekThroughLayer1);
+ EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
+}
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 0f4d0dd119..1b1a8cddc7 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -25,7 +25,6 @@
#include "ComposerHal.h"
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
-#include <gui/BufferQueue.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
#include <log/log.h>
@@ -378,15 +377,15 @@ Error Composer::acceptDisplayChanges(Display display)
Error Composer::createLayer(Display display, Layer* outLayer)
{
Error error = kDefaultError;
- mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS,
- [&](const auto& tmpError, const auto& tmpLayer) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ mClient->createLayer(display, kMaxLayerBufferCount,
+ [&](const auto& tmpError, const auto& tmpLayer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outLayer = tmpLayer;
- });
+ *outLayer = tmpLayer;
+ });
return error;
}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 9104414daf..7be856fb1a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,7 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <gui/BufferQueue.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/DisplayedFrameStats.h>
@@ -522,6 +523,11 @@ private:
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize =
64 * 1024 / sizeof(uint32_t) - 16;
+ // Max number of buffers that may be cached for a given layer
+ // We obtain this number by:
+ // 1. Tightly coupling this cache to the max size of BufferQueue
+ // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
+ static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
CommandWriter mWriter;
CommandReader mReader;
};
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index 0bc2d3eb01..23db805d5b 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -26,10 +26,18 @@
namespace android {
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger)
- : mFrameTimeline(frameTimeline), mFlinger(flinger) {}
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ std::unique_ptr<Clock> clock)
+ : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) {
+ LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!");
+}
+
+void FpsReporter::dispatchLayerFps() {
+ const auto now = mClock->now();
+ if (now - mLastDispatch < kMinDispatchDuration) {
+ return;
+ }
-void FpsReporter::dispatchLayerFps() const {
std::vector<TrackedListener> localListeners;
{
std::scoped_lock lock(mMutex);
@@ -71,6 +79,8 @@ void FpsReporter::dispatchLayerFps() const {
listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
}
+
+ mLastDispatch = now;
}
void FpsReporter::binderDied(const wp<IBinder>& who) {
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index 1cec295a31..bd7b9a5222 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -22,6 +22,7 @@
#include <unordered_map>
+#include "Clock.h"
#include "FrameTimeline/FrameTimeline.h"
namespace android {
@@ -31,12 +32,13 @@ class SurfaceFlinger;
class FpsReporter : public IBinder::DeathRecipient {
public:
- FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger);
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
// Dispatches updated layer fps values for the registered listeners
// This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
// must be held when calling this method.
- void dispatchLayerFps() const EXCLUDES(mMutex);
+ void dispatchLayerFps() EXCLUDES(mMutex);
// Override for IBinder::DeathRecipient
void binderDied(const wp<IBinder>&) override;
@@ -61,6 +63,10 @@ private:
frametimeline::FrameTimeline& mFrameTimeline;
SurfaceFlinger& mFlinger;
+ static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration =
+ std::chrono::milliseconds(500);
+ std::unique_ptr<Clock> mClock;
+ std::chrono::steady_clock::time_point mLastDispatch;
std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index be552c6435..f19e2a7863 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -256,6 +256,10 @@ int32_t jankTypeBitmaskToProto(int32_t jankType) {
protoJank |= FrameTimelineEvent::JANK_UNKNOWN;
jankType &= ~JankType::Unknown;
}
+ if (jankType & JankType::SurfaceFlingerStuffing) {
+ protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
+ jankType &= ~JankType::SurfaceFlingerStuffing;
+ }
// jankType should be 0 if all types of jank were checked for.
LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -726,9 +730,11 @@ namespace impl {
int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
ATRACE_CALL();
std::scoped_lock lock(mMutex);
+ while (mPredictions.size() >= kMaxTokens) {
+ mPredictions.erase(mPredictions.begin());
+ }
const int64_t assignedToken = mCurrentToken++;
- mPredictions[assignedToken] = {systemTime(), predictions};
- flushTokens(systemTime());
+ mPredictions[assignedToken] = predictions;
return assignedToken;
}
@@ -736,23 +742,11 @@ std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token)
std::scoped_lock lock(mMutex);
auto predictionsIterator = mPredictions.find(token);
if (predictionsIterator != mPredictions.end()) {
- return predictionsIterator->second.predictions;
+ return predictionsIterator->second;
}
return {};
}
-void TokenManager::flushTokens(nsecs_t flushTime) {
- for (auto it = mPredictions.begin(); it != mPredictions.end();) {
- if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
- it = mPredictions.erase(it);
- } else {
- // Tokens are ordered by time. If i'th token is within the retention time, then the
- // i+1'th token will also be within retention time.
- break;
- }
- }
-}
-
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
JankClassificationThresholds thresholds)
: mMaxDisplayFrames(kDefaultMaxDisplayFrames),
@@ -875,7 +869,8 @@ void FrameTimeline::DisplayFrame::setGpuFence(const std::shared_ptr<FenceTime>&
mGpuFence = gpuFence;
}
-void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+ nsecs_t previousPresentTime) {
if (mPredictionState == PredictionState::Expired ||
mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
// Cannot do jank classification with expired predictions or invalid signal times. Set the
@@ -949,7 +944,15 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t&
mJankType = JankType::Unknown;
}
} else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
- if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
+ mJankClassificationThresholds.presentThreshold ||
+ previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
+ // The previous frame was either presented in the current frame's expected vsync or
+ // it was presented even later than the current frame's expected vsync.
+ mJankType = JankType::SurfaceFlingerStuffing;
+ }
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
+ !(mJankType & JankType::SurfaceFlingerStuffing)) {
// Finish on time, Present late
if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
@@ -963,11 +966,12 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t&
mJankType = JankType::PredictionError;
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- if (mFrameStartMetadata == FrameStartMetadata::LateStart) {
- // Late start, Late finish, Late Present
- mJankType = JankType::SurfaceFlingerScheduling;
- } else {
- // OnTime start, Finish late, Present late
+ if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
+ mSurfaceFlingerActuals.presentTime - previousPresentTime >
+ mRefreshRate.getPeriodNsecs() +
+ mJankClassificationThresholds.presentThreshold) {
+ // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
+ // was presented more than a vsync late.
if (mGpuFence != FenceTime::NO_FENCE &&
mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
mRefreshRate.getPeriodNsecs()) {
@@ -989,11 +993,11 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t&
}
}
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) {
mSurfaceFlingerActuals.presentTime = signalTime;
nsecs_t deadlineDelta = 0;
nsecs_t deltaToVsync = 0;
- classifyJank(deadlineDelta, deltaToVsync);
+ classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
for (auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
@@ -1158,8 +1162,9 @@ void FrameTimeline::flushPendingPresentFences() {
}
}
auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime);
+ displayFrame->onPresent(signalTime, mPreviousPresentTime);
displayFrame->trace(mSurfaceFlingerPid);
+ mPreviousPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
--i;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 41f4978543..42be55ae2c 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -92,11 +92,6 @@ struct TimelineItem {
bool operator!=(const TimelineItem& other) const { return !(*this == other); }
};
-struct TokenManagerPrediction {
- nsecs_t timestamp = 0;
- TimelineItem predictions;
-};
-
struct JankClassificationThresholds {
// The various thresholds for App and SF. If the actual timestamp falls within the threshold
// compared to prediction, we treat it as on time.
@@ -334,11 +329,10 @@ private:
void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
- std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
+ std::map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
int64_t mCurrentToken GUARDED_BY(mMutex);
mutable std::mutex mMutex;
- static constexpr nsecs_t kMaxRetentionTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+ static constexpr size_t kMaxTokens = 500;
};
class FrameTimeline : public android::frametimeline::FrameTimeline {
@@ -370,7 +364,7 @@ public:
void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
nsecs_t wakeUpTime);
// Sets the appropriate metadata and classifies the jank.
- void onPresent(nsecs_t signalTime);
+ void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
// Adds the provided SurfaceFrame to the current display frame.
void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
@@ -398,7 +392,8 @@ public:
void dump(std::string& result, nsecs_t baseTime) const;
void tracePredictions(pid_t surfaceFlingerPid) const;
void traceActuals(pid_t surfaceFlingerPid) const;
- void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync);
+ void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+ nsecs_t previousPresentTime);
int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
@@ -480,6 +475,7 @@ private:
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
+ nsecs_t mPreviousPresentTime = 0;
const JankClassificationThresholds mJankClassificationThresholds;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 41b7ecc740..b35f6ecc0e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -639,8 +639,10 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom
if (!targetSettings.disableBlurs) {
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
layerSettings.blurRegions = getBlurRegions();
+ layerSettings.blurRegionTransform =
+ getActiveTransform(getDrawingState()).inverse().asMatrix4();
}
- layerSettings.stretchEffect = getDrawingState().stretchEffect;
+ layerSettings.stretchEffect = getStretchEffect();
// Record the name of the layer for debugging further down the stack.
layerSettings.name = getName();
return layerSettings;
@@ -880,7 +882,7 @@ uint32_t Layer::doTransaction(uint32_t flags) {
const State& s(getDrawingState());
State& c(getCurrentState());
- if (getActiveGeometry(c) != getActiveGeometry(s)) {
+ if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
}
@@ -955,20 +957,18 @@ uint32_t Layer::setTransactionFlags(uint32_t flags) {
}
bool Layer::setPosition(float x, float y) {
- if (mCurrentState.requested_legacy.transform.tx() == x &&
- mCurrentState.requested_legacy.transform.ty() == y)
- return false;
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false;
mCurrentState.sequence++;
// We update the requested and active position simultaneously because
// we want to apply the position portion of the transform matrix immediately,
// but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
- mCurrentState.requested_legacy.transform.set(x, y);
+ mCurrentState.transform.set(x, y);
// Here we directly update the active state
// unlike other setters, because we store it within
// the transform, but use different latching rules.
// b/38182305
- mCurrentState.active_legacy.transform.set(x, y);
+ mCurrentState.transform.set(x, y);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -1087,6 +1087,7 @@ bool Layer::setSize(uint32_t w, uint32_t h) {
setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h);
return true;
}
+
bool Layer::setAlpha(float alpha) {
if (mCurrentState.color.a == alpha) return false;
mCurrentState.sequence++;
@@ -1165,8 +1166,7 @@ bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
return false;
}
mCurrentState.sequence++;
- mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx,
- matrix.dsdy);
+ mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1588,20 +1588,20 @@ LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
info.mVisibleRegion = getVisibleRegion(display);
info.mSurfaceDamageRegion = surfaceDamageRegion;
info.mLayerStack = getLayerStack();
- info.mX = ds.active_legacy.transform.tx();
- info.mY = ds.active_legacy.transform.ty();
+ info.mX = ds.transform.tx();
+ info.mY = ds.transform.ty();
info.mZ = ds.z;
- info.mWidth = ds.active_legacy.w;
- info.mHeight = ds.active_legacy.h;
+ info.mWidth = ds.width;
+ info.mHeight = ds.height;
info.mCrop = ds.crop;
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
- info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
- info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
- info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
- info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
+ info.mMatrix[0][0] = ds.transform[0][0];
+ info.mMatrix[0][1] = ds.transform[0][1];
+ info.mMatrix[1][0] = ds.transform[1][0];
+ info.mMatrix[1][1] = ds.transform[1][1];
{
sp<const GraphicBuffer> buffer = getBuffer();
if (buffer != 0) {
@@ -2479,6 +2479,8 @@ InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
ui::Transform toPhysicalDisplay;
if (display) {
toPhysicalDisplay = display->getTransform();
+ info.displayWidth = display->getWidth();
+ info.displayHeight = display->getHeight();
}
fillInputFrameInfo(info, toPhysicalDisplay);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7118513717..7d43a59a01 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -274,6 +274,8 @@ public:
// Stretch effect to apply to this layer
StretchEffect stretchEffect;
+
+ Rect bufferCrop;
};
/*
@@ -483,12 +485,9 @@ public:
// to avoid grabbing the lock again to avoid deadlock
virtual bool isCreatedFromMainThread() const { return false; }
- virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
- virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
- virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
- virtual ui::Transform getActiveTransform(const Layer::State& s) const {
- return s.active_legacy.transform;
- }
+ uint32_t getActiveWidth(const Layer::State& s) const { return s.width; }
+ uint32_t getActiveHeight(const Layer::State& s) const { return s.height; }
+ ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
virtual Region getActiveTransparentRegion(const Layer::State& s) const {
return s.activeTransparentRegion_legacy;
}
@@ -890,6 +889,7 @@ public:
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
+ virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
virtual std::string getPendingBufferCounterName() { return ""; }
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index d659398394..16f041ae31 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -15,7 +15,6 @@
*/
#include "OneShotTimer.h"
-
#include <utils/Log.h>
#include <utils/Timers.h>
#include <chrono>
@@ -40,14 +39,9 @@ void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) {
namespace android {
namespace scheduler {
-std::chrono::steady_clock::time_point OneShotTimer::Clock::now() const {
- return std::chrono::steady_clock::now();
-}
-
OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
const ResetCallback& resetCallback,
- const TimeoutCallback& timeoutCallback,
- std::unique_ptr<OneShotTimer::Clock> clock)
+ const TimeoutCallback& timeoutCallback, std::unique_ptr<Clock> clock)
: mClock(std::move(clock)),
mName(std::move(name)),
mInterval(interval),
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 7285427abb..09265bb947 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -20,6 +20,7 @@
#include <chrono>
#include <condition_variable>
#include <thread>
+#include "../Clock.h"
#include <android-base/thread_annotations.h>
@@ -36,17 +37,9 @@ public:
using ResetCallback = std::function<void()>;
using TimeoutCallback = std::function<void()>;
- class Clock {
- public:
- Clock() = default;
- virtual ~Clock() = default;
-
- virtual std::chrono::steady_clock::time_point now() const;
- };
-
OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
const TimeoutCallback& timeoutCallback,
- std::unique_ptr<OneShotTimer::Clock> = std::make_unique<OneShotTimer::Clock>());
+ std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
// Initializes and turns on the idle timer.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5279af341a..f825fae486 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -606,13 +606,9 @@ void Scheduler::registerLayer(Layer* layer) {
scheduler::LayerHistory::LayerVoteType voteType;
- if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+ if (!mOptions.useContentDetection ||
+ layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
- } else if (!mOptions.useContentDetection) {
- // If the content detection feature is off, all layers are registered at Max. We still keep
- // the layer history, since we use it for other features (like Frame Rate API), so layers
- // still need to be registered.
- voteType = scheduler::LayerHistory::LayerVoteType::Max;
} else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
voteType = scheduler::LayerHistory::LayerVoteType::Min;
@@ -620,6 +616,9 @@ void Scheduler::registerLayer(Layer* layer) {
voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
}
+ // If the content detection feature is off, we still keep the layer history,
+ // since we use it for other features (like Frame Rate API), so layers
+ // still need to be registered.
mLayerHistory->registerLayer(layer, voteType);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1cab62767e..6c3efee19e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2107,12 +2107,6 @@ void SurfaceFlinger::updateFrameScheduler() NO_THREAD_SAFETY_ANALYSIS {
}
void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
- const auto vsyncIn = [&] {
- if (!ATRACE_ENABLED()) return 0.f;
- return (expectedVSyncTime - systemTime()) / 1e6f;
- }();
-
- ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn);
switch (what) {
case MessageQueue::INVALIDATE: {
onMessageInvalidate(vsyncId, expectedVSyncTime);
@@ -2138,8 +2132,6 @@ void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t ex
}
void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
- ATRACE_CALL();
-
const nsecs_t frameStart = systemTime();
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
@@ -2155,6 +2147,13 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT
mScheduledPresentTime = expectedVSyncTime;
updateFrameScheduler();
+ const auto vsyncIn = [&] {
+ if (!ATRACE_ENABLED()) return 0.f;
+ return (mExpectedPresentTime - systemTime()) / 1e6f;
+ }();
+ ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn,
+ mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)");
+
// When Backpressure propagation is enabled we want to give a small grace period
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
@@ -3841,7 +3840,7 @@ void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- signalSynchronousTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
mAnimTransactionPending = false;
}
@@ -4277,7 +4276,9 @@ void SurfaceFlinger::queueTransaction(TransactionState& state) {
// Generate a CountDownLatch pending state if this is a synchronous transaction.
if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
- (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+ (state.inputWindowCommands.syncInputWindows
+ ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction)
+ : CountDownLatch::eSyncTransaction));
}
mTransactionQueue.emplace(state);
@@ -4302,10 +4303,10 @@ void SurfaceFlinger::waitForSynchronousTransaction(
}
}
-void SurfaceFlinger::signalSynchronousTransactions() {
+void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
for (auto it = mTransactionCommittedSignals.begin();
it != mTransactionCommittedSignals.end();) {
- if ((*it)->countDown() == 0) {
+ if ((*it)->countDown(flag)) {
it = mTransactionCommittedSignals.erase(it);
} else {
it++;
@@ -4828,6 +4829,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eBufferCropChanged) {
+ if (layer->setBufferCrop(s.bufferCrop)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// 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
@@ -6064,6 +6070,13 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co
result.append("\n");
}
+ {
+ DumpArgs plannerArgs;
+ plannerArgs.add(); // first argument is ignored
+ plannerArgs.add(String16("--layers"));
+ dumpPlannerInfo(plannerArgs, result);
+ }
+
/*
* Dump HWComposer state
*/
@@ -7414,7 +7427,7 @@ status_t SurfaceFlinger::renderScreenImplLocked(
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
- signalSynchronousTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 87fe55f453..ec76de35eb 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -543,24 +543,31 @@ private:
class CountDownLatch {
public:
- explicit CountDownLatch(int32_t count) : mCount(count) {}
-
- int32_t countDown() {
+ enum {
+ eSyncTransaction = 1 << 0,
+ eSyncInputWindows = 1 << 1,
+ };
+ explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
+
+ // True if there is no waiting condition after count down.
+ bool countDown(uint32_t flag) {
std::unique_lock<std::mutex> lock(mMutex);
- if (mCount == 0) {
- return 0;
+ if (mFlags == 0) {
+ return true;
}
- if (--mCount == 0) {
+ mFlags &= ~flag;
+ if (mFlags == 0) {
mCountDownComplete.notify_all();
+ return true;
}
- return mCount;
+ return false;
}
// Return true if triggered.
bool wait_until(const std::chrono::seconds& timeout) const {
std::unique_lock<std::mutex> lock(mMutex);
const auto untilTime = std::chrono::system_clock::now() + timeout;
- while (mCount != 0) {
+ while (mFlags != 0) {
// Conditional variables can be woken up sporadically, so we check count
// to verify the wakeup was triggered by |countDown|.
if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
@@ -571,7 +578,7 @@ private:
}
private:
- int32_t mCount;
+ uint32_t mFlags;
mutable std::condition_variable mCountDownComplete;
mutable std::mutex mMutex;
};
@@ -1256,7 +1263,7 @@ private:
// Add transaction to the Transaction Queue
void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
- void signalSynchronousTransactions();
+ void signalSynchronousTransactions(const uint32_t flag);
/*
* Generic Layer Metadata
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 113f463c39..c5f1598d15 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -130,8 +130,8 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
const int32_t layerId(getLayerId(layer));
- addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(),
- layer->mCurrentState.active_legacy.transform.ty());
+ addPositionLocked(transaction, layerId, layer->mCurrentState.transform.tx(),
+ layer->mCurrentState.transform.ty());
addDepthLocked(transaction, layerId, layer->mCurrentState.z);
addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
addTransparentRegionLocked(transaction, layerId,
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 3d82afa43a..10d58a6087 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -427,8 +427,8 @@ bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
return true;
}
-static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) {
- return (fps.getIntValue() / bucketWidth) * bucketWidth;
+static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) {
+ return std::round(fps.getValue() / bucketWidth) * bucketWidth;
}
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
@@ -441,10 +441,10 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
const int32_t refreshRateBucket =
- clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
const int32_t renderRateBucket =
- clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate,
- RENDER_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(renderRate ? *renderRate : displayRefreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
while (!timeRecords.empty()) {
if (!recordReadyLocked(layerId, &timeRecords[0])) break;
ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
@@ -799,10 +799,10 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) {
static const std::string kDefaultLayerName = "none";
const int32_t refreshRateBucket =
- clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
const int32_t renderRateBucket =
- clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
- RENDER_RATE_BUCKET_WIDTH);
+ clampToNearestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
if (!mTimeStats.stats.count(timelineKey)) {
@@ -1021,6 +1021,7 @@ void TimeStats::disable() {
void TimeStats::clearAll() {
std::lock_guard<std::mutex> lock(mMutex);
+ mTimeStats.stats.clear();
clearGlobalLocked();
clearLayersLocked();
}
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 011ff70409..965aac301d 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -767,7 +767,8 @@ TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateC
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
}
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
@@ -938,7 +939,8 @@ TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
}
-TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 7581cd3af4..c8eeac66e9 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1336,7 +1336,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
Color::GREEN, true /* filtered */);
}
-TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+// TODO (b/186543004): Fix & re-enable
+TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) {
sp<SurfaceControl> layer;
Transaction transaction;
ASSERT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index a2291b2762..dec0ff5df2 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -28,6 +28,7 @@
#include "FpsReporter.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
+#include "fake/FakeClock.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
#include "mock/MockFrameTimeline.h"
@@ -91,7 +92,9 @@ protected:
sp<Layer> mUnrelated;
sp<TestableFpsListener> mFpsListener;
- sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger()));
+ fake::FakeClock* mClock = new fake::FakeClock();
+ sp<FpsReporter> mFpsReporter =
+ new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock));
};
FpsReporterTest::FpsReporterTest() {
@@ -178,6 +181,7 @@ TEST_F(FpsReporterTest, callsListeners) {
.WillOnce(Return(expectedFps));
mFpsReporter->addListener(mFpsListener, kTaskId);
+ mClock->advanceTime(600ms);
mFpsReporter->dispatchLayerFps();
EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
mFpsReporter->removeListener(mFpsListener);
@@ -187,5 +191,34 @@ TEST_F(FpsReporterTest, callsListeners) {
mFpsReporter->dispatchLayerFps();
}
+TEST_F(FpsReporterTest, rateLimits) {
+ const constexpr int32_t kTaskId = 12;
+ LayerMetadata targetMetadata;
+ targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+ mTarget = createBufferStateLayer(targetMetadata);
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+
+ float firstFps = 44.0;
+ float secondFps = 53.0;
+
+ EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence())))
+ .WillOnce(Return(firstFps))
+ .WillOnce(Return(secondFps));
+
+ mFpsReporter->addListener(mFpsListener, kTaskId);
+ mClock->advanceTime(600ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(secondFps, mFpsListener->lastReportedFps);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 8a3f561487..c6a41159c1 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -73,7 +73,7 @@ public:
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
- maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+ maxTokens = mTokenManager->kMaxTokens;
}
// Each tracing session can be used for a single block of Start -> Stop.
@@ -111,9 +111,11 @@ public:
mFrameTimeline->setSfPresent(2500, presentFence1);
}
- void flushTokens(nsecs_t flushTime) {
- std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
- mTokenManager->flushTokens(flushTime);
+ void flushTokens() {
+ for (size_t i = 0; i < maxTokens; i++) {
+ mTokenManager->generateTokenForPredictions({});
+ }
+ EXPECT_EQ(getPredictions().size(), maxTokens);
}
SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
@@ -132,7 +134,7 @@ public:
a.presentTime == b.presentTime;
}
- const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
+ const std::map<int64_t, TimelineItem>& getPredictions() const {
return mTokenManager->mPredictions;
}
@@ -155,7 +157,7 @@ public:
TraceCookieCounter* mTraceCookieCounter;
FenceToFenceTimeMap fenceFactory;
uint32_t* maxDisplayFrames;
- nsecs_t maxTokenRetentionTime;
+ size_t maxTokens;
static constexpr pid_t kSurfaceFlingerPid = 666;
static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count();
@@ -177,12 +179,11 @@ static constexpr int32_t sLayerIdTwo = 2;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
EXPECT_EQ(getPredictions().size(), 1u);
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
// token1 should have expired
- EXPECT_EQ(getPredictions().size(), 1u);
EXPECT_EQ(predictions.has_value(), false);
predictions = mTokenManager->getPredictionsForToken(token2);
@@ -212,7 +213,7 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
@@ -707,7 +708,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres
sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1065,7 +1066,7 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_predictionExpiredDoesNotTraceExpecte
tracingSession->StartBlocking();
int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
@@ -1283,7 +1284,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpecte
mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
@@ -1359,7 +1360,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced
mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
@@ -1561,13 +1562,22 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent)
/*
* Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed
* Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed
+ * Case 3 - previous frame ran longer -> sf_stuffing
+ * Case 4 - Long cpu under SF stuffing -> cpudeadlinemissed
*/
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken3 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+ int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+
// case 1 - cpu time = 33 - 12 = 21, vsync period = 11
mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
@@ -1578,12 +1588,12 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent)
// Fences haven't been flushed yet, so it should be 0
EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
- // case 2 - cpu time = 56 - 52 = 4, vsync period = 11
- mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ // case 2 - cpu time = 56 - 52 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
auto displayFrame1 = getDisplayFrame(1);
- gpuFence2->signalForTest(66);
- presentFence2->signalForTest(71);
+ gpuFence2->signalForTest(76);
+ presentFence2->signalForTest(90);
EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
// Fences have flushed for first displayFrame, so the present timestamps should be updated
@@ -1592,35 +1602,41 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent)
EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
- addEmptyDisplayFrame();
+ // case 3 - cpu time = 86 - 82 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3);
+ auto displayFrame2 = getDisplayFrame(2);
+ gpuFence3->signalForTest(116);
+ presentFence3->signalForTest(120);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
// Fences have flushed for second displayFrame, so the present timestamps should be updated
- EXPECT_EQ(displayFrame1->getActuals().presentTime, 71);
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 90);
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
-}
-TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
- auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11));
- mFrameTimeline->setSfPresent(36, presentFence1);
- auto displayFrame = getDisplayFrame(0);
- presentFence1->signalForTest(52);
+ // case 4 - cpu time = 86 - 82 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4);
+ auto displayFrame3 = getDisplayFrame(3);
+ gpuFence4->signalForTest(156);
+ presentFence4->signalForTest(180);
- // Fences haven't been flushed yet, so it should be 0
- EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(displayFrame3->getActuals().presentTime, 0);
+ // Fences have flushed for third displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing);
addEmptyDisplayFrame();
- displayFrame = getDisplayFrame(0);
- // Fences have flushed, so the present timestamps should be updated
- EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
- EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart);
- EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
- EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
- EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+ // Fences have flushed for third displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame3->getActuals().presentTime, 180);
+ EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index a1f0588fc7..691676420c 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -23,23 +23,13 @@
#include "AsyncCallRecorder.h"
#include "Scheduler/OneShotTimer.h"
+#include "fake/FakeClock.h"
using namespace std::chrono_literals;
namespace android {
namespace scheduler {
-class FakeClock : public OneShotTimer::Clock {
-public:
- virtual ~FakeClock() = default;
- std::chrono::steady_clock::time_point now() const override { return mNow; }
-
- void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
-
-private:
- std::chrono::steady_clock::time_point mNow;
-};
-
class OneShotTimerTest : public testing::Test {
protected:
OneShotTimerTest() = default;
@@ -58,17 +48,17 @@ protected:
namespace {
TEST_F(OneShotTimerTest, createAndDestroyTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
- "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<FakeClock>(clock));
+ "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<fake::FakeClock>(clock));
}
TEST_F(OneShotTimerTest, startStopTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
@@ -83,11 +73,11 @@ TEST_F(OneShotTimerTest, startStopTest) {
}
TEST_F(OneShotTimerTest, resetTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -105,11 +95,11 @@ TEST_F(OneShotTimerTest, resetTest) {
}
TEST_F(OneShotTimerTest, resetBackToBackTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -139,11 +129,11 @@ TEST_F(OneShotTimerTest, resetBackToBackTest) {
}
TEST_F(OneShotTimerTest, startNotCalledTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
// The start hasn't happened, so the callback does not happen.
EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
@@ -155,11 +145,11 @@ TEST_F(OneShotTimerTest, startNotCalledTest) {
}
TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
clock->advanceTime(2ms);
@@ -180,11 +170,11 @@ TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
}
TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -197,11 +187,11 @@ TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
clock->advanceTime(2ms);
@@ -215,11 +205,11 @@ TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
- FakeClock* clock = new FakeClock();
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
mExpiredTimerCallback.getInvocable(),
- std::unique_ptr<FakeClock>(clock));
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 188ea758d4..ff53a7b09c 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -897,24 +897,24 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) {
EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
- std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "jankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
- expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
- EXPECT_THAT(result, HasSubstr(expectedResult));
+ std::string expectedResult = "totalTimelineFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "jankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfLongCpuJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfLongGpuJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfUnattributedJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "appUnattributedJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfSchedulingJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "sfPredictionErrorJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+ expectedResult = "appBufferStuffingJankyFrames = ";
+ EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
}
TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -1348,6 +1348,30 @@ TEST_F(TimeStatsTest, canSurviveMonkey) {
}
}
+TEST_F(TimeStatsTest, refreshRateIsClampedToNearestBucket) {
+ // this stat is not in the proto so verify by checking the string dump
+ const auto verifyRefreshRateBucket = [&](Fps fps, int32_t bucket) {
+ EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames(
+ {fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0), JankType::None, 0, 0, 0});
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "displayRefreshRate = " + std::to_string(bucket) + " fps";
+ EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
+ };
+
+ verifyRefreshRateBucket(Fps(91.f), 90);
+ verifyRefreshRateBucket(Fps(89.f), 90);
+
+ verifyRefreshRateBucket(Fps(61.f), 60);
+ verifyRefreshRateBucket(Fps(59.f), 60);
+
+ verifyRefreshRateBucket(Fps(31.f), 30);
+ verifyRefreshRateBucket(Fps(29.f), 30);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/fake/FakeClock.h b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
new file mode 100644
index 0000000000..6d9c7646c8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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 "../../Clock.h"
+
+namespace android::fake {
+
+class FakeClock : public Clock {
+public:
+ virtual ~FakeClock() = default;
+ std::chrono::steady_clock::time_point now() const override { return mNow; }
+
+ void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
+
+private:
+ std::chrono::steady_clock::time_point mNow;
+};
+
+} // namespace android::fake \ No newline at end of file
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index eb24a22719..80e9a3c3b1 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -106,42 +106,6 @@ cc_library_static {
],
}
-cc_binary {
- name: "vr_hwc",
- enabled: false,
- system_ext_specific: true,
- vintf_fragments: ["manifest_vr_hwc.xml"],
- srcs: [
- "vr_hardware_composer_service.cpp",
- ],
- static_libs: [
- "libvr_hwc-impl",
- // NOTE: This needs to be included after the *-impl lib otherwise the
- // symbols in the *-binder library get optimized out.
- "libvr_hwc-binder",
- ],
- shared_libs: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.3",
- "libbase",
- "libbinder",
- "liblog",
- "libhardware",
- "libhidlbase",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-Wall",
- "-Werror",
- ],
- init_rc: [
- "vr_hwc.rc",
- ],
-}
-
cc_test {
name: "vr_hwc_test",
gtest: true,
diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml
deleted file mode 100644
index 1068cac33a..0000000000
--- a/services/vr/hardware_composer/manifest_vr_hwc.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
- <hal>
- <name>android.hardware.graphics.composer</name>
- <transport>hwbinder</transport>
- <version>2.1</version>
- <interface>
- <name>IComposer</name>
- <instance>vr</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp
deleted file mode 100644
index 7701847120..0000000000
--- a/services/vr/hardware_composer/vr_hardware_composer_service.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2017 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 <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <hwbinder/IPCThreadState.h>
-#include <impl/vr_hwc.h>
-#include <inttypes.h>
-
-#include "vr_composer.h"
-
-int main() {
- android::ProcessState::self()->startThreadPool();
-
- // Register the hwbinder HWC HAL service used by SurfaceFlinger while in VR
- // mode.
- android::sp<android::dvr::VrHwc> service = new android::dvr::VrHwc();
-
- LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service");
- LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote");
-
- const char instance[] = "vr";
- LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != android::OK,
- "Failed to register service");
-
- android::sp<android::dvr::VrComposer> composer =
- new android::dvr::VrComposer(service.get());
-
- android::sp<android::IServiceManager> sm(android::defaultServiceManager());
-
- // Register the binder service used by VR Window Manager service to receive
- // frame information from VR HWC HAL.
- android::status_t status = sm->addService(
- android::dvr::VrComposer::SERVICE_NAME(), composer.get(),
- false /* allowIsolated */);
- LOG_ALWAYS_FATAL_IF(status != android::OK,
- "VrDisplay service failed to start: %" PRId32, status);
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::hardware::IPCThreadState::self()->joinThreadPool();
-
- return 0;
-}
diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc
deleted file mode 100644
index 645ab807da..0000000000
--- a/services/vr/hardware_composer/vr_hwc.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service vr_hwc /system/bin/vr_hwc
- class hal animation
- user system
- group system graphics
- onrestart restart surfaceflinger
- writepid /dev/cpuset/system-background/tasks
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 67cd8754a3..440c5b144a 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -29,17 +29,14 @@ ndk_library {
unversioned_until: "current",
}
-llndk_library {
- name: "libvulkan.llndk",
- symbol_file: "libvulkan.map.txt",
- export_llndk_headers: [
- "vulkan_headers_llndk",
- ],
-}
-
cc_library_shared {
name: "libvulkan",
- llndk_stubs: "libvulkan.llndk",
+ llndk: {
+ symbol_file: "libvulkan.map.txt",
+ export_llndk_headers: [
+ "vulkan_headers",
+ ],
+ },
clang: true,
sanitize: {
misc_undefined: ["integer"],