Merge "binder: optimize OnewayCallQueueing"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ce3d669..794750f 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1917,10 +1917,11 @@
// Open the reference profile if needed.
UniqueFile reference_profile = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
-
- if (reference_profile.fd() == -1) {
- // We don't create an app image without reference profile since there is no speedup from
- // loading it in that case and instead will be a small overhead.
+ struct stat sbuf;
+ if (reference_profile.fd() == -1 ||
+ (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+ // We don't create an app image with empty or non existing reference profile since there
+ // is no speedup from loading it in that case and instead will be a small overhead.
generate_app_image = false;
}
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6a3120c..bf2c0d1 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -308,7 +308,7 @@
// This is different from the normal installd. We only do the base
// directory, the rest will be created on demand when each app is compiled.
if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
- LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+ PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
return false;
}
@@ -460,7 +460,7 @@
// this tool will wipe the OTA artifact cache and try again (for robustness after
// a failed OTA with remaining cache artifacts).
if (access(apk_path, F_OK) != 0) {
- LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+ PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
return true;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..1b7acab 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
namespace android {
namespace installd {
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -82,6 +86,27 @@
}
}
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+ for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+ const char* fstype = kTryMountFsTypes[i];
+ int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+ if (mount_result == 0) {
+ return true;
+ }
+ if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+ // Only try the next fstype if mounting failed due to the current one
+ // being invalid.
+ LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype << " - trying " << kTryMountFsTypes[i + 1];
+ } else {
+ PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype;
+ return false;
+ }
+ }
+ __builtin_unreachable();
+}
+
static void TryExtraMount(const char* name, const char* slot, const char* target) {
std::string partition_name = StringPrintf("%s%s", name, slot);
@@ -91,12 +116,7 @@
if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
std::string path;
if (dm.GetDmDevicePathByName(partition_name, &path)) {
- int mount_result = mount(path.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- if (mount_result == 0) {
+ if (TryMountWithFstypes(path.c_str(), target)) {
return;
}
}
@@ -105,12 +125,7 @@
// Fall back and attempt a direct mount.
std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
- int mount_result = mount(block_device.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(mount_result);
+ (void)TryMountWithFstypes(block_device.c_str(), target);
}
// Entry for otapreopt_chroot. Expected parameters are:
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
i=0
while ((i<MAXIMUM_PACKAGES)) ; do
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+
DEXOPT_PARAMS=$(cmd otadexopt next)
/system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
PROGRESS=$(cmd otadexopt progress)
print -u${STATUS_FD} "global_progress $PROGRESS"
- DONE=$(cmd otadexopt done)
- if [ "$DONE" = "OTA incomplete." ] ; then
- sleep 1
- i=$((i+1))
- continue
- fi
- break
+ sleep 1
+ i=$((i+1))
done
DONE=$(cmd otadexopt done)
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 3b589dc..5c4e1a4 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -185,7 +185,7 @@
std::optional<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
- std::string empty_dm_file_;
+ std::string dm_file_;
std::string app_apk_dir_;
std::string app_private_dir_ce_;
std::string app_private_dir_de_;
@@ -248,26 +248,6 @@
<< " : " << error_msg;
}
- // Create an empty dm file.
- empty_dm_file_ = apk_path_ + ".dm";
- {
- int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_;
- }
- FILE* file = fdopen(fd, "wb");
- if (file == nullptr) {
- return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_
- << " fd=" << fd;
- }
- ZipWriter writer(file);
- // Add vdex to zip.
- writer.StartEntry("primary.prof", ZipWriter::kCompress);
- writer.FinishEntry();
- writer.Finish();
- fclose(file);
- }
-
// Create the app user data.
binder::Status status = service_->createAppData(
volume_uuid_,
@@ -316,6 +296,46 @@
<< secondary_dex_de_ << " : " << error_msg;
}
+ // Create a non-empty dm file.
+ dm_file_ = apk_path_ + ".dm";
+ {
+ android::base::unique_fd fd(open(dm_file_.c_str(),
+ O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
+ if (fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Could not open " << dm_file_;
+ }
+ FILE* file = fdopen(fd.release(), "wb");
+ if (file == nullptr) {
+ return ::testing::AssertionFailure() << "Null file for " << dm_file_
+ << " fd=" << fd.get();
+ }
+
+ // Create a profile file.
+ std::string profile_file = app_private_dir_ce_ + "/primary.prof";
+ run_cmd("profman --generate-test-profile=" + profile_file);
+
+ // Add profile to zip.
+ ZipWriter writer(file);
+ writer.StartEntry("primary.prof", ZipWriter::kCompress);
+ android::base::unique_fd profile_fd(open(profile_file.c_str(), O_RDONLY));
+ if (profile_fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Failed to open profile '"
+ << profile_file << "'";
+ }
+ std::string profile_content;
+ if (!android::base::ReadFdToString(profile_fd, &profile_content)) {
+ return ::testing::AssertionFailure() << "Failed to read profile "
+ << profile_file << "'";
+ }
+ writer.WriteBytes(profile_content.c_str(), profile_content.length());
+ writer.FinishEntry();
+ writer.Finish();
+ fclose(file);
+
+ // Delete the temp file.
+ unlink(profile_file.c_str());
+ }
+
// Fix app data uid.
status = service_->fixupAppData(volume_uuid_, kTestUserId);
if (!status.isOk()) {
@@ -608,7 +628,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_,
@@ -657,13 +677,13 @@
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC |
DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC);
CompilePrimaryDexOk("speed-profile",
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE);
}
};
@@ -787,7 +807,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
@@ -799,7 +819,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
@@ -811,7 +831,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptBlockPrimary) {
@@ -874,7 +894,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -893,7 +913,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -926,7 +946,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
// Enable the property and use dex2oat64.
ASSERT_TRUE(android::base::SetProperty(property, "true")) << property;
CompilePrimaryDexOk("speed-profile",
@@ -936,7 +956,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
class PrimaryDexReCompilationTest : public DexoptTest {
@@ -1143,7 +1163,7 @@
service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL,
kTestAppId, profile_name, apk_path_,
has_dex_metadata ? std::make_optional<std::string>(
- empty_dm_file_)
+ dm_file_)
: std::nullopt,
&result));
ASSERT_EQ(expected_result, result);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index eadf678..bec262e 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -223,8 +223,13 @@
#endif // !VENDORSERVICEMANAGER
ServiceManager::Service::~Service() {
- if (!hasClients) {
- // only expected to happen on process death
+ if (hasClients) {
+ // only expected to happen on process death, we don't store the service
+ // name this late (it's in the map that holds this service), but if it
+ // is happening, we might want to change 'unlinkToDeath' to explicitly
+ // clear this bit so that we can abort in other cases, where it would
+ // mean inconsistent logic in servicemanager (unexpected and tested, but
+ // the original lazy service impl here had that bug).
LOG(WARNING) << "a service was removed when there are clients";
}
}
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 51b97165..ef58ed3 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -127,8 +127,7 @@
t.mData.mInterfaceName = std::string(String8(interfaceName).string());
if (interfaceName.size() != t.mData.mInterfaceName.size()) {
LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
- "utf-8: "
- << interfaceName;
+ "utf-8.";
return std::nullopt;
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 233a8e4..fbad0f7 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -20,6 +20,7 @@
#include <dlfcn.h>
#include <inttypes.h>
+#include <netinet/tcp.h>
#include <poll.h>
#include <unistd.h>
@@ -608,6 +609,18 @@
return -savedErrno;
}
+ if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) {
+ int noDelay = 1;
+ int result =
+ setsockopt(serverFd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay));
+ if (result < 0) {
+ int savedErrno = errno;
+ ALOGE("Could not set TCP_NODELAY on %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return -savedErrno;
+ }
+ }
+
RpcTransportFd transportFd(std::move(serverFd));
if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 2b0e5ba..38bd081 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -1036,8 +1036,8 @@
return DEAD_OBJECT;
}
- if (it->second.asyncTodo.size() == 0) return OK;
- if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+ if (it->second.asyncTodo.size() != 0 &&
+ it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
it->second.asyncNumber, addr);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 1488400..07b38d7 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -119,5 +119,10 @@
{
"name": "memunreachable_binder_test"
}
+ ],
+ "imports": [
+ {
+ "path": "packages/modules/Virtualization"
+ }
]
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index e454a03..9f54087 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -129,7 +129,7 @@
static std::string allocateSocketAddress() {
static size_t id = 0;
std::string temp = getenv("TMPDIR") ?: "/tmp";
- auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+ auto ret = temp + "/binderRpcTest_" + std::to_string(getpid()) + "_" + std::to_string(id++);
unlink(ret.c_str());
return ret;
};
@@ -237,9 +237,13 @@
std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
if (singleThreaded) {
ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
}
if (noKernel) {
ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
}
return ret;
}
@@ -435,8 +439,7 @@
for (auto& t : ts) t.join();
}
-static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs = 500) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -462,7 +465,7 @@
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = kNumThreads + 3;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -475,7 +478,7 @@
constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
auto proc = createRpcTestSocketServerProcess(
{.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadingStressTest) {
@@ -483,9 +486,9 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumClientThreads = 10;
- constexpr size_t kNumServerThreads = 10;
- constexpr size_t kNumCalls = 100;
+ constexpr size_t kNumClientThreads = 5;
+ constexpr size_t kNumServerThreads = 5;
+ constexpr size_t kNumCalls = 50;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index 84abbac..63b56a3 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -45,9 +45,13 @@
std::to_string(serverVersion);
if (singleThreaded) {
ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
}
if (noKernel) {
ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
}
return ret;
}
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
#define LOG_TAG "Surface"
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
namespace android {
namespace view {
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel) {
+ int value;
+ int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+ if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+ ALOGE("Error: ANativeWindow is not backed by Surface");
+ return STATUS_BAD_VALUE;
+ }
+ // Use a android::view::Surface to parcelize the window
+ android::view::Surface shimSurface;
+ shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+ shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+ return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+ const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ // Use a android::view::Surface to unparcel the window
+ android::view::Surface shimSurface;
+ status_t ret = shimSurface.readFromParcel(parcel);
+ if (ret != OK) {
+ ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+ auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+ shimSurface.surfaceControlHandle);
+ ANativeWindow* anw = surface.get();
+ ANativeWindow_acquire(anw);
+ *outWindow = anw;
+ return STATUS_OK;
+}
+
+#endif
+
status_t Surface::writeToParcel(Parcel* parcel) const {
return writeToParcel(parcel, false);
}
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 3762e66..73a05fc 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -24,14 +24,49 @@
#include <private/android/AHardwareBufferHelpers.h>
+#include <android/binder_libbinder.h>
+#include <dlfcn.h>
#include <log/log.h>
#include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/view/Surface.h>
-#include <android/binder_libbinder.h>
using namespace android;
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+ status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+ android_view_Surface_writeToParcel write = nullptr;
+ android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+ static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+ SurfaceParcelables ret;
+ void* dl = dlopen("libgui.so", RTLD_NOW);
+ LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+ ret.write =
+ (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.write,
+ "libgui.so missing android_view_Surface_writeToParcel; "
+ "loaded wrong libgui?");
+ ret.read =
+ (android_view_Surface_readFromParcel)dlsym(dl,
+ "android_view_Surface_readFromParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.read,
+ "libgui.so missing android_view_Surface_readFromParcel; "
+ "loaded wrong libgui?");
+ return ret;
+ }();
+ return &funcs;
+}
+
static int32_t query(ANativeWindow* window, int what) {
int value;
int res = window->query(window, what, &value);
@@ -64,13 +99,6 @@
return false;
}
}
-static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getIGraphicBufferProducer(window);
-}
-
-static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getSurfaceControlHandle(window);
-}
/**************************************************************************************************
* NDK
@@ -348,38 +376,24 @@
binder_status_t ANativeWindow_readFromParcel(
const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
- const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
-
- // Use a android::view::Surface to unparcel the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- status_t ret = shimSurface->readFromParcel(nativeParcel);
- if (ret != OK) {
- ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->read == nullptr) {
+ ALOGE("Failed to load Surface_readFromParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- sp<Surface> surface = sp<Surface>::make(
- shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle);
- ANativeWindow* anw = surface.get();
- ANativeWindow_acquire(anw);
- *outWindow = anw;
- return STATUS_OK;
+ const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return funcs->read(nativeParcel, outWindow);
}
binder_status_t ANativeWindow_writeToParcel(
ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
- int value;
- int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
- if (err != OK || value != NATIVE_WINDOW_SURFACE) {
- ALOGE("Error: ANativeWindow is not backed by Surface");
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->write == nullptr) {
+ ALOGE("Failed to load Surface_writeToParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- // Use a android::view::Surface to parcelize the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window);
- shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window);
-
Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
- return shimSurface->writeToParcel(nativeParcel);
+ return funcs->write(window, nativeParcel);
}
/**************************************************************************************************
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index eb97a68..96164c0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -107,6 +107,8 @@
constexpr int LOGTAG_INPUT_FOCUS = 62001;
constexpr int LOGTAG_INPUT_CANCEL = 62003;
+const ui::Transform kIdentityTransform;
+
inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -459,8 +461,8 @@
}
// Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
- bool isStylus) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
+ bool isStylus, const ui::Transform& displayTransform) {
const auto inputConfig = windowInfo.inputConfig;
if (windowInfo.displayId != displayId ||
inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
@@ -470,7 +472,17 @@
if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
return false;
}
- if (!windowInfo.touchableRegionContainsPoint(x, y)) {
+
+ // Window Manager works in the logical display coordinate space. When it specifies bounds for a
+ // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside
+ // the window. Points on the right and bottom edges should not be inside the window, so we need
+ // to be careful about performing a hit test when the display is rotated, since the "right" and
+ // "bottom" of the window will be different in the display (un-rotated) space compared to in the
+ // logical display in which WM determined the bounds. Perform the hit test in the logical
+ // display space to ensure these edges are considered correctly in all orientations.
+ const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
+ const auto p = displayTransform.transform(x, y);
+ if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
return false;
}
return true;
@@ -928,10 +940,8 @@
// the touch into the other window.
if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
int32_t displayId = motionEntry.displayId;
- int32_t x = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const float x = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> touchedWindowHandle =
@@ -1058,8 +1068,8 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, TouchState* touchState,
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,
+ TouchState* touchState,
bool isStylus,
bool addOutsideTargets,
bool ignoreDragWindow) {
@@ -1074,7 +1084,8 @@
}
const WindowInfo& info = *windowHandle->getInfo();
- if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!info.isSpy() &&
+ windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
return windowHandle;
}
@@ -1088,14 +1099,14 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
+ int32_t displayId, float x, float y, bool isStylus) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
- if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
continue;
}
if (!info.isSpy()) {
@@ -2057,16 +2068,16 @@
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
- int32_t x;
- int32_t y;
+ float x;
+ float y;
const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
// Always dispatch mouse events to cursor position.
if (isFromMouse) {
- x = int32_t(entry.xCursorPosition);
- y = int32_t(entry.yCursorPosition);
+ x = entry.xCursorPosition;
+ y = entry.yCursorPosition;
} else {
- x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
- y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ x = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X);
+ y = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y);
}
const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2075,8 +2086,7 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
- displayId);
+ ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
@@ -2123,7 +2133,8 @@
}
if (newTouchedWindows.empty()) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
+ ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
+ "%d.",
x, y, displayId);
injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
@@ -2157,7 +2168,8 @@
computeTouchOcclusionInfoLocked(windowHandle, x, y);
if (!isTouchTrustedLocked(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
- ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x,
+ y);
for (const auto& log : occlusionInfo.debugInfo) {
ALOGD("%s", log.c_str());
}
@@ -2218,8 +2230,8 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
- const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const float x = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> oldTouchedWindowHandle =
@@ -2541,8 +2553,8 @@
}
const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
- const int32_t x = entry.pointerCoords[pointerIndex].getX();
- const int32_t y = entry.pointerCoords[pointerIndex].getY();
+ const float x = entry.pointerCoords[pointerIndex].getX();
+ const float y = entry.pointerCoords[pointerIndex].getY();
switch (maskedAction) {
case AMOTION_EVENT_ACTION_MOVE: {
@@ -4582,6 +4594,12 @@
return getWindowHandleLocked(focusedToken, displayId);
}
+ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const {
+ auto displayInfoIt = mDisplayInfos.find(displayId);
+ return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
+ : kIdentityTransform;
+}
+
bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
const bool noInputChannel =
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ed89ed0..24e7432 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -240,11 +240,11 @@
std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
+ int32_t displayId, float x, float y, TouchState* touchState, bool isStylus = false,
bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
+ int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
REQUIRES(mLock);
@@ -370,6 +370,7 @@
int32_t displayId) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
+ ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
// Same function as above, but faster. Since displayId is provided, this avoids the need
// to loop through all displays.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index fce0f99..bcef62c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2481,8 +2481,7 @@
public:
void SetUp() override {
InputDispatcherTest::SetUp();
- mDisplayInfos.clear();
- mWindowInfos.clear();
+ removeAllWindowsAndDisplays();
}
void addDisplayInfo(int displayId, const ui::Transform& transform) {
@@ -2498,6 +2497,11 @@
mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
}
+ void removeAllWindowsAndDisplays() {
+ mDisplayInfos.clear();
+ mWindowInfos.clear();
+ }
+
// Set up a test scenario where the display has a scaled projection and there are two windows
// on the display.
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
@@ -2529,11 +2533,11 @@
std::vector<gui::WindowInfo> mWindowInfos;
};
-TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestCoordinateSpaceConsistency) {
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
// Send down to the first window. The point is represented in the display space. The point is
- // selected so that if the hit test was done with the transform applied to it, then it would
- // end up in the incorrect window.
+ // selected so that if the hit test was performed with the point and the bounds being in
+ // different coordinate spaces, the event would end up in the incorrect window.
NotifyMotionArgs downMotionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
@@ -2608,6 +2612,78 @@
EXPECT_EQ(80, event->getY(0));
}
+/** Ensure consistent behavior of InputDispatcher in all orientations. */
+class InputDispatcherDisplayOrientationFixture
+ : public InputDispatcherDisplayProjectionTest,
+ public ::testing::WithParamInterface<ui::Rotation> {};
+
+// This test verifies the touchable region of a window for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) {
+ constexpr static int32_t displayWidth = 400;
+ constexpr static int32_t displayHeight = 800;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ const auto rotation = GetParam();
+
+ // Set up the display with the specified rotation.
+ const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+ const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+ const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+ const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+ logicalDisplayWidth, logicalDisplayHeight);
+ addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+ // Create a window with its bounds determined in the logical display.
+ const Rect frameInLogicalDisplay(100, 100, 200, 300);
+ const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(frameInDisplay, displayTransform);
+ addWindow(window);
+
+ // The following points in logical display space should be inside the window.
+ static const std::array<vec2, 4> insidePoints{
+ {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+ for (const auto pointInsideWindow : insidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+ window->consumeMotionDown();
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ window->consumeMotionUp();
+ }
+
+ // The following points in logical display space should be outside the window.
+ static const std::array<vec2, 5> outsidePoints{
+ {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+ for (const auto pointOutsideWindow : outsidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ }
+ window->assertNoEvents();
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
+ InputDispatcherDisplayOrientationFixture,
+ ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+ ui::ROTATION_270));
+
using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
sp<IBinder>, sp<IBinder>)>;