Merge "Ensure onHandleDestroyed drops reference before releasing lock."
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 19c2830..53b3a00 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -228,6 +228,10 @@
{ OPT, "events/kmem/rss_stat/enable" },
{ OPT, "events/kmem/ion_heap_grow/enable" },
{ OPT, "events/kmem/ion_heap_shrink/enable" },
+ { OPT, "events/oom/oom_score_adj_update/enable" },
+ { OPT, "events/sched/sched_process_exit/enable" },
+ { OPT, "events/task/task_rename/enable" },
+ { OPT, "events/task/task_newtask/enable" },
} },
};
@@ -435,14 +439,10 @@
const char* path = category.sysfiles[i].path;
bool req = category.sysfiles[i].required == REQ;
if (path != nullptr) {
- if (req) {
- if (!fileIsWritable(path)) {
- return false;
- } else {
- ok = true;
- }
- } else {
+ if (fileIsWritable(path)) {
ok = true;
+ } else if (req) {
+ return false;
}
}
}
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index ed9531e..fa7d908 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -131,7 +131,7 @@
* POSIX error code (see errno.h) if an immediate error occurs.
*/
int android_res_nsend(net_handle_t network,
- const unsigned char *msg, int msglen) __INTRODUCED_IN(29);
+ const uint8_t *msg, size_t msglen) __INTRODUCED_IN(29);
/**
* Read a result for the query associated with the |fd| descriptor.
@@ -141,7 +141,7 @@
* >= 0: length of |answer|. |rcode| is the resolver return code (e.g., ns_r_nxdomain)
*/
int android_res_nresult(int fd,
- int *rcode, unsigned char *answer, int anslen) __INTRODUCED_IN(29);
+ int *rcode, uint8_t *answer, size_t anslen) __INTRODUCED_IN(29);
/**
* Attempts to cancel the in-progress query associated with the |nsend_fd|
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 95b1038..1be55e6 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -59,7 +59,15 @@
if (err != NO_ERROR) {
return err;
}
- err = output->writeInt64(presentTime);
+ if (presentFence) {
+ err = output->writeBool(true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->write(*presentFence);
+ } else {
+ err = output->writeBool(false);
+ }
if (err != NO_ERROR) {
return err;
}
@@ -71,10 +79,18 @@
if (err != NO_ERROR) {
return err;
}
- err = input->readInt64(&presentTime);
+ bool hasFence = false;
+ err = input->readBool(&hasFence);
if (err != NO_ERROR) {
return err;
}
+ if (hasFence) {
+ presentFence = new Fence();
+ err = input->read(*presentFence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
return input->readParcelableVector(&surfaceStats);
}
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 5c41c21..8acfa7a 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -21,6 +21,7 @@
#include <binder/Parcelable.h>
#include <binder/SafeInterface.h>
+#include <ui/Fence.h>
#include <utils/Timers.h>
#include <cstdint>
@@ -65,7 +66,7 @@
status_t readFromParcel(const Parcel* input) override;
nsecs_t latchTime = -1;
- nsecs_t presentTime = -1;
+ sp<Fence> presentFence = nullptr;
std::vector<SurfaceStats> surfaceStats;
};
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 86e9c23..60542bd 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,11 +24,15 @@
#include <memory>
+#include <android/native_window.h>
+
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
@@ -37,6 +41,7 @@
#include <input/InputTransport.h>
#include <input/Input.h>
+#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -44,6 +49,8 @@
namespace android {
namespace test {
+using Transaction = SurfaceComposerClient::Transaction;
+
sp<IInputFlinger> getInputFlinger() {
sp<IBinder> input(defaultServiceManager()->getService(
String16("inputflinger")));
@@ -58,9 +65,8 @@
class InputSurface {
public:
- InputSurface(const sp<SurfaceComposerClient>& scc, int width, int height) {
- mSurfaceControl = scc->createSurface(String8("Test Surface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor);
+ InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
+ mSurfaceControl = sc;
InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
mServerChannel->setToken(new BBinder());
@@ -73,6 +79,31 @@
mInputConsumer = new InputConsumer(mClientChannel);
}
+ static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
+ int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
+ static std::unique_ptr<InputSurface> makeBufferInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Buffer Surface"), width, height,
+ PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
+ static std::unique_ptr<InputSurface> makeContainerInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
InputEvent* consumeEvent() {
waitForEventAvailable();
@@ -180,6 +211,15 @@
void SetUp() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ DisplayInfo info;
+ auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+
+ // After a new buffer is queued, SurfaceFlinger is notified and will
+ // latch the new buffer on next vsync. Let's heuristically wait for 3
+ // vsyncs.
+ mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
}
void TearDown() {
@@ -187,10 +227,23 @@
}
std::unique_ptr<InputSurface> makeSurface(int width, int height) {
- return std::make_unique<InputSurface>(mComposerClient, width, height);
+ return InputSurface::makeColorInputSurface(mComposerClient, width, height);
+ }
+
+ void postBuffer(const sp<SurfaceControl> &layer) {
+ // wait for previous transactions (such as setSize) to complete
+ Transaction().apply(true);
+ ANativeWindow_Buffer buffer = {};
+ EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+ // Request an empty transaction to get applied synchronously to ensure the buffer is
+ // latched.
+ Transaction().apply(true);
+ usleep(mBufferPostDelay);
}
sp<SurfaceComposerClient> mComposerClient;
+ int32_t mBufferPostDelay;
};
void injectTap(int x, int y) {
@@ -267,5 +320,124 @@
surface->expectTap(1, 1);
}
+// Surface Insets are set to offset the client content and draw a border around the client surface
+// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
+// of the client content.
+TEST_F(InputSurfacesTest, input_respects_surface_insets) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ bgSurface->showAt(100, 100);
+
+ fgSurface->mInputInfo.surfaceInset = 5;
+ fgSurface->showAt(100, 100);
+
+ injectTap(106, 106);
+ fgSurface->expectTap(1, 1);
+
+ injectTap(101, 101);
+ bgSurface->expectTap(1, 1);
+}
+
+// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
+TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
+ parentSurface->showAt(100, 100);
+
+ childSurface->mInputInfo.surfaceInset = 10;
+ childSurface->showAt(100, 100);
+
+ childSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setPosition(sc, -5, -5);
+ t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+ });
+
+ injectTap(106, 106);
+ childSurface->expectTap(1, 1);
+
+ injectTap(101, 101);
+ parentSurface->expectTap(1, 1);
+}
+
+// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
+TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([](auto &t, auto &sc) {
+ Region transparentRegion(Rect(0, 0, 10, 10));
+ t.setTransparentRegionHint(sc, transparentRegion);
+ });
+ surface->showAt(100, 100);
+ injectTap(101, 101);
+ surface->expectTap(1, 1);
+}
+
+// Ensure we send the input to the right surface when the surface visibility changes due to the
+// first buffer being submitted. ref: b/120839715
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+
+ bgSurface->showAt(10, 10);
+ bufferSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+
+ postBuffer(bufferSurface->mSurfaceControl);
+ injectTap(11, 11);
+ bufferSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+ postBuffer(bufferSurface->mSurfaceControl);
+
+ bgSurface->showAt(10, 10);
+ bufferSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ bufferSurface->expectTap(1, 1);
+
+ bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+
+ bgSurface->showAt(10, 10);
+ fgSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ fgSurface->expectTap(1, 1);
+
+ fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> containerSurface =
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+
+ bgSurface->showAt(10, 10);
+ containerSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ containerSurface->expectTap(1, 1);
+
+ containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+}
}
}
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 226b6ee..0582e1a 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -169,6 +169,13 @@
buffer_state_ = &metadata_header->buffer_state;
fence_state_ = &metadata_header->fence_state;
active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask;
+ // The C++ standard recommends (but does not require) that lock-free atomic operations are
+ // also address-free, that is, suitable for communication between processes using shared
+ // memory.
+ LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) ||
+ !std::atomic_is_lock_free(fence_state_) ||
+ !std::atomic_is_lock_free(active_clients_bit_mask_),
+ "Atomic variables in ashmen are not lock free.");
// Import the buffer: We only need to hold on the native_handle_t here so that
// GraphicBuffer instance can be created in future.
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index b0869fe..2663812 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -39,7 +39,8 @@
BufferHubIdGenerator::getInstance().getId());
if (node == nullptr || !node->IsValid()) {
ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
- _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::ALLOCATION_FAILED);
+ _hidl_cb(/*status=*/BufferHubStatus::ALLOCATION_FAILED, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
return Void();
}
@@ -48,7 +49,13 @@
std::lock_guard<std::mutex> lock(mClientSetMutex);
mClientSet.emplace(client);
- _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
+ BufferTraits bufferTraits = {/*bufferDesc=*/description,
+ /*bufferHandle=*/hidl_handle(node->buffer_handle()),
+ // TODO(b/116681016): return real data to client
+ /*bufferInfo=*/hidl_handle()};
+
+ _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
+ /*bufferTraits=*/bufferTraits);
return Void();
}
@@ -56,7 +63,8 @@
importBuffer_cb _hidl_cb) {
if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts != 1) {
// nullptr handle or wrong format
- _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+ _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
return Void();
}
@@ -68,7 +76,8 @@
auto iter = mTokenMap.find(token);
if (iter == mTokenMap.end()) {
// Invalid token
- _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+ _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
return Void();
}
@@ -81,15 +90,37 @@
if (!originClient) {
// Should not happen since token should be removed if already gone
ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
- _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::BUFFER_FREED);
+ _hidl_cb(/*status=*/BufferHubStatus::BUFFER_FREED, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
return Void();
}
sp<BufferClient> client = new BufferClient(*originClient);
+ uint32_t clientStateMask = client->getBufferNode()->AddNewActiveClientsBitToMask();
+ if (clientStateMask == 0U) {
+ // Reach max client count
+ ALOGE("%s: import failed, BufferNode#%u reached maximum clients.", __FUNCTION__,
+ client->getBufferNode()->id());
+ _hidl_cb(/*status=*/BufferHubStatus::MAX_CLIENT, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
std::lock_guard<std::mutex> lock(mClientSetMutex);
mClientSet.emplace(client);
- _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
+
+ std::shared_ptr<BufferNode> node = client->getBufferNode();
+
+ HardwareBufferDescription bufferDesc;
+ memcpy(&bufferDesc, &node->buffer_desc(), sizeof(HardwareBufferDescription));
+
+ BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
+ /*bufferHandle=*/hidl_handle(node->buffer_handle()),
+ // TODO(b/116681016): return real data to client
+ /*bufferInfo=*/hidl_handle()};
+
+ _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
+ /*bufferTraits=*/bufferTraits);
return Void();
}
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
index cc87e15..da19a6f 100644
--- a/services/bufferhub/BufferNode.cpp
+++ b/services/bufferhub/BufferNode.cpp
@@ -2,6 +2,7 @@
#include <bufferhub/BufferHubService.h>
#include <bufferhub/BufferNode.h>
+#include <log/log.h>
#include <ui/GraphicBufferAllocator.h>
namespace android {
@@ -18,6 +19,13 @@
fence_state_ = new (&metadata_header->fence_state) std::atomic<uint32_t>(0);
active_clients_bit_mask_ =
new (&metadata_header->active_clients_bit_mask) std::atomic<uint32_t>(0);
+ // The C++ standard recommends (but does not require) that lock-free atomic operations are
+ // also address-free, that is, suitable for communication between processes using shared
+ // memory.
+ LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) ||
+ !std::atomic_is_lock_free(fence_state_) ||
+ !std::atomic_is_lock_free(active_clients_bit_mask_),
+ "Atomic variables in ashmen are not lock free.");
}
// Allocates a new BufferNode.
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 7f5d3a6..66ed4bd 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -49,6 +49,9 @@
Return<BufferHubStatus> close() override;
Return<void> duplicate(duplicate_cb _hidl_cb) override;
+ // Non-binder functions
+ const std::shared_ptr<BufferNode>& getBufferNode() const { return mBufferNode; }
+
private:
BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
: mService(service), mBufferNode(node) {}
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index a025f31..f7802b9 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -73,6 +73,8 @@
static const char *WAKE_LOCK_ID = "KeyEvents";
static const char *DEVICE_PATH = "/dev/input";
+// v4l2 devices go directly into /dev
+static const char *VIDEO_DEVICE_PATH = "/dev";
static inline const char* toString(bool value) {
return value ? "true" : "false";
@@ -100,6 +102,13 @@
}
}
+/**
+ * Return true if name matches "v4l-touch*"
+ */
+static bool isV4lTouchNode(const char* name) {
+ return strstr(name, "v4l-touch") == name;
+}
+
// --- Global Functions ---
uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
@@ -206,18 +215,21 @@
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
- LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
mINotifyFd = inotify_init();
- int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
- LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
- DEVICE_PATH, errno);
+ mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
+ LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s",
+ DEVICE_PATH, strerror(errno));
+ mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
+ LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
+ VIDEO_DEVICE_PATH, strerror(errno));
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.fd = mINotifyFd;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
+ int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
@@ -1667,8 +1679,6 @@
status_t EventHub::readNotifyLocked() {
int res;
- char devname[PATH_MAX];
- char *filename;
char event_buf[512];
int event_size;
int event_pos = 0;
@@ -1682,21 +1692,27 @@
ALOGW("could not get event, %s\n", strerror(errno));
return -1;
}
- //printf("got %d bytes of event information\n", res);
-
- strcpy(devname, DEVICE_PATH);
- filename = devname + strlen(devname);
- *filename++ = '/';
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
if(event->len) {
- strcpy(filename, event->name);
- if(event->mask & IN_CREATE) {
- openDeviceLocked(devname);
- } else {
- ALOGI("Removing device '%s' due to inotify event\n", devname);
- closeDeviceByPathLocked(devname);
+ if (event->wd == mInputWd) {
+ std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+ if(event->mask & IN_CREATE) {
+ openDeviceLocked(filename.c_str());
+ } else {
+ ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
+ closeDeviceByPathLocked(filename.c_str());
+ }
+ }
+ else if (event->wd == mVideoWd) {
+ if (isV4lTouchNode(event->name)) {
+ std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+ ALOGV("Received an inotify event for a video device %s", filename.c_str());
+ }
+ }
+ else {
+ LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
}
}
event_size = sizeof(*event) + event->len;
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 2d342bc..73fcb11 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -254,7 +254,6 @@
}
-
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
@@ -3446,7 +3445,17 @@
} else {
viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
}
- return mConfig.getDisplayViewportByType(viewportTypeToUse);
+
+ std::optional<DisplayViewport> viewport =
+ mConfig.getDisplayViewportByType(viewportTypeToUse);
+ if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+ ALOGW("Input device %s should be associated with external display, "
+ "fallback to internal one for the external viewport is not found.",
+ getDeviceName().c_str());
+ viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ }
+
+ return viewport;
}
DisplayViewport newViewport;
diff --git a/services/inputflinger/include/EventHub.h b/services/inputflinger/include/EventHub.h
index 5db7c0a..1ddb978 100644
--- a/services/inputflinger/include/EventHub.h
+++ b/services/inputflinger/include/EventHub.h
@@ -452,6 +452,9 @@
int mWakeReadPipeFd;
int mWakeWritePipeFd;
+ int mInputWd;
+ int mVideoWd;
+
// Epoll FD list size hint.
static const int EPOLL_SIZE_HINT = 8;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 167a624..b5d2090 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6284,4 +6284,34 @@
ASSERT_EQ(DISPLAY_ID, args.displayId);
}
+/**
+ * Expect fallback to internal viewport if device is external and external viewport is not present.
+ */
+TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ mDevice->setExternal(true);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+
+ NotifyMotionArgs motionArgs;
+
+ // Expect the event to be sent to the internal viewport,
+ // because an external viewport is not present.
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
+
+ // Expect the event to be sent to the external viewport if it is present.
+ prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4537d23..4a93be6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2736,7 +2736,7 @@
commitTransaction();
- if ((inputChanged || mVisibleRegionsDirty) && mInputFlinger) {
+ if (inputChanged || mVisibleRegionsDirty) {
updateInputWindows();
}
@@ -2746,6 +2746,10 @@
void SurfaceFlinger::updateInputWindows() {
ATRACE_CALL();
+ if (mInputFlinger == nullptr) {
+ return;
+ }
+
Vector<InputWindowInfo> inputHandles;
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
@@ -3015,6 +3019,11 @@
mVisibleRegionsDirty |= visibleRegions;
+ if (visibleRegions) {
+ // Update input window info if the layer receives its first buffer.
+ updateInputWindows();
+ }
+
// If we will need to wake up at some time in the future to deal with a
// queued frame that shouldn't be displayed during this vsync period, wake
// up during the next vsync period to check again.
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 389118a..a1a8692 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -151,18 +151,6 @@
while (mKeepRunning) {
mConditionVariable.wait(mMutex);
- // Present fence should fire almost immediately. If the fence has not signaled in 100ms,
- // there is a major problem and it will probably never fire.
- nsecs_t presentTime = -1;
- if (mPresentFence) {
- status_t status = mPresentFence->wait(100);
- if (status == NO_ERROR) {
- presentTime = mPresentFence->getSignalTime();
- } else {
- ALOGE("present fence has not signaled, err %d", status);
- }
- }
-
// We should never hit this case. The release fences from the previous frame should have
// signaled long before the current frame is presented.
for (const auto& fence : mPreviousReleaseFences) {
@@ -188,17 +176,11 @@
// If the transaction has been latched
if (transactionStats.latchTime >= 0) {
- // If the present time is < 0, this transaction has been latched but not
- // presented. Skip it for now. This can happen when a new transaction comes
- // in between the latch and present steps. sendCallbacks is called by
- // SurfaceFlinger when the transaction is received to ensure that if the
- // transaction that didn't update state it still got a callback.
- if (presentTime < 0) {
+ if (!mPresentFence) {
sendCallback = false;
break;
}
-
- transactionStats.presentTime = presentTime;
+ transactionStats.presentFence = mPresentFence;
}
}
// If the listener has no pending transactions and all latched transactions have been
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index cef598c..e62fc6e 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2502,12 +2502,12 @@
}
void verifyTransactionStats(const TransactionStats& transactionStats) const {
- const auto& [latchTime, presentTime, surfaceStats] = transactionStats;
+ const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
ASSERT_GE(latchTime, 0) << "bad latch time";
- ASSERT_GE(presentTime, 0) << "bad present time";
+ ASSERT_NE(presentFence, nullptr);
} else {
- ASSERT_EQ(presentTime, -1) << "transaction shouldn't have been presented";
+ ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
}