Merge "TouchpadInputMapper: rotate multi-finger swipes with display"
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 6c07849..045e61b 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -63,14 +63,10 @@
MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
void record(const MotionEvent& event);
- std::vector<std::unique_ptr<MotionEvent>> predict();
+ std::vector<std::unique_ptr<MotionEvent>> predict(nsecs_t timestamp);
bool isPredictionAvailable(int32_t deviceId, int32_t source);
- void setExpectedPresentationTimeNanos(int64_t expectedPresentationTimeNanos);
private:
- std::mutex mLock;
- int64_t mExpectedPresentationTimeNanos GUARDED_BY(mLock) = 0;
- int64_t getExpectedPresentationTimeNanos();
std::vector<MotionEvent> mEvents;
const nsecs_t mPredictionTimestampOffsetNanos;
const std::function<bool()> mCheckMotionPredictionEnabled;
diff --git a/libs/binder/BinderRecordReplay.cpp b/libs/binder/BinderRecordReplay.cpp
index 90c02a8..9463785 100644
--- a/libs/binder/BinderRecordReplay.cpp
+++ b/libs/binder/BinderRecordReplay.cpp
@@ -16,10 +16,12 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <binder/BinderRecordReplay.h>
#include <algorithm>
using android::Parcel;
+using android::base::borrowed_fd;
using android::base::unique_fd;
using android::binder::debug::RecordedTransaction;
@@ -30,22 +32,78 @@
static_assert(PADDING8(7) == 1);
static_assert(PADDING8(8) == 0);
-// Transactions are sequentially recorded to the file descriptor in the following format:
+// Transactions are sequentially recorded to a file descriptor.
//
-// RecordedTransaction.TransactionHeader (32 bytes)
-// Sent Parcel data (getDataSize() bytes)
-// padding (enough bytes to align the reply Parcel data to 8 bytes)
-// Reply Parcel data (getReplySize() bytes)
-// padding (enough bytes to align the next header to 8 bytes)
-// [repeats with next transaction]
+// An individual RecordedTransaction is written with the following format:
//
-// Warning: This format is non-stable
+// WARNING: Though the following format is designed to be stable and
+// extensible, it is under active development and should be considered
+// unstable until this warning is removed.
+//
+// A RecordedTransaction is written to a file as a sequence of Chunks.
+//
+// A Chunk consists of a ChunkDescriptor, Data, and Padding.
+//
+// Data and Padding may each be zero-length as specified by the
+// ChunkDescriptor.
+//
+// The ChunkDescriptor identifies the type of data in the chunk, the size of
+// the data in bytes, and the number of zero-bytes padding to land on an
+// 8-byte boundary by the end of the Chunk.
+//
+// ┌───────────────────────────┐
+// │Chunk │
+// │┌─────────────────────────┐│
+// ││ChunkDescriptor ││
+// ││┌───────────┬───────────┐││
+// │││chunkType │paddingSize│││
+// │││uint32_t │uint32_t ├┼┼───┐
+// ││├───────────┴───────────┤││ │
+// │││dataSize │││ │
+// │││uint64_t ├┼┼─┐ │
+// ││└───────────────────────┘││ │ │
+// │└─────────────────────────┘│ │ │
+// │┌─────────────────────────┐│ │ │
+// ││Data ││ │ │
+// ││bytes * dataSize │◀─┘ │
+// │└─────────────────────────┘│ │
+// │┌─────────────────────────┐│ │
+// ││Padding ││ │
+// ││bytes * paddingSize │◀───┘
+// │└─────────────────────────┘│
+// └───────────────────────────┘
+//
+// A RecordedTransaction is written as a Header Chunk with fields about the
+// transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk.
+// ┌──────────────────────┐
+// │ Header Chunk │
+// ├──────────────────────┤
+// │ Sent Parcel Chunk │
+// ├──────────────────────┤
+// │ Reply Parcel Chunk │
+// ├──────────────────────┤
+// ║ End Chunk ║
+// ╚══════════════════════╝
+//
+// On reading a RecordedTransaction, an unrecognized chunk is skipped using
+// the size information in the ChunkDescriptor. Chunks are read and either
+// assimilated or skipped until an End Chunk is encountered. This has three
+// notable implications:
+//
+// 1. Older and newer implementations should be able to read one another's
+// Transactions, though there will be loss of information.
+// 2. With the exception of the End Chunk, Chunks can appear in any
+// order and even repeat, though this is not recommended.
+// 3. If any Chunk is repeated, old values will be overwritten by versions
+// encountered later in the file.
+//
+// No effort is made to ensure the expected chunks are present. A single
+// End Chunk may therefore produce a empty, meaningless RecordedTransaction.
RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
- mHeader = {t.getCode(), t.getFlags(), t.getDataSize(),
- t.getReplySize(), t.getReturnedStatus(), t.getVersion()};
- mSent.setData(t.getDataParcel().data(), t.getDataSize());
- mReply.setData(t.getReplyParcel().data(), t.getReplySize());
+ mHeader = {t.getCode(), t.getFlags(), t.getReturnedStatus(), t.getVersion()};
+ mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize());
+ mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize());
}
std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags,
@@ -53,19 +111,15 @@
const Parcel& replyParcel,
status_t err) {
RecordedTransaction t;
- t.mHeader = {code,
- flags,
- static_cast<uint64_t>(dataParcel.dataSize()),
- static_cast<uint64_t>(replyParcel.dataSize()),
- static_cast<int32_t>(err),
+ t.mHeader = {code, flags, static_cast<int32_t>(err),
dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)};
- if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) {
+ if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) {
LOG(INFO) << "Failed to set sent parcel data.";
return std::nullopt;
}
- if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) {
+ if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) {
LOG(INFO) << "Failed to set reply parcel data.";
return std::nullopt;
}
@@ -73,80 +127,154 @@
return std::optional<RecordedTransaction>(std::move(t));
}
+enum {
+ HEADER_CHUNK = 0x00000001,
+ DATA_PARCEL_CHUNK = 0x00000002,
+ REPLY_PARCEL_CHUNK = 0x00000003,
+ INVALID_CHUNK = 0x00fffffe,
+ END_CHUNK = 0x00ffffff,
+};
+
+struct ChunkDescriptor {
+ uint32_t chunkType = 0;
+ uint32_t padding = 0;
+ uint32_t dataSize = 0;
+ uint32_t reserved = 0; // Future checksum
+};
+
+static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut) {
+ if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) {
+ LOG(INFO) << "Failed to read Chunk Descriptor from fd " << fd.get();
+ return android::UNKNOWN_ERROR;
+ }
+ if (PADDING8(chunkOut->dataSize) != chunkOut->padding) {
+ chunkOut->chunkType = INVALID_CHUNK;
+ LOG(INFO) << "Chunk data and padding sizes do not align." << fd.get();
+ return android::BAD_VALUE;
+ }
+ return android::NO_ERROR;
+}
+
std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
RecordedTransaction t;
- if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) {
- LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get();
- return std::nullopt;
- }
- if (t.getVersion() != 0) {
- LOG(INFO) << "File corrupted: transaction version is not 0.";
- return std::nullopt;
- }
+ ChunkDescriptor chunk;
- std::vector<uint8_t> bytes;
- bytes.resize(t.getDataSize());
- if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) {
- LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get();
- return std::nullopt;
- }
- if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) {
- LOG(INFO) << "Failed to set sent parcel data.";
- return std::nullopt;
- }
-
- uint8_t padding[7];
- if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) {
- LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get();
- return std::nullopt;
- }
- if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
- LOG(INFO) << "File corrupted: padding isn't 0.";
- return std::nullopt;
- }
-
- bytes.resize(t.getReplySize());
- if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) {
- LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get();
- return std::nullopt;
- }
- if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) {
- LOG(INFO) << "Failed to set reply parcel data.";
- return std::nullopt;
- }
-
- if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) {
- LOG(INFO) << "Failed to read parcel padding from fd " << fd.get();
- return std::nullopt;
- }
- if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
- LOG(INFO) << "File corrupted: padding isn't 0.";
- return std::nullopt;
- }
+ do {
+ if (NO_ERROR != readChunkDescriptor(fd, &chunk)) {
+ LOG(INFO) << "Failed to read chunk descriptor.";
+ return std::nullopt;
+ }
+ switch (chunk.chunkType) {
+ case HEADER_CHUNK: {
+ if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
+ LOG(INFO) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
+ << sizeof(TransactionHeader) << ".";
+ return std::nullopt;
+ }
+ if (!android::base::ReadFully(fd, &t.mHeader, chunk.dataSize)) {
+ LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get();
+ return std::nullopt;
+ }
+ break;
+ }
+ case DATA_PARCEL_CHUNK: {
+ std::vector<uint8_t> bytes;
+ bytes.resize(chunk.dataSize);
+ if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) {
+ LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get();
+ return std::nullopt;
+ }
+ if (t.mSent.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) {
+ LOG(INFO) << "Failed to set sent parcel data.";
+ return std::nullopt;
+ }
+ lseek(fd.get(), chunk.padding, SEEK_CUR);
+ break;
+ }
+ case REPLY_PARCEL_CHUNK: {
+ std::vector<uint8_t> bytes;
+ bytes.resize(chunk.dataSize);
+ if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) {
+ LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get();
+ return std::nullopt;
+ }
+ if (t.mReply.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) {
+ LOG(INFO) << "Failed to set reply parcel data.";
+ return std::nullopt;
+ }
+ lseek(fd.get(), chunk.padding, SEEK_CUR);
+ break;
+ }
+ case INVALID_CHUNK:
+ LOG(INFO) << "Invalid chunk.";
+ return std::nullopt;
+ case END_CHUNK:
+ LOG(INFO) << "Read end chunk";
+ FALLTHROUGH_INTENDED;
+ default:
+ // Unrecognized or skippable chunk
+ lseek(fd.get(), chunk.dataSize + chunk.padding, SEEK_CUR);
+ break;
+ }
+ } while (chunk.chunkType != END_CHUNK);
return std::optional<RecordedTransaction>(std::move(t));
}
+android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
+ size_t byteCount, const uint8_t* data) const {
+ // Write Chunk Descriptor
+ // - Chunk Type
+ if (!android::base::WriteFully(fd, &chunkType, sizeof(uint32_t))) {
+ LOG(INFO) << "Failed to write chunk header to fd " << fd.get();
+ return UNKNOWN_ERROR;
+ }
+ // - Chunk Data Padding Size
+ uint32_t additionalPaddingCount = static_cast<uint32_t>(PADDING8(byteCount));
+ if (!android::base::WriteFully(fd, &additionalPaddingCount, sizeof(uint32_t))) {
+ LOG(INFO) << "Failed to write chunk padding size to fd " << fd.get();
+ return UNKNOWN_ERROR;
+ }
+ // - Chunk Data Size
+ uint64_t byteCountToWrite = (uint64_t)byteCount;
+ if (!android::base::WriteFully(fd, &byteCountToWrite, sizeof(uint64_t))) {
+ LOG(INFO) << "Failed to write chunk size to fd " << fd.get();
+ return UNKNOWN_ERROR;
+ }
+ if (byteCount == 0) {
+ return NO_ERROR;
+ }
+
+ if (!android::base::WriteFully(fd, data, byteCount)) {
+ LOG(INFO) << "Failed to write chunk data to fd " << fd.get();
+ return UNKNOWN_ERROR;
+ }
+
+ const uint8_t zeros[7] = {0};
+ if (!android::base::WriteFully(fd, zeros, additionalPaddingCount)) {
+ LOG(INFO) << "Failed to write chunk padding to fd " << fd.get();
+ return UNKNOWN_ERROR;
+ }
+ return NO_ERROR;
+}
+
android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
- if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) {
+ if (NO_ERROR !=
+ writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader),
+ reinterpret_cast<const uint8_t*>(&mHeader))) {
LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get();
return UNKNOWN_ERROR;
}
- if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) {
- LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get();
+ if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) {
+ LOG(INFO) << "Failed to write sent Parcel to fd " << fd.get();
return UNKNOWN_ERROR;
}
- const uint8_t zeros[7] = {0};
- if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) {
- LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get();
+ if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) {
+ LOG(INFO) << "Failed to write reply Parcel to fd " << fd.get();
return UNKNOWN_ERROR;
}
- if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) {
- LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get();
- return UNKNOWN_ERROR;
- }
- if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) {
- LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get();
+ if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
+ LOG(INFO) << "Failed to write end chunk to fd " << fd.get();
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -160,14 +288,6 @@
return mHeader.flags;
}
-uint64_t RecordedTransaction::getDataSize() const {
- return mHeader.dataSize;
-}
-
-uint64_t RecordedTransaction::getReplySize() const {
- return mHeader.replySize;
-}
-
int32_t RecordedTransaction::getReturnedStatus() const {
return mHeader.statusReturned;
}
diff --git a/libs/binder/include/binder/BinderRecordReplay.h b/libs/binder/include/binder/BinderRecordReplay.h
index 25ed5e5..609e5be 100644
--- a/libs/binder/include/binder/BinderRecordReplay.h
+++ b/libs/binder/include/binder/BinderRecordReplay.h
@@ -42,8 +42,6 @@
uint32_t getCode() const;
uint32_t getFlags() const;
- uint64_t getDataSize() const;
- uint64_t getReplySize() const;
int32_t getReturnedStatus() const;
uint32_t getVersion() const;
const Parcel& getDataParcel() const;
@@ -52,27 +50,24 @@
private:
RecordedTransaction() = default;
+ android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType,
+ size_t byteCount, const uint8_t* data) const;
+
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wpadded"
struct TransactionHeader {
uint32_t code = 0;
uint32_t flags = 0;
- uint64_t dataSize = 0;
- uint64_t replySize = 0;
int32_t statusReturned = 0;
uint32_t version = 0; // !0 iff Rpc
};
#pragma clang diagnostic pop
- static_assert(sizeof(TransactionHeader) == 32);
+ static_assert(sizeof(TransactionHeader) == 16);
static_assert(sizeof(TransactionHeader) % 8 == 0);
TransactionHeader mHeader;
Parcel mSent;
Parcel mReply;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-private-field"
- uint8_t mReserved[40];
-#pragma clang diagnostic pop
};
} // namespace binder::debug
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 78dae4b..e7943dd 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -112,6 +112,13 @@
LOG(ERROR) << "Failed to get fd for the socket:" << name;
return nullptr;
}
+ // Control socket fds are inherited from init, so they don't have O_CLOEXEC set.
+ // But we don't want any child processes to inherit the socket we are running
+ // the server on, so attempt to set the flag now.
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+ LOG(WARNING) << "Failed to set CLOEXEC on control socket with name " << name
+ << " error: " << errno;
+ }
if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
<< " error: " << statusToString(status).c_str();
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
index 23864e6..67e482d 100644
--- a/libs/binder/tests/binderRecordedTransactionTest.cpp
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -42,8 +42,8 @@
EXPECT_EQ(retrievedTransaction->getCode(), 1);
EXPECT_EQ(retrievedTransaction->getFlags(), 42);
- EXPECT_EQ(retrievedTransaction->getDataSize(), 12);
- EXPECT_EQ(retrievedTransaction->getReplySize(), 4);
+ EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12);
+ EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4);
EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0);
EXPECT_EQ(retrievedTransaction->getVersion(), 0);
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index d32cd80..fa0cc81 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -213,7 +213,8 @@
size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
const uint8_t* buffer = parcelData.data();
- binder_status_t status = AParcel_unmarshal(AParcel_create(), buffer, len);
+ NdkParcelAdapter adapter;
+ binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, len);
FUZZ_LOG() << "status: " << status;
},
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 97e45c6..797d6ae 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -483,20 +483,18 @@
mSyncedFrameNumbers.erase(callbackId.framenumber);
}
-void BLASTBufferQueue::acquireNextBufferLocked(
+status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
// If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
// include the extra buffer when checking if we can acquire the next buffer.
- const bool includeExtraAcquire = !transaction;
- const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire);
- if (mNumFrameAvailable == 0 || maxAcquired) {
- BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired));
- return;
+ if (mNumFrameAvailable == 0) {
+ BQA_LOGV("Can't process next buffer. No available frames");
+ return NOT_ENOUGH_DATA;
}
if (mSurfaceControl == nullptr) {
BQA_LOGE("ERROR : surface control is null");
- return;
+ return NAME_NOT_FOUND;
}
SurfaceComposerClient::Transaction localTransaction;
@@ -513,10 +511,10 @@
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
if (status == BufferQueue::NO_BUFFER_AVAILABLE) {
BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE");
- return;
+ return status;
} else if (status != OK) {
BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
- return;
+ return status;
}
auto buffer = bufferItem.mGraphicBuffer;
@@ -526,7 +524,7 @@
if (buffer == nullptr) {
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
BQA_LOGE("Buffer was empty");
- return;
+ return BAD_VALUE;
}
if (rejectBuffer(bufferItem)) {
@@ -535,8 +533,7 @@
mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
- acquireNextBufferLocked(transaction);
- return;
+ return acquireNextBufferLocked(transaction);
}
mNumAcquired++;
@@ -624,6 +621,7 @@
bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform);
+ return OK;
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -647,32 +645,6 @@
mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
}
-void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
- BBQ_TRACE();
- if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) {
- // We are waiting on a previous sync's transaction callback so allow another sync
- // transaction to proceed.
- //
- // We need to first flush out the transactions that were in between the two syncs.
- // We do this by merging them into mSyncTransaction so any buffer merging will get
- // a release callback invoked. The release callback will be async so we need to wait
- // on max acquired to make sure we have the capacity to acquire another buffer.
- if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting to flush shadow queue...");
- mCallbackCV.wait(lock);
- }
- while (mNumFrameAvailable > 0) {
- // flush out the shadow queue
- acquireAndReleaseBuffer();
- }
- }
-
- while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting for free buffer.");
- mCallbackCV.wait(lock);
- }
-}
-
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
SurfaceComposerClient::Transaction* prevTransaction = nullptr;
@@ -685,7 +657,6 @@
BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
if (syncTransactionSet) {
- bool mayNeedToWaitForBuffer = true;
// If we are going to re-use the same mSyncTransaction, release the buffer that may
// already be set in the Transaction. This is to allow us a free slot early to continue
// processing a new buffer.
@@ -696,14 +667,20 @@
bufferData->frameNumber);
releaseBuffer(bufferData->generateReleaseCallbackId(),
bufferData->acquireFence);
- // Because we just released a buffer, we know there's no need to wait for a free
- // buffer.
- mayNeedToWaitForBuffer = false;
}
}
- if (mayNeedToWaitForBuffer) {
- flushAndWaitForFreeBuffer(_lock);
+ if (waitForTransactionCallback) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mSyncTransaction so any buffer merging will get
+ // a release callback invoked.
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
}
}
@@ -719,7 +696,12 @@
item.mFrameNumber, boolToString(syncTransactionSet));
if (syncTransactionSet) {
- acquireNextBufferLocked(mSyncTransaction);
+ // If there's no available buffer and we're in a sync transaction, we need to wait
+ // instead of returning since we guarantee a buffer will be acquired for the sync.
+ while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) {
+ BQA_LOGD("waiting for available buffer");
+ mCallbackCV.wait(_lock);
+ }
// Only need a commit callback when syncing to ensure the buffer that's synced has been
// sent to SF
@@ -829,15 +811,6 @@
return mSize != bufferSize;
}
-// Check if we have acquired the maximum number of buffers.
-// Consumer can acquire an additional buffer if that buffer is not droppable. Set
-// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
-// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
-bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
- int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
- return mNumAcquired >= maxAcquiredBuffers;
-}
-
class BBQSurface : public Surface {
private:
std::mutex mMutex;
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 47dcc42..001d8e5 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -130,12 +130,11 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer);
- void acquireNextBufferLocked(
+ status_t acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
// Return true if we need to reject the buffer based on the scaling mode and the buffer size.
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
- bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
REQUIRES(mMutex);
@@ -144,7 +143,6 @@
void acquireAndReleaseBuffer() REQUIRES(mMutex);
void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
REQUIRES(mMutex);
- void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
std::string mName;
// Represents the queued buffer count from buffer queue,
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 0719fe5..5e2ffa6 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -55,7 +55,7 @@
* The returned event should not contain any of the real, existing data. It should only
* contain the predicted samples.
*/
-std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict() {
+std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict(nsecs_t timestamp) {
if (mEvents.size() < 2) {
return {};
}
@@ -67,7 +67,7 @@
std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
std::vector<PointerCoords> futureCoords;
- const int64_t futureTime = getExpectedPresentationTimeNanos() + mPredictionTimestampOffsetNanos;
+ const nsecs_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
const nsecs_t currentTime = event.getEventTime();
const MotionEvent& previous = mEvents.rbegin()[1];
const nsecs_t oldTime = previous.getEventTime();
@@ -143,14 +143,4 @@
return true;
}
-int64_t MotionPredictor::getExpectedPresentationTimeNanos() {
- std::scoped_lock lock(mLock);
- return mExpectedPresentationTimeNanos;
-}
-
-void MotionPredictor::setExpectedPresentationTimeNanos(int64_t expectedPresentationTimeNanos) {
- std::scoped_lock lock(mLock);
- mExpectedPresentationTimeNanos = expectedPresentationTimeNanos;
-}
-
} // namespace android
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 0f39055..d2b59a1 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -63,8 +63,7 @@
predictor.record(getMotionEvent(MOVE, 1, 3, 10));
predictor.record(getMotionEvent(MOVE, 2, 5, 20));
predictor.record(getMotionEvent(MOVE, 3, 7, 30));
- predictor.setExpectedPresentationTimeNanos(40);
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict();
+ std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40);
ASSERT_EQ(1u, predicted.size());
ASSERT_EQ(predicted[0]->getX(0), 4);
ASSERT_EQ(predicted[0]->getY(0), 9);
@@ -81,8 +80,7 @@
predictor.record(getMotionEvent(MOVE, 0, 1, 10));
predictor.record(getMotionEvent(MOVE, 0, 1, 20));
predictor.record(getMotionEvent(MOVE, 0, 1, 30));
- predictor.setExpectedPresentationTimeNanos(40);
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict();
+ std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40);
ASSERT_EQ(1u, predicted.size());
ASSERT_EQ(predicted[0]->getX(0), 0);
ASSERT_EQ(predicted[0]->getY(0), 1);
@@ -98,21 +96,19 @@
TEST(MotionPredictorTest, Offset) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
[]() { return true /*enable prediction*/; });
- predictor.setExpectedPresentationTimeNanos(40);
predictor.record(getMotionEvent(DOWN, 0, 1, 30));
predictor.record(getMotionEvent(MOVE, 0, 1, 35));
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict();
+ std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40);
ASSERT_EQ(1u, predicted.size());
ASSERT_GE(predicted[0]->getEventTime(), 41);
}
-TEST(MotionPredictionTest, FlagDisablesPrediction) {
+TEST(MotionPredictorTest, FlagDisablesPrediction) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return false /*disable prediction*/; });
- predictor.setExpectedPresentationTimeNanos(40);
predictor.record(getMotionEvent(DOWN, 0, 1, 30));
predictor.record(getMotionEvent(MOVE, 0, 1, 35));
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict();
+ std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40);
ASSERT_EQ(0u, predicted.size());
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
index 39c79c9..5993546 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -36,11 +36,11 @@
JpegDecoder();
~JpegDecoder();
/*
- * Decompresses JPEG image to raw image (YUV420planer or grey-scale) format. After calling
- * this method, call getDecompressedImage() to get the image.
+ * Decompresses JPEG image to raw image (YUV420planer, grey-scale or RGBA) format. After
+ * calling this method, call getDecompressedImage() to get the image.
* Returns false if decompressing the image fails.
*/
- bool decompressImage(const void* image, int length);
+ bool decompressImage(const void* image, int length, bool decodeToRGBA = false);
/*
* Returns the decompressed raw image buffer pointer. This method must be called only after
* calling decompressImage().
@@ -98,10 +98,11 @@
bool extractEXIF(const void* image, int length);
private:
- bool decode(const void* image, int length);
+ bool decode(const void* image, int length, bool decodeToRGBA);
// Returns false if errors occur.
bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ bool decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest);
bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
// Process 16 lines of Y and 16 lines of U/V each time.
// We must pass at least 16 scanlines according to libjpeg documentation.
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 905bf16..05b1421 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -339,11 +339,11 @@
/*
* This method will tone map a HDR image to an SDR image.
*
- * @param uncompressed_p010_image (input) uncompressed P010 image
+ * @param src (input) uncompressed P010 image
* @param dest (output) tone mapping result as a YUV_420 image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ status_t toneMap(jr_uncompressed_ptr src,
jr_uncompressed_ptr dest);
};
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
index c2a8f45..07b527c 100644
--- a/libs/jpegrecoverymap/jpegdecoder.cpp
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -93,7 +93,7 @@
JpegDecoder::~JpegDecoder() {
}
-bool JpegDecoder::decompressImage(const void* image, int length) {
+bool JpegDecoder::decompressImage(const void* image, int length, bool decodeToRGBA) {
if (image == nullptr || length <= 0) {
ALOGE("Image size can not be handled: %d", length);
return false;
@@ -101,7 +101,7 @@
mResultBuffer.clear();
mXMPBuffer.clear();
- if (!decode(image, length)) {
+ if (!decode(image, length, decodeToRGBA)) {
return false;
}
@@ -140,7 +140,7 @@
return mHeight;
}
-bool JpegDecoder::decode(const void* image, int length) {
+bool JpegDecoder::decode(const void* image, int length, bool decodeToRGBA) {
jpeg_decompress_struct cinfo;
jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
jpegrerror_mgr myerr;
@@ -210,15 +210,26 @@
mWidth = cinfo.image_width;
mHeight = cinfo.image_height;
- if (cinfo.jpeg_color_space == JCS_YCbCr) {
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
- } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ if (decodeToRGBA) {
+ if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ // We don't intend to support decoding grayscale to RGBA
+ return false;
+ }
+ // 4 bytes per pixel
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
+ cinfo.out_color_space = JCS_EXT_RGBA;
+ } else {
+ if (cinfo.jpeg_color_space == JCS_YCbCr) {
+ // 1 byte per pixel for Y, 0.5 byte per pixel for U+V
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
+ } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ }
+ cinfo.out_color_space = cinfo.jpeg_color_space;
+ cinfo.raw_data_out = TRUE;
}
- cinfo.raw_data_out = TRUE;
cinfo.dct_method = JDCT_IFAST;
- cinfo.out_color_space = cinfo.jpeg_color_space;
jpeg_start_decompress(&cinfo);
@@ -292,7 +303,10 @@
if (isSingleChannel) {
return decompressSingleChannel(cinfo, dest);
}
- return decompressYUV(cinfo, dest);
+ if (cinfo->out_color_space == JCS_EXT_RGBA)
+ return decompressRGBA(cinfo, dest);
+ else
+ return decompressYUV(cinfo, dest);
}
bool JpegDecoder::getCompressedImageParameters(const void* image, int length,
@@ -331,6 +345,20 @@
return true;
}
+bool JpegDecoder::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+ JSAMPLE* decodeDst = (JSAMPLE*) dest;
+ uint32_t lines = 0;
+ // TODO: use batches for more effectiveness
+ while (lines < cinfo->image_height) {
+ uint32_t ret = jpeg_read_scanlines(cinfo, &decodeDst, 1);
+ if (ret == 0) {
+ break;
+ }
+ decodeDst += cinfo->image_width * 4;
+ lines++;
+ }
+ return lines == cinfo->image_height;
+}
bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 1b69743..ee68043 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -25,7 +25,6 @@
#include <image_io/jpeg/jpeg_scanner.h>
#include <image_io/jpeg/jpeg_info_builder.h>
#include <image_io/base/data_segment_data_source.h>
-#include <utils/Log.h>
#include <memory>
#include <sstream>
@@ -239,6 +238,9 @@
}
jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
+ uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
+ uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
jpegr_uncompressed_struct map;
@@ -265,7 +267,7 @@
jpeg.length = jpeg_encoder.getCompressedImageSize();
jpegr_exif_struct new_exif;
- if (exif->data == nullptr) {
+ if (exif == nullptr || exif->data == nullptr) {
new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
} else {
new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
@@ -542,23 +544,37 @@
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
// TODO: fill EXIF data
(void) exif;
+ if (request_sdr) {
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
+ true)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+ jpegr_uncompressed_struct uncompressed_rgba_image;
+ uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
+ uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
+ uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
+ memcpy(dest->data, uncompressed_rgba_image.data,
+ uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
+ dest->width = uncompressed_rgba_image.width;
+ dest->height = uncompressed_rgba_image.height;
+ return NO_ERROR;
+ }
+
jpegr_compressed_struct compressed_map;
jpegr_metadata metadata;
JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
-
JpegDecoder jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
JpegDecoder recovery_map_decoder;
- if (!recovery_map_decoder.decompressImage(compressed_map.data,
- compressed_map.length)) {
+ if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
@@ -567,26 +583,17 @@
map.width = recovery_map_decoder.getDecompressedImageWidth();
map.height = recovery_map_decoder.getDecompressedImageHeight();
-
jpegr_uncompressed_struct uncompressed_yuv_420_image;
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
- jpeg_decoder.getXMPSize(), &metadata)) {
+ jpeg_decoder.getXMPSize(), &metadata)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- if (request_sdr) {
- memcpy(dest->data, uncompressed_yuv_420_image.data,
- uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
- dest->width = uncompressed_yuv_420_image.width;
- dest->height = uncompressed_yuv_420_image.height;
- } else {
- JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
- }
-
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
return NO_ERROR;
}
@@ -926,18 +933,39 @@
return NO_ERROR;
}
-status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr src,
jr_uncompressed_ptr dest) {
- if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ if (src == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- dest->width = uncompressed_p010_image->width;
- dest->height = uncompressed_p010_image->height;
- unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
- dest->data = dest_data.get();
+ dest->width = src->width;
+ dest->height = src->height;
- // TODO: Tone map algorighm here.
+ size_t pixel_count = src->width * src->height;
+ for (size_t y = 0; y < src->height; ++y) {
+ for (size_t x = 0; x < src->width; ++x) {
+ size_t pixel_y_idx = x + y * src->width;
+ size_t pixel_uv_idx = x / 2 + (y / 2) * (src->width / 2);
+
+ uint16_t y_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_y_idx]
+ >> 6;
+ uint16_t u_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2]
+ >> 6;
+ uint16_t v_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2 + 1]
+ >> 6;
+
+ uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[pixel_y_idx];
+ uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count + pixel_uv_idx];
+ uint8_t* v = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count * 5 / 4 + pixel_uv_idx];
+
+ *y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
+ *u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
+ *v = static_cast<uint8_t>((v_uint >> 2) & 0xff);
+ }
+ }
+
+ dest->colorGamut = src->colorGamut;
return NO_ERROR;
}
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index c3c6fd4..8ff12fb 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -114,58 +114,57 @@
}
/* Test Encode API-0 and decode */
-// TODO: enable when tonemapper is ready.
-//TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
-// int ret;
-//
-// // Load input files.
-// if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
-// FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
-// }
-// mRawP010Image.width = TEST_IMAGE_WIDTH;
-// mRawP010Image.height = TEST_IMAGE_HEIGHT;
-// mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
-//
-// RecoveryMap recoveryMap;
-//
-// jpegr_compressed_struct jpegR;
-// jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
-// jpegR.data = malloc(jpegR.maxLength);
-// ret = recoveryMap.encodeJPEGR(
-// &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
-// if (ret != OK) {
-// FAIL() << "Error code is " << ret;
-// }
-// if (SAVE_ENCODING_RESULT) {
-// // Output image data to file
-// std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
-// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
-// if (!imageFile.is_open()) {
-// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
-// }
-// imageFile.write((const char*)jpegR.data, jpegR.length);
-// }
-//
-// jpegr_uncompressed_struct decodedJpegR;
-// int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
-// decodedJpegR.data = malloc(decodedJpegRSize);
-// ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
-// if (ret != OK) {
-// FAIL() << "Error code is " << ret;
-// }
-// if (SAVE_DECODING_RESULT) {
-// // Output image data to file
-// std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
-// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
-// if (!imageFile.is_open()) {
-// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
-// }
-// imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
-// }
-//
-// free(jpegR.data);
-// free(decodedJpegR.data);
-//}
+TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
/* Test Encode API-1 and decode */
TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h
index 906d9c6..9fea21e 100644
--- a/libs/nativewindow/include/android/hardware_buffer_aidl.h
+++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h
@@ -83,7 +83,7 @@
class HardwareBuffer {
public:
HardwareBuffer() noexcept {}
- explicit HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {}
+ HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {}
~HardwareBuffer() {
reset();
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index d2c940f..d55ab28 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -197,6 +197,9 @@
// The keyboard layout association has changed.
CHANGE_KEYBOARD_LAYOUT_ASSOCIATION = 1 << 11,
+ // The stylus button reporting configurations has changed.
+ CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -309,6 +312,10 @@
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
+ // True if stylus button reporting through motion events should be enabled, in which case
+ // stylus button state changes are reported through motion events.
+ bool stylusButtonMotionEventsEnabled;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
@@ -329,7 +336,8 @@
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
showTouches(false),
- pointerCaptureRequest() {}
+ pointerCaptureRequest(),
+ stylusButtonMotionEventsEnabled(true) {}
static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 9a7af40..4d51aee 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -88,6 +88,14 @@
return rotatedDisplaySize;
}
+static int32_t filterButtonState(InputReaderConfiguration& config, int32_t buttonState) {
+ if (!config.stylusButtonMotionEventsEnabled) {
+ buttonState &=
+ ~(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY | AMOTION_EVENT_BUTTON_STYLUS_SECONDARY);
+ }
+ return buttonState;
+}
+
// --- RawPointerData ---
void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
@@ -1400,8 +1408,9 @@
next.readTime = readTime;
// Sync button state.
- next.buttonState =
- mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
+ next.buttonState = filterButtonState(mConfig,
+ mTouchButtonAccumulator.getButtonState() |
+ mCursorButtonAccumulator.getButtonState());
// Sync scroll
next.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
@@ -1640,7 +1649,9 @@
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) {
// If any of the external buttons are already pressed by the touch device, ignore them.
- const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons;
+ const int32_t pressedButtons =
+ filterButtonState(mConfig,
+ ~mCurrentRawState.buttonState & mExternalStylusState.buttons);
const int32_t releasedButtons =
mExternalStylusButtonsApplied & ~mExternalStylusState.buttons;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index f755356..bb8a30e 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -201,6 +201,10 @@
mConfig.wheelVelocityControlParameters = params;
}
+void FakeInputReaderPolicy::setStylusButtonMotionEventsEnabled(bool enabled) {
+ mConfig.stylusButtonMotionEventsEnabled = enabled;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 862ff0b..9ec3217 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -75,6 +75,7 @@
float getPointerGestureMovementSpeedRatio();
float getPointerGestureZoomSpeedRatio();
void setVelocityControlParams(const VelocityControlParameters& params);
+ void setStylusButtonMotionEventsEnabled(bool enabled);
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a464639..95d35f4 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2020,6 +2020,56 @@
WithDeviceId(touchscreenId))));
}
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
+ TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
+ TestFixture::mReader->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
+
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+
+ // Start a stylus gesture. By the time this event is processed, the configuration change that
+ // was requested is guaranteed to be completed.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Press and release a stylus button. Each change only generates a MOVE motion event.
+ // Key events are unaffected.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Finish the stylus gesture.
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+}
+
// --- ExternalStylusIntegrationTest ---
// Verify the behavior of an external stylus. An external stylus can report pressure or button
@@ -6577,6 +6627,46 @@
WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
}
+TEST_F(SingleTouchInputMapperTest, StylusButtonMotionEventsDisabled) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+
+ mFakePolicy->setStylusButtonMotionEventsEnabled(false);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Press a stylus button.
+ processKey(mapper, BTN_STYLUS, 1);
+ processSync(mapper);
+
+ // Start a touch gesture and ensure that the stylus button is not reported.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0))));
+
+ // Release and press the stylus button again.
+ processKey(mapper, BTN_STYLUS, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+ processKey(mapper, BTN_STYLUS, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+ // Release the touch gesture.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorrectType) {
mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
prepareDisplay(ui::ROTATION_0);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3dd86aa..b930477 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -440,9 +440,6 @@
property_get("debug.sf.treat_170m_as_sRGB", value, "0");
mTreat170mAsSrgb = atoi(value);
- mIgnoreHwcPhysicalDisplayOrientation =
- base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
-
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
@@ -2445,8 +2442,7 @@
if (!id) {
return ui::ROTATION_0;
}
- if (!mIgnoreHwcPhysicalDisplayOrientation &&
- getHwComposer().getComposer()->isSupported(
+ if (getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
case Hwc2::AidlTransform::ROT_90:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 23ac19e..7bb0514 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -311,11 +311,6 @@
// on this behavior to increase contrast for some media sources.
bool mTreat170mAsSrgb = false;
- // Allows to ignore physical orientation provided through hwc API in favour of
- // 'ro.surface_flinger.primary_display_orientation'.
- // TODO(b/246793311): Clean up a temporary property
- bool mIgnoreHwcPhysicalDisplayOrientation = false;
-
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();