Merge "Add BufferReleaseChannel" into main
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index ef2fa4d..fa7cb64 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -505,8 +505,9 @@
return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services.");
}
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
if (binder == nullptr) {
@@ -888,8 +889,9 @@
}
auto ctx = mAccess->getCallingContext();
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
auto serviceIt = mNameToService.find(name);
@@ -1051,8 +1053,9 @@
}
auto ctx = mAccess->getCallingContext();
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
auto serviceIt = mNameToService.find(name);
@@ -1110,6 +1113,23 @@
return Status::ok();
}
+Status ServiceManager::canAddService(const Access::CallingContext& ctx, const std::string& name,
+ std::optional<std::string>* accessor) {
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied for service.");
+ }
+#ifndef VENDORSERVICEMANAGER
+ *accessor = getVintfAccessorName(name);
+#endif
+ if (accessor->has_value()) {
+ if (!mAccess->canAdd(ctx, accessor->value())) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "SELinux denied for the accessor of the service.");
+ }
+ }
+ return Status::ok();
+}
+
Status ServiceManager::canFindService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor) {
if (!mAccess->canFind(ctx, name)) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 0d666c6..c92141b 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -115,6 +115,8 @@
os::Service tryGetService(const std::string& name, bool startIfNotFound);
sp<IBinder> tryGetBinder(const std::string& name, bool startIfNotFound);
+ binder::Status canAddService(const Access::CallingContext& ctx, const std::string& name,
+ std::optional<std::string>* accessor);
binder::Status canFindService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor);
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index ae8de5f..65c2914 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -17,6 +17,7 @@
#pragma once
#include <input/InputTransport.h>
+#include <input/Resampler.h>
#include <utils/Looper.h>
namespace android {
@@ -47,13 +48,13 @@
/**
* Consumes input events from an input channel.
*
- * This is a re-implementation of InputConsumer that does not have resampling at the current moment.
- * A lot of the higher-level logic has been folded into this class, to make it easier to use.
- * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer,
- * as well as various actions like adding the fd to the Choreographer.
+ * This is a re-implementation of InputConsumer. At the moment it only supports resampling for
+ * single pointer events. A lot of the higher-level logic has been folded into this class, to make
+ * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled
+ * in the jni layer, as well as various actions like adding the fd to the Choreographer.
*
* TODO(b/297226446): use this instead of "InputConsumer":
- * - Add resampling to this class
+ * - Add resampling for multiple pointer events.
* - Allow various resampling strategies to be specified
* - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer".
* - Add tracing
@@ -64,8 +65,18 @@
*/
class InputConsumerNoResampling final {
public:
+ /**
+ * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever
+ * the event is ready to consume.
+ * @param looper needs to be sp and not shared_ptr because it inherits from
+ * RefBase
+ * @param resampler the resampling strategy to use. If null, no resampling will be
+ * performed.
+ */
explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper, InputConsumerCallbacks& callbacks);
+ sp<Looper> looper, InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler);
+
~InputConsumerNoResampling();
/**
@@ -99,6 +110,7 @@
std::shared_ptr<InputChannel> mChannel;
sp<Looper> mLooper;
InputConsumerCallbacks& mCallbacks;
+ std::unique_ptr<Resampler> mResampler;
// Looper-related infrastructure
/**
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
new file mode 100644
index 0000000..ff9c4b0
--- /dev/null
+++ b/include/input/Resampler.h
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <optional>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/RingBuffer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Resampler is an interface for resampling MotionEvents. Every resampling implementation
+ * must use this interface to enable resampling inside InputConsumer's logic.
+ */
+struct Resampler {
+ virtual ~Resampler() = default;
+
+ /**
+ * Tries to resample motionEvent at resampleTime. The provided resampleTime must be greater than
+ * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
+ * resampleTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
+ * may be resampled by another method, or not resampled at all. Furthermore, it is the
+ * implementer's responsibility to guarantee the following:
+ * - If resampling occurs, a single additional sample should be added to motionEvent. That is,
+ * if motionEvent had N samples before being passed to Resampler, then it will have N + 1
+ * samples by the end of the resampling. No other field of motionEvent should be modified.
+ * - If resampling does not occur, then motionEvent must not be modified in any way.
+ */
+ virtual void resampleMotionEvent(const std::chrono::nanoseconds resampleTime,
+ MotionEvent& motionEvent,
+ const InputMessage* futureSample) = 0;
+};
+
+class LegacyResampler final : public Resampler {
+public:
+ /**
+ * Tries to resample `motionEvent` at `resampleTime` by adding a resampled sample at the end of
+ * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by
+ * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if
+ * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
+ * not null, interpolation will occur. If `futureSample` is null and there is enough historical
+ * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
+ * `motionEvent` is unmodified.
+ */
+ void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) override;
+
+private:
+ struct Pointer {
+ PointerProperties properties;
+ PointerCoords coords;
+ };
+
+ struct Sample {
+ std::chrono::nanoseconds eventTime;
+ Pointer pointer;
+
+ Sample(const std::chrono::nanoseconds eventTime, const PointerProperties& properties,
+ const PointerCoords& coords)
+ : eventTime{eventTime}, pointer{properties, coords} {}
+ };
+
+ /**
+ * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous
+ * and the current deviceId.
+ */
+ std::optional<DeviceId> mPreviousDeviceId;
+
+ /**
+ * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called.
+ * Note: We store up to two samples in order to simplify the implementation. Although,
+ * calculations are possible with only one previous sample.
+ */
+ RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
+
+ /**
+ * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. (If
+ * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
+ * added to mLatestSamples.)
+ */
+ void updateLatestSamples(const MotionEvent& motionEvent);
+
+ /**
+ * May add a sample at the end of motionEvent with eventTime equal to resampleTime, and
+ * interpolated coordinates between the latest motionEvent sample and futureSample.
+ */
+ void interpolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage& futureSample) const;
+
+ /**
+ * May add a sample at the end of motionEvent by extrapolating from the latest two samples. The
+ * added sample either has eventTime equal to resampleTime, or an earlier time if resampleTime
+ * is too far in the future.
+ */
+ void extrapolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent) const;
+};
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index c57c9cd..53bd08d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -143,6 +143,22 @@
return reply.readNullableStrongBinder(out);
}
+status_t IBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->addFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
+status_t IBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->removeFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
status_t IBinder::getDebugPid(pid_t* out) {
BBinder* local = this->localBinder();
if (local != nullptr) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 6594aa6..eae844c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -160,11 +160,12 @@
// ---------------------------------------------------------------------------
-sp<BpBinder> BpBinder::create(int32_t handle) {
+sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) {
if constexpr (!kEnableKernelIpc) {
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
return nullptr;
}
+ LOG_ALWAYS_FATAL_IF(postTask == nullptr, "BAD STATE");
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
@@ -183,7 +184,11 @@
ALOGE("Still too many binder proxy objects sent to uid %d from uid %d (%d proxies "
"held)",
getuid(), trackedUid, trackedValue);
- if (sLimitCallback) sLimitCallback(trackedUid);
+
+ if (sLimitCallback) {
+ *postTask = [=]() { sLimitCallback(trackedUid); };
+ }
+
sLastLimitCallbackMap[trackedUid] = trackedValue;
}
} else {
@@ -197,7 +202,11 @@
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
- if (sLimitCallback) sLimitCallback(trackedUid);
+
+ if (sLimitCallback) {
+ *postTask = [=]() { sLimitCallback(trackedUid); };
+ }
+
sLastLimitCallbackMap[trackedUid] = trackedValue & COUNTING_VALUE_MASK;
if (sBinderProxyThrottleCreate) {
ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
@@ -557,6 +566,123 @@
}
}
+status_t BpBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "addFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+ LOG_ALWAYS_FATAL_IF(ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0,
+ "addFrozenStateChangeCallback on %s but there are no threads "
+ "(yet?) listening to incoming transactions. See "
+ "ProcessState::startThreadPool "
+ "and ProcessState::setThreadPoolMaxThreadCount. Generally you should "
+ "setup the binder threadpool before other initialization steps.",
+ String8(getInterfaceDescriptor()).c_str());
+ LOG_ALWAYS_FATAL_IF(callback == nullptr,
+ "addFrozenStateChangeCallback(): callback must be non-NULL");
+
+ const sp<FrozenStateChangeCallback> strongCallback = callback.promote();
+ if (strongCallback == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ ALOGV("Requesting freeze notification: %p handle %d\n", this, binderHandle());
+ IPCThreadState* self = IPCThreadState::self();
+ status_t status = self->addFrozenStateChangeCallback(binderHandle(), this);
+ if (status != NO_ERROR) {
+ // Avoids logspam if kernel does not support freeze
+ // notification.
+ if (status != INVALID_OPERATION) {
+ ALOGE("IPCThreadState.addFrozenStateChangeCallback "
+ "failed with %s. %p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ return status;
+ }
+ mFrozen = std::make_unique<FrozenStateChange>();
+ if (!mFrozen) {
+ std::ignore =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ return NO_MEMORY;
+ }
+ }
+ if (mFrozen->initialStateReceived) {
+ strongCallback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ mFrozen->isFrozen
+ ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ }
+ ssize_t res = mFrozen->callbacks.add(callback);
+ if (res < 0) {
+ return res;
+ }
+ return NO_ERROR;
+ }
+}
+
+status_t BpBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "removeFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ RpcMutexUniqueLock _l(mLock);
+
+ const size_t N = mFrozen ? mFrozen->callbacks.size() : 0;
+ for (size_t i = 0; i < N; i++) {
+ if (mFrozen->callbacks.itemAt(i) == callback) {
+ mFrozen->callbacks.removeAt(i);
+ if (mFrozen->callbacks.size() == 0) {
+ ALOGV("Clearing freeze notification: %p handle %d\n", this, binderHandle());
+ status_t status =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ if (status != NO_ERROR) {
+ ALOGE("Unexpected error from "
+ "IPCThreadState.removeFrozenStateChangeCallback: %s. "
+ "%p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ mFrozen.reset();
+ }
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+void BpBinder::onFrozenStateChanged(bool isFrozen) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(), "onFrozenStateChanged is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ ALOGV("Sending frozen state change notification for proxy %p handle %d, isFrozen=%s\n", this,
+ binderHandle(), isFrozen ? "true" : "false");
+
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ return;
+ }
+ bool stateChanged = !mFrozen->initialStateReceived || mFrozen->isFrozen != isFrozen;
+ if (stateChanged) {
+ mFrozen->isFrozen = isFrozen;
+ mFrozen->initialStateReceived = true;
+ for (size_t i = 0; i < mFrozen->callbacks.size();) {
+ sp<FrozenStateChangeCallback> callback = mFrozen->callbacks.itemAt(i).promote();
+ if (callback != nullptr) {
+ callback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ isFrozen ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ i++;
+ } else {
+ mFrozen->callbacks.removeItemsAt(i);
+ }
+ }
+ }
+}
+
void BpBinder::reportOneDeath(const Obituary& obit)
{
sp<DeathRecipient> recipient = obit.recipient.promote();
@@ -686,6 +812,10 @@
if (ipc) ipc->clearDeathNotification(binderHandle(), this);
mObituaries = nullptr;
}
+ if (mFrozen != nullptr) {
+ std::ignore = IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), this);
+ mFrozen.reset();
+ }
mLock.unlock();
if (obits != nullptr) {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 984c93d..1d26d85 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -89,26 +89,33 @@
"BR_FROZEN_REPLY",
"BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_PENDING_FROZEN",
+ "BR_FROZEN_BINDER",
+ "BR_CLEAR_FREEZE_NOTIFICATION_DONE",
};
-static const char *kCommandStrings[] = {
- "BC_TRANSACTION",
- "BC_REPLY",
- "BC_ACQUIRE_RESULT",
- "BC_FREE_BUFFER",
- "BC_INCREFS",
- "BC_ACQUIRE",
- "BC_RELEASE",
- "BC_DECREFS",
- "BC_INCREFS_DONE",
- "BC_ACQUIRE_DONE",
- "BC_ATTEMPT_ACQUIRE",
- "BC_REGISTER_LOOPER",
- "BC_ENTER_LOOPER",
- "BC_EXIT_LOOPER",
- "BC_REQUEST_DEATH_NOTIFICATION",
- "BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+static const char* kCommandStrings[] = {
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
+ "BC_REQUEST_FREEZE_NOTIFICATION",
+ "BC_CLEAR_FREEZE_NOTIFICATION",
+ "BC_FREEZE_NOTIFICATION_DONE",
};
static const int64_t kWorkSourcePropagatedBitIndex = 32;
@@ -203,6 +210,18 @@
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BR_FROZEN_BINDER: {
+ const int32_t c = *cmd++;
+ const int32_t h = *cmd++;
+ const int32_t isFrozen = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c << " isFrozen: " << isFrozen;
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BR_OK, BR_DEAD_REPLY,
// BR_TRANSACTION_COMPLETE, BR_FINISHED
@@ -270,11 +289,23 @@
out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")";
} break;
+ case BC_REQUEST_FREEZE_NOTIFICATION:
+ case BC_CLEAR_FREEZE_NOTIFICATION: {
+ const int32_t h = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": handle=" << h << " (freeze cookie " << (void*)(uint64_t)c << ")";
+ } break;
+
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BC_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER,
// BC_EXIT_LOOPER
@@ -953,6 +984,33 @@
return NO_ERROR;
}
+status_t IPCThreadState::addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ proxy->getWeakRefs()->incWeak(proxy);
+ mOut.writeInt32(BC_REQUEST_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
+status_t IPCThreadState::removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ mOut.writeInt32(BC_CLEAR_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mServingStackPointer(nullptr),
@@ -1487,6 +1545,26 @@
proxy->getWeakRefs()->decWeak(proxy);
} break;
+ case BR_FROZEN_BINDER: {
+ const struct binder_frozen_state_info* data =
+ reinterpret_cast<const struct binder_frozen_state_info*>(
+ mIn.readInplace(sizeof(struct binder_frozen_state_info)));
+ if (data == nullptr) {
+ result = UNKNOWN_ERROR;
+ break;
+ }
+ BpBinder* proxy = (BpBinder*)data->cookie;
+ bool isFrozen = mIn.readInt32() > 0;
+ proxy->getPrivateAccessor().onFrozenStateChanged(data->is_frozen);
+ mOut.writeInt32(BC_FREEZE_NOTIFICATION_DONE);
+ mOut.writePointer(data->cookie);
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ BpBinder* proxy = (BpBinder*)mIn.readPointer();
+ proxy->getWeakRefs()->decWeak(proxy);
+ } break;
+
case BR_FINISHED:
result = TIMED_OUT;
break;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a42ede2..5e7f151 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -24,7 +24,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Stability.h>
-#include <cutils/atomic.h>
#include <utils/AndroidThreads.h>
#include <utils/String8.h>
#include <utils/Thread.h>
@@ -57,6 +56,25 @@
// -------------------------------------------------------------------------
+namespace {
+bool readDriverFeatureFile(const char* filename) {
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
+ char on;
+ if (fd == -1) {
+ ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, filename, strerror(errno));
+ return false;
+ }
+ if (read(fd, &on, sizeof(on)) == -1) {
+ ALOGE("%s: error reading to %s: %s", __func__, filename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return on == '1';
+}
+
+} // namespace
+
namespace android {
using namespace android::binder::impl;
@@ -311,6 +329,7 @@
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
+ std::function<void()> postTask;
std::unique_lock<std::mutex> _l(mLock);
@@ -358,7 +377,7 @@
return nullptr;
}
- sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
+ sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle, &postTask);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
@@ -371,6 +390,10 @@
}
}
+ _l.unlock();
+
+ if (postTask) postTask();
+
return result;
}
@@ -387,7 +410,7 @@
}
String8 ProcessState::makeBinderThreadName() {
- int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ int32_t s = mThreadPoolSeq.fetch_add(1, std::memory_order_release);
pid_t pid = getpid();
std::string_view driverName = mDriverName.c_str();
@@ -429,8 +452,17 @@
}
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
+ // Need to read `mKernelStartedThreads` before `mThreadPoolStarted` (with
+ // non-relaxed memory ordering) to avoid a race like the following:
+ //
+ // thread A: if (mThreadPoolStarted) { // evaluates false
+ // thread B: mThreadPoolStarted = true;
+ // thread B: mKernelStartedThreads++;
+ // thread A: size_t kernelStarted = mKernelStartedThreads;
+ // thread A: LOG_ALWAYS_FATAL_IF(kernelStarted != 0, ...);
+ size_t kernelStarted = mKernelStartedThreads;
+
if (mThreadPoolStarted) {
- size_t kernelStarted = mKernelStartedThreads;
size_t max = mMaxThreads;
size_t current = mCurrentThreads;
@@ -460,7 +492,6 @@
// must not be initialized or maybe has poll thread setup, we
// currently don't track this in libbinder
- size_t kernelStarted = mKernelStartedThreads;
LOG_ALWAYS_FATAL_IF(kernelStarted != 0, "Expecting 0 kernel started threads but have %zu",
kernelStarted);
return mCurrentThreads;
@@ -472,27 +503,20 @@
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
- static const char* const names[] = {
- [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
- DRIVER_FEATURES_PATH "oneway_spam_detection",
- [static_cast<int>(DriverFeature::EXTENDED_ERROR)] =
- DRIVER_FEATURES_PATH "extended_error",
- };
- int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
- char on;
- if (fd == -1) {
- ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- return false;
+ // Use static variable to cache the results.
+ if (feature == DriverFeature::ONEWAY_SPAM_DETECTION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "oneway_spam_detection");
+ return enabled;
}
- if (read(fd, &on, sizeof(on)) == -1) {
- ALOGE("%s: error reading to %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- close(fd);
- return false;
+ if (feature == DriverFeature::EXTENDED_ERROR) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "extended_error");
+ return enabled;
}
- close(fd);
- return on == '1';
+ if (feature == DriverFeature::FREEZE_NOTIFICATION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "freeze_notification");
+ return enabled;
+ }
+ return false;
}
status_t ProcessState::enableOnewaySpamDetection(bool enable) {
@@ -577,7 +601,7 @@
#ifdef __ANDROID__
LOG_ALWAYS_FATAL_IF(!opened.ok(),
"Binder driver '%s' could not be opened. Error: %s. Terminating.",
- error.c_str(), driver);
+ driver, error.c_str());
#endif
if (opened.ok()) {
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index b3a2d9e..65cdcd7 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,4 +32,34 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
+struct binder_frozen_state_info {
+ binder_uintptr_t cookie;
+ __u32 is_frozen;
+};
+
+#ifndef BR_FROZEN_BINDER
+// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it.
+#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info)
+#endif // BR_FROZEN_BINDER
+
+#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t)
+#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE
+
+#ifndef BC_REQUEST_FREEZE_NOTIFICATION
+// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie)
+#endif // BC_REQUEST_FREEZE_NOTIFICATION
+
+#ifndef BC_CLEAR_FREEZE_NOTIFICATION
+// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie)
+#endif // BC_CLEAR_FREEZE_NOTIFICATION
+
+#ifndef BC_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t)
+#endif // BC_FREEZE_NOTIFICATION_DONE
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index d7f74c4..7518044 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
// ---------------------------------------------------------------------------
namespace android {
+class IPCThreadState;
class RpcSession;
class RpcState;
namespace internal {
@@ -66,6 +67,12 @@
void* cookie = nullptr, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
void* cleanupCookie,
object_cleanup_func func) final;
@@ -75,7 +82,6 @@
LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID,
IBinder::object_make_func make,
const void* makeArgs);
-
LIBBINDER_EXPORTED virtual BpBinder* remoteBinder();
LIBBINDER_EXPORTED void sendObituary();
@@ -132,9 +138,14 @@
friend class ::android::ProcessState;
friend class ::android::RpcSession;
friend class ::android::RpcState;
- explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {}
+ friend class ::android::IPCThreadState;
+ explicit PrivateAccessor(const BpBinder* binder)
+ : mBinder(binder), mMutableBinder(nullptr) {}
+ explicit PrivateAccessor(BpBinder* binder) : mBinder(binder), mMutableBinder(binder) {}
- static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }
+ static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask) {
+ return BpBinder::create(handle, postTask);
+ }
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) {
return BpBinder::create(session, address);
}
@@ -146,17 +157,22 @@
uint64_t rpcAddress() const { return mBinder->rpcAddress(); }
const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
+ void onFrozenStateChanged(bool isFrozen) { mMutableBinder->onFrozenStateChanged(isFrozen); }
const BpBinder* mBinder;
+ BpBinder* mMutableBinder;
};
+
LIBBINDER_EXPORTED const PrivateAccessor getPrivateAccessor() const {
return PrivateAccessor(this);
}
+ PrivateAccessor getPrivateAccessor() { return PrivateAccessor(this); }
+
private:
friend PrivateAccessor;
friend class sp<BpBinder>;
- static sp<BpBinder> create(int32_t handle);
+ static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask);
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address);
struct BinderHandle {
@@ -192,6 +208,14 @@
uint32_t flags;
};
+ void onFrozenStateChanged(bool isFrozen);
+
+ struct FrozenStateChange {
+ bool isFrozen = false;
+ Vector<wp<FrozenStateChangeCallback>> callbacks;
+ bool initialStateReceived = false;
+ };
+
void reportOneDeath(const Obituary& obit);
bool isDescriptorCached() const;
@@ -199,6 +223,7 @@
volatile int32_t mAlive;
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
+ std::unique_ptr<FrozenStateChange> mFrozen;
ObjectManager mObjects;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 4eb1c08..1ed7c91 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -202,9 +202,18 @@
virtual void binderDied(const wp<IBinder>& who) = 0;
};
- #if defined(__clang__)
- #pragma clang diagnostic pop
- #endif
+ class FrozenStateChangeCallback : public virtual RefBase {
+ public:
+ enum class State {
+ FROZEN,
+ UNFROZEN,
+ };
+ virtual void onStateChanged(const wp<IBinder>& who, State state) = 0;
+ };
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
/**
* Register the @a recipient for a notification if this binder
@@ -253,6 +262,48 @@
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr) = 0;
+ /**
+ * addFrozenStateChangeCallback provides a callback mechanism to notify
+ * about process frozen/unfrozen events. Upon registration and any
+ * subsequent state changes, the callback is invoked with the latest process
+ * frozen state.
+ *
+ * If the listener process (the one using this API) is itself frozen, state
+ * change events might be combined into a single one with the latest state.
+ * (meaning 'frozen, unfrozen' might just be 'unfrozen'). This single event
+ * would then be delivered when the listener process becomes unfrozen.
+ * Similarly, if an event happens before the previous event is consumed,
+ * they might be combined. This means the callback might not be called for
+ * every single state change, so don't rely on this API to count how many
+ * times the state has changed.
+ *
+ * @note When all references to the binder are dropped, the callback is
+ * automatically removed. So, you must hold onto a binder in order to
+ * receive notifications about it.
+ *
+ * @note You will only receive freeze notifications for remote binders, as
+ * local binders by definition can't be frozen without you being frozen as
+ * well. Trying to use this function on a local binder will result in an
+ * INVALID_OPERATION code being returned and nothing happening.
+ *
+ * @note This binder always holds a weak reference to the callback.
+ *
+ * @note You will only receive a weak reference to the binder object. You
+ * should not try to promote this to a strong reference. (Nor should you
+ * need to, as there is nothing useful you can directly do with it now that
+ * it has passed on.)
+ */
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
+ /**
+ * Remove a previously registered freeze callback.
+ * The @a callback will no longer be called if this object
+ * changes its frozen state.
+ */
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
virtual bool checkSubclass(const void* subclassID) const;
typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 09ab442..9ef4e69 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -174,6 +174,8 @@
LIBBINDER_EXPORTED static void expungeHandle(int32_t handle, IBinder* binder);
LIBBINDER_EXPORTED status_t requestDeathNotification(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED status_t clearDeathNotification(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED static void shutdown();
@@ -210,13 +212,14 @@
IPCThreadState();
~IPCThreadState();
- status_t sendReply(const Parcel& reply, uint32_t flags);
- status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
- status_t talkWithDriver(bool doReceive = true);
- status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code,
- const Parcel& data, status_t* statusBuffer);
- status_t getAndExecuteCommand();
- status_t executeCommand(int32_t command);
+ [[nodiscard]] status_t sendReply(const Parcel& reply, uint32_t flags);
+ [[nodiscard]] status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
+ [[nodiscard]] status_t talkWithDriver(bool doReceive = true);
+ [[nodiscard]] status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle,
+ uint32_t code, const Parcel& data,
+ status_t* statusBuffer);
+ [[nodiscard]] status_t getAndExecuteCommand();
+ [[nodiscard]] status_t executeCommand(int32_t command);
void processPendingDerefs();
void processPostWriteDerefs();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 021bd58..21bfd42 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -133,6 +133,7 @@
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
+ FREEZE_NOTIFICATION,
};
// Determine whether a feature is supported by the binder driver.
LIBBINDER_EXPORTED static bool isDriverFeatureEnabled(const DriverFeature feature);
@@ -188,8 +189,8 @@
Vector<handle_entry> mHandleToObject;
bool mForked;
- bool mThreadPoolStarted;
- volatile int32_t mThreadPoolSeq;
+ std::atomic_bool mThreadPoolStarted;
+ std::atomic_int32_t mThreadPoolSeq;
CallRestriction mCallRestriction;
};
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 33dfe19..7f70396 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -1333,7 +1333,7 @@
let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
- let u16s = [u16::max_value(), 12_345, 42, 117];
+ let u16s = [u16::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1348,7 +1348,7 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::MAX
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
@@ -1361,9 +1361,9 @@
let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u16::MAX, 12_345, 42, 117]);
- let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+ let i16s = [i16::MAX, i16::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1378,8 +1378,8 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::MAX
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::MIN
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
@@ -1391,9 +1391,9 @@
let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+ assert_eq!(vec, [i16::MAX, i16::MIN, 42, -117]);
- let u32s = [u32::max_value(), 12_345, 42, 117];
+ let u32s = [u32::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1408,7 +1408,7 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::MAX
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
@@ -1421,9 +1421,9 @@
let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u32::MAX, 12_345, 42, 117]);
- let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+ let i32s = [i32::MAX, i32::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1438,8 +1438,8 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::MAX
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::MIN
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
@@ -1451,9 +1451,9 @@
let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+ assert_eq!(vec, [i32::MAX, i32::MIN, 42, -117]);
- let u64s = [u64::max_value(), 12_345, 42, 117];
+ let u64s = [u64::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1469,9 +1469,9 @@
let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u64::MAX, 12_345, 42, 117]);
- let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+ let i64s = [i64::MAX, i64::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1487,9 +1487,9 @@
let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+ assert_eq!(vec, [i64::MAX, i64::MIN, 42, -117]);
- let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON];
+ let f32s = [f32::NAN, f32::INFINITY, 1.23456789, f32::EPSILON];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1509,7 +1509,7 @@
assert!(vec[0].is_nan());
assert_eq!(vec[1..], f32s[1..]);
- let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON];
+ let f64s = [f64::NAN, f64::INFINITY, 1.234567890123456789, f64::EPSILON];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2b6c282..a902e96 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -124,7 +124,7 @@
bindings::Transaction_TEST_BYTE => {
assert_eq!(parcel.read::<i8>()?, 0);
assert_eq!(parcel.read::<i8>()?, 1);
- assert_eq!(parcel.read::<i8>()?, i8::max_value());
+ assert_eq!(parcel.read::<i8>()?, i8::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
// SAFETY: Just reading an extern constant.
@@ -133,7 +133,7 @@
reply.write(&0i8)?;
reply.write(&1i8)?;
- reply.write(&i8::max_value())?;
+ reply.write(&i8::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?;
// SAFETY: Just reading an extern constant.
@@ -143,14 +143,14 @@
bindings::Transaction_TEST_U16 => {
assert_eq!(parcel.read::<u16>()?, 0);
assert_eq!(parcel.read::<u16>()?, 1);
- assert_eq!(parcel.read::<u16>()?, u16::max_value());
+ assert_eq!(parcel.read::<u16>()?, u16::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS });
assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
reply.write(&0u16)?;
reply.write(&1u16)?;
- reply.write(&u16::max_value())?;
+ reply.write(&u16::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
reply.write(&(None as Option<Vec<u16>>))?;
@@ -158,14 +158,14 @@
bindings::Transaction_TEST_I32 => {
assert_eq!(parcel.read::<i32>()?, 0);
assert_eq!(parcel.read::<i32>()?, 1);
- assert_eq!(parcel.read::<i32>()?, i32::max_value());
+ assert_eq!(parcel.read::<i32>()?, i32::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 });
assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
reply.write(&0i32)?;
reply.write(&1i32)?;
- reply.write(&i32::max_value())?;
+ reply.write(&i32::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
reply.write(&(None as Option<Vec<i32>>))?;
@@ -173,14 +173,14 @@
bindings::Transaction_TEST_I64 => {
assert_eq!(parcel.read::<i64>()?, 0);
assert_eq!(parcel.read::<i64>()?, 1);
- assert_eq!(parcel.read::<i64>()?, i64::max_value());
+ assert_eq!(parcel.read::<i64>()?, i64::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 });
assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
reply.write(&0i64)?;
reply.write(&1i64)?;
- reply.write(&i64::max_value())?;
+ reply.write(&i64::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
reply.write(&(None as Option<Vec<i64>>))?;
@@ -188,14 +188,14 @@
bindings::Transaction_TEST_U64 => {
assert_eq!(parcel.read::<u64>()?, 0);
assert_eq!(parcel.read::<u64>()?, 1);
- assert_eq!(parcel.read::<u64>()?, u64::max_value());
+ assert_eq!(parcel.read::<u64>()?, u64::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 });
assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
reply.write(&0u64)?;
reply.write(&1u64)?;
- reply.write(&u64::max_value())?;
+ reply.write(&u64::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
reply.write(&(None as Option<Vec<u64>>))?;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index dd50fbd..21c32ac 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -42,6 +42,9 @@
defaults: ["binder_test_defaults"],
header_libs: ["libbinder_headers"],
srcs: ["binderDriverInterfaceTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ ],
test_suites: [
"device-tests",
"vts",
@@ -127,6 +130,7 @@
"libbase",
"libbinder",
"liblog",
+ "libprocessgroup",
"libutils",
],
static_libs: [
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 7be4f21..af82860 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -20,12 +20,15 @@
#include <stdlib.h>
#include <binder/IBinder.h>
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <linux/android/binder.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include "../binder_module.h"
+
#define BINDER_DEV_NAME "/dev/binder"
testing::Environment* binder_env;
@@ -365,6 +368,251 @@
binderTestReadEmpty();
}
+TEST_F(BinderDriverInterfaceTest, RequestFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ // Expecting a BR_FROZEN_BINDER since BC_REQUEST_FREEZE_NOTIFICATION
+ // above should lead to an immediate notification of the current state.
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ binder_uintptr_t arg2;
+ uint32_t cmd3;
+ struct binder_handle_cookie arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc2 = {
+ // Tell kernel that userspace has done handling BR_FROZEN_BINDER.
+ .cmd2 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg2 = cookie,
+ .cmd3 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg3 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr2);
+ EXPECT_EQ(sizeof(bc2), bwr2.write_consumed);
+ EXPECT_EQ(sizeof(br2) - sizeof(br2.pad), bwr2.read_consumed);
+ EXPECT_EQ(BR_NOOP, br2.cmd2);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br2.cmd3);
+ EXPECT_EQ(cookie, br2.arg3);
+
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, OverwritePendingFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ uint32_t cmd3;
+ uint32_t arg3;
+ } __attribute__((packed)) bc = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ // This BC_REQUEST_FREEZE_NOTIFICATION should lead to a pending
+ // frozen notification inserted into the queue.
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ // Send BC_CLEAR_FREEZE_NOTIFICATION before the above frozen
+ // notification has a chance of being sent. The notification should
+ // be overwritten. Userspace is expected to only receive
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd3 = BC_DECREFS,
+ .arg3 = 0,
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ binder_uintptr_t arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br;
+ struct binder_write_read bwr = binder_write_read();
+
+ bwr.write_buffer = (uintptr_t)&bc;
+ bwr.write_size = sizeof(bc);
+ bwr.read_buffer = (uintptr_t)&br;
+ bwr.read_size = sizeof(br);
+
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed);
+ EXPECT_EQ(BR_NOOP, br.cmd0);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br.cmd1);
+ EXPECT_EQ(cookie, br.arg1);
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, ResendFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ } __attribute__((packed)) bc2 = {
+ // Clear the notification before acknowledging the in-flight
+ // BR_FROZEN_BINDER. Kernel should hold off sending
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE until the acknowledgement
+ // reaches kernel.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+ struct {
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc3 = {
+ // Send the acknowledgement. Now the kernel should send out
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd3 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg3 = cookie,
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br3;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr2, EAGAIN);
+ binderTestReadEmpty();
+
+ struct binder_write_read bwr3 = binder_write_read();
+ bwr3.write_buffer = (uintptr_t)&bc3;
+ bwr3.write_size = sizeof(bc3);
+ bwr3.read_buffer = (uintptr_t)&br3;
+ bwr3.read_size = sizeof(br3);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr3);
+ EXPECT_EQ(sizeof(bc3), bwr3.write_consumed);
+ EXPECT_EQ(sizeof(br3) - sizeof(br3.pad), bwr3.read_consumed);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br3.cmd3);
+ EXPECT_EQ(cookie, br3.arg3);
+ binderTestReadEmpty();
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 9b1ba01..bcab6de 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -27,6 +27,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
#include <binder/Binder.h>
@@ -39,6 +40,8 @@
#include <binder/RpcSession.h>
#include <binder/Status.h>
#include <binder/unique_fd.h>
+#include <input/BlockingQueue.h>
+#include <processgroup/processgroup.h>
#include <utils/Flattenable.h>
#include <linux/sched.h>
@@ -57,6 +60,7 @@
using android::base::testing::HasValue;
using android::binder::Status;
using android::binder::unique_fd;
+using std::chrono_literals::operator""ms;
using testing::ExplainMatchResult;
using testing::Matcher;
using testing::Not;
@@ -115,6 +119,8 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_GETUID,
+ BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE,
+ BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
BINDER_LIB_TEST_REJECT_OBJECTS,
@@ -247,6 +253,43 @@
sp<IBinder> m_server;
};
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ BlockingQueue<std::pair<const wp<IBinder>, State>> events;
+
+ virtual void onStateChanged(const wp<IBinder>& who, State state) {
+ events.push(std::make_pair(who, state));
+ }
+
+ void ensureFrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::FROZEN, event->second); // isFrozen should be true
+ EXPECT_EQ(0u, events.size());
+ }
+
+ void ensureUnfrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::UNFROZEN, event->second); // isFrozen should be false
+ EXPECT_EQ(0u, events.size());
+ }
+
+ std::vector<bool> getAllAndClear() {
+ std::vector<bool> results;
+ while (true) {
+ auto event = events.popWithTimeout(0ms);
+ if (!event.has_value()) {
+ break;
+ }
+ results.push_back(event->second == State::FROZEN);
+ }
+ return results;
+ }
+
+ sp<IBinder> binder;
+};
+
class BinderLibTest : public ::testing::Test {
public:
virtual void SetUp() {
@@ -291,6 +334,51 @@
EXPECT_EQ(1, ret);
}
+ bool checkFreezeSupport() {
+ std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ // Pass test on devices where the cgroup v2 freezer is not supported
+ if (freezer_file.fail()) {
+ return false;
+ }
+ return IPCThreadState::self()->freeze(getpid(), false, 0) == NO_ERROR;
+ }
+
+ bool checkFreezeAndNotificationSupport() {
+ if (!checkFreezeSupport()) {
+ return false;
+ }
+ return ProcessState::isDriverFeatureEnabled(
+ ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ }
+
+ bool getBinderPid(int32_t* pid, sp<IBinder> server) {
+ Parcel data, replypid;
+ if (server->transact(BINDER_LIB_TEST_GETPID, data, &replypid) != NO_ERROR) {
+ ALOGE("BINDER_LIB_TEST_GETPID failed");
+ return false;
+ }
+ *pid = replypid.readInt32();
+ if (*pid <= 0) {
+ ALOGE("pid should be greater than zero");
+ return false;
+ }
+ return true;
+ }
+
+ void freezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
+ }
+
+ void unfreezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
+ }
+
+ void removeCallbackAndValidateNoEvent(sp<IBinder> binder,
+ sp<TestFrozenStateChangeCallback> callback) {
+ EXPECT_THAT(binder->removeFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ EXPECT_EQ(0u, callback->events.size());
+ }
+
sp<IBinder> m_server;
};
@@ -516,29 +604,18 @@
}
TEST_F(BinderLibTest, Freeze) {
- Parcel data, reply, replypid;
- std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
-
- // Pass test on devices where the cgroup v2 freezer is not supported
- if (freezer_file.fail()) {
- GTEST_SKIP();
+ if (!checkFreezeSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support proceess freezing";
return;
}
-
+ Parcel data, reply, replypid;
EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
for (int i = 0; i < 10; i++) {
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
}
- // Pass test on devices where BINDER_FREEZE ioctl is not supported
- int ret = IPCThreadState::self()->freeze(pid, false, 0);
- if (ret == -EINVAL) {
- GTEST_SKIP();
- return;
- }
- EXPECT_EQ(NO_ERROR, ret);
-
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
// b/268232063 - succeeds ~0.08% of the time
@@ -835,6 +912,199 @@
EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
}
+TEST_F(BinderLibTest, ReturnErrorIfKernelDoesNotSupportFreezeNotification) {
+ if (ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_EQ(nullptr, binder->localBinder());
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(INVALID_OPERATION));
+}
+
+TEST_F(BinderLibTest, FrozenStateChangeNotificatiion) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+}
+
+TEST_F(BinderLibTest, AddFrozenCallbackWhenFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ // Add the callback while the target process is frozen.
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ // Make sure no callback happens since the listener has been removed.
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, NoFrozenNotificationAfterCallbackRemoval) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Make sure no callback happens after the listener is removed.
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, MultipleFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback1 = sp<TestFrozenStateChangeCallback>::make();
+ sp<TestFrozenStateChangeCallback> callback2 = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback1), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback1->ensureUnfrozenEventReceived();
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback2), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback2->ensureUnfrozenEventReceived();
+
+ freezeProcess(pid);
+ callback1->ensureFrozenEventReceived();
+ callback2->ensureFrozenEventReceived();
+
+ removeCallbackAndValidateNoEvent(binder, callback1);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback1->events.size());
+ callback2->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback2);
+
+ freezeProcess(pid);
+ EXPECT_EQ(0u, callback2->events.size());
+}
+
+TEST_F(BinderLibTest, RemoveThenAddFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+}
+
+TEST_F(BinderLibTest, CoalesceFreezeCallbacksWhenListenerIsFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<IBinder> binder = addServer();
+ sp<IBinder> listener = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_NE(nullptr, listener);
+ int32_t pid, listenerPid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+ ASSERT_TRUE(getBinderPid(&listenerPid, listener));
+
+ // Ask the listener process to register for state change callbacks.
+ {
+ Parcel data, reply;
+ data.writeStrongBinder(binder);
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE, data,
+ &reply),
+ StatusEq(NO_ERROR));
+ }
+ // Freeze the listener process.
+ freezeProcess(listenerPid);
+ createProcessGroup(getuid(), listenerPid);
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Frozen"}));
+ // Repeatedly flip the target process between frozen and unfrozen states.
+ for (int i = 0; i < 1000; i++) {
+ usleep(50);
+ unfreezeProcess(pid);
+ usleep(50);
+ freezeProcess(pid);
+ }
+ // Unfreeze the listener process. Now it should receive the frozen state
+ // change notifications.
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Unfrozen"}));
+ unfreezeProcess(listenerPid);
+ // Wait for 500ms to give the process enough time to wake up and handle
+ // notifications.
+ usleep(500 * 1000);
+ {
+ std::vector<bool> events;
+ Parcel data, reply;
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS, data, &reply),
+ StatusEq(NO_ERROR));
+ reply.readBoolVector(&events);
+ // There should only be one single state change notifications delievered.
+ ASSERT_EQ(1u, events.size());
+ EXPECT_TRUE(events[0]);
+ }
+}
+
TEST_F(BinderLibTest, PassFile) {
int ret;
int pipefd[2];
@@ -1981,6 +2251,26 @@
reply->writeInt32(param.sched_priority);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE: {
+ sp<IBinder> binder = data.readStrongBinder();
+ frozenStateChangeCallback = sp<TestFrozenStateChangeCallback>::make();
+ // Hold an strong pointer to the binder object so it doesn't go
+ // away.
+ frozenStateChangeCallback->binder = binder;
+ int ret = binder->addFrozenStateChangeCallback(frozenStateChangeCallback);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ if (!event.has_value()) {
+ return NOT_ENOUGH_DATA;
+ }
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS: {
+ reply->writeBoolVector(frozenStateChangeCallback->getAllAndClear());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
@@ -2067,6 +2357,7 @@
sp<IBinder> m_callback;
bool m_exitOnDestroy;
std::mutex m_blockMutex;
+ sp<TestFrozenStateChangeCallback> frozenStateChangeCallback;
};
int run_server(int index, int readypipefd, bool usePoll)
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index f480780..2cec243 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -323,6 +323,22 @@
// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ virtual void onStateChanged(const wp<IBinder>&, State) {}
+};
+
+TEST_P(BinderRpc, RpcBinderShouldFailOnFrozenStateCallbacks) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ EXPECT_DEATH_IF_SUPPORTED(
+ { std::ignore = a->addFrozenStateChangeCallback(callback); },
+ "addFrozenStateChangeCallback\\(\\) is not supported for RPC Binder.");
+}
+
TEST_P(BinderRpc, RepeatRootObject) {
auto proc = createRpcTestSocketServerProcess({});
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 23f583b..7f45581 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -125,6 +125,11 @@
VULKAN_DEVICE_EXTENSION = 9,
};
+ enum GLTelemetryHints {
+ NO_HINT = 0,
+ SKIP_TELEMETRY = 1,
+ };
+
GpuStatsInfo() = default;
GpuStatsInfo(const GpuStatsInfo&) = default;
virtual ~GpuStatsInfo() = default;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8fbf5c6..e4e81ad 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -232,6 +232,7 @@
"MotionPredictorMetricsManager.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
+ "Resampler.cpp",
"TfLiteMotionPredictor.cpp",
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index c145d5c..99ffa68 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "InputTransport"
#define ATRACE_TAG ATRACE_TAG_INPUT
+#include <chrono>
+
#include <inttypes.h>
#include <android-base/logging.h>
@@ -168,6 +170,10 @@
return msg;
}
+bool isPointerEvent(const MotionEvent& motionEvent) {
+ return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
+}
+
} // namespace
using android::base::Result;
@@ -177,8 +183,13 @@
InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
sp<Looper> looper,
- InputConsumerCallbacks& callbacks)
- : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) {
+ InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler)
+ : mChannel(channel),
+ mLooper(looper),
+ mCallbacks(callbacks),
+ mResampler(std::move(resampler)),
+ mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
std::bind(&InputConsumerNoResampling::handleReceiveCallback, this,
@@ -463,6 +474,15 @@
}
messages.pop();
}
+ // Check if resampling should be performed.
+ if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) {
+ InputMessage* futureSample = nullptr;
+ if (!messages.empty()) {
+ futureSample = &messages.front();
+ }
+ mResampler->resampleMotionEvent(static_cast<std::chrono::nanoseconds>(frameTime),
+ *motionEvent, futureSample);
+ }
return std::make_pair(std::move(motionEvent), firstSeqForBatch);
}
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
new file mode 100644
index 0000000..af8354c
--- /dev/null
+++ b/libs/input/Resampler.cpp
@@ -0,0 +1,151 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LegacyResampler"
+
+#include <algorithm>
+#include <chrono>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include <input/Resampler.h>
+#include <utils/Timers.h>
+
+using std::chrono::nanoseconds;
+
+namespace android {
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+bool debugResampling() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
+ ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_RESAMPLING;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+}
+
+constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
+constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
+
+inline float lerp(float a, float b, float alpha) {
+ return a + alpha * (b - a);
+}
+
+const PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
+ const float alpha) {
+ // Ensure the struct PointerCoords is initialized.
+ PointerCoords resampledCoords{};
+ resampledCoords.isResampled = true;
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
+ return resampledCoords;
+}
+} // namespace
+
+void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
+ const size_t motionEventSampleSize = motionEvent.getHistorySize() + 1;
+ for (size_t i = 0; i < motionEventSampleSize; ++i) {
+ Sample sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
+ *motionEvent.getPointerProperties(0),
+ motionEvent.getSamplePointerCoords()[i]};
+ mLatestSamples.pushBack(sample);
+ }
+}
+
+void LegacyResampler::interpolate(const nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage& futureSample) const {
+ const Sample pastSample = mLatestSamples.back();
+ const nanoseconds delta =
+ static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return;
+ }
+ const float alpha =
+ std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
+
+ const PointerCoords resampledCoords =
+ calculateResampledCoords(pastSample.pointer.coords,
+ futureSample.body.motion.pointers[0].coords, alpha);
+ motionEvent.addSample(resampleTime.count(), &resampledCoords, motionEvent.getId());
+}
+
+void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& motionEvent) const {
+ if (mLatestSamples.size() < 2) {
+ return;
+ }
+ const Sample pastSample = *(mLatestSamples.end() - 2);
+ const Sample presentSample = *(mLatestSamples.end() - 1);
+ const nanoseconds delta =
+ static_cast<nanoseconds>(presentSample.eventTime - pastSample.eventTime);
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return;
+ } else if (delta > RESAMPLE_MAX_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
+ return;
+ }
+ // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
+ // we use this value as the resample time target.
+ const nanoseconds farthestPrediction = static_cast<nanoseconds>(presentSample.eventTime) +
+ std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
+ const nanoseconds newResampleTime =
+ (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
+ LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
+ << "Resample time is too far in the future. Adjusting prediction from "
+ << (resampleTime - presentSample.eventTime) << " to "
+ << (farthestPrediction - presentSample.eventTime) << "ns.";
+ const float alpha =
+ std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
+ delta;
+
+ const PointerCoords resampledCoords =
+ calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
+ alpha);
+ motionEvent.addSample(newResampleTime.count(), &resampledCoords, motionEvent.getId());
+}
+
+void LegacyResampler::resampleMotionEvent(const nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) {
+ if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
+ mLatestSamples.clear();
+ }
+ mPreviousDeviceId = motionEvent.getDeviceId();
+ updateLatestSamples(motionEvent);
+ if (futureSample) {
+ interpolate(resampleTime, motionEvent, *futureSample);
+ } else {
+ extrapolate(resampleTime, motionEvent);
+ }
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
+}
+} // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e9d799e..132866b 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -23,6 +23,7 @@
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
+ "Resampler_test.cpp",
"RingBuffer_test.cpp",
"TfLiteMotionPredictor_test.cpp",
"TouchResampling_test.cpp",
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index e710613..467c3b4 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -396,8 +396,9 @@
break;
}
case LooperMessage::CREATE_CONSUMER: {
- mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel),
- mLooper, *this);
+ mConsumer =
+ std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), mLooper,
+ *this, /*resampler=*/nullptr);
break;
}
case LooperMessage::DESTROY_CONSUMER: {
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
new file mode 100644
index 0000000..e160ca0
--- /dev/null
+++ b/libs/input/tests/Resampler_test.cpp
@@ -0,0 +1,417 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/Resampler.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/InputTransport.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::literals::chrono_literals;
+
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+ /**
+ * Converts from Pointer to PointerCoords. Enables calling LegacyResampler methods and
+ * assertions only with the relevant data for tests.
+ */
+ operator PointerCoords() const;
+};
+
+Pointer::operator PointerCoords() const {
+ PointerCoords pointerCoords;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ pointerCoords.isResampled = isResampled;
+ return pointerCoords;
+}
+
+struct InputSample {
+ std::chrono::milliseconds eventTime{0};
+ std::vector<Pointer> pointers{};
+ /**
+ * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator InputMessage() const;
+};
+
+InputSample::operator InputMessage() const {
+ InputMessage message;
+ message.header.type = InputMessage::Type::MOTION;
+ message.body.motion.pointerCount = pointers.size();
+ message.body.motion.eventTime = static_cast<std::chrono::nanoseconds>(eventTime).count();
+ message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
+ message.body.motion.downTime = 0;
+ const uint32_t pointerCount = message.body.motion.pointerCount;
+ for (uint32_t i = 0; i < pointerCount; ++i) {
+ message.body.motion.pointers[i].properties.id = pointers[i].id;
+ message.body.motion.pointers[i].properties.toolType = pointers[i].toolType;
+ message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
+ message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
+ message.body.motion.pointers[i].coords.isResampled = pointers[i].isResampled;
+ }
+ return message;
+}
+
+struct InputStream {
+ std::vector<InputSample> samples{};
+ int32_t action{0};
+ DeviceId deviceId{0};
+ /**
+ * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator MotionEvent() const;
+};
+
+InputStream::operator MotionEvent() const {
+ const InputSample& firstSample{*samples.begin()};
+ MotionEventBuilder motionEventBuilder =
+ MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
+ .downTime(0)
+ .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count())
+ .deviceId(deviceId);
+ for (const Pointer& pointer : firstSample.pointers) {
+ const PointerBuilder pointerBuilder =
+ PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y);
+ motionEventBuilder.pointer(pointerBuilder);
+ }
+ MotionEvent motionEvent = motionEventBuilder.build();
+ const size_t numSamples = samples.size();
+ for (size_t i = 1; i < numSamples; ++i) {
+ std::vector<PointerCoords> pointersCoords{samples[i].pointers.begin(),
+ samples[i].pointers.end()};
+ motionEvent.addSample(static_cast<std::chrono::nanoseconds>(samples[i].eventTime).count(),
+ pointersCoords.data(), motionEvent.getId());
+ }
+ return motionEvent;
+}
+
+} // namespace
+
+class ResamplerTest : public testing::Test {
+protected:
+ ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {}
+
+ ~ResamplerTest() override {}
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ std::unique_ptr<Resampler> mResampler;
+
+ MotionEvent buildMotionEvent(const int32_t action, const nsecs_t eventTime,
+ const std::vector<PointerBuilder>& pointers);
+
+ InputMessage createMessage(const uint32_t pointerCount, const nsecs_t eventTime,
+ const int32_t action,
+ const std::vector<PointerProperties>& properties,
+ const std::vector<PointerCoords>& coords);
+
+ /**
+ * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample
+ * member function.
+ * @param beforeCall MotionEvent before passing it to resampleMotionEvent
+ * @param afterCall MotionEvent after passing it to resampleMotionEvent
+ */
+ void assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall);
+
+ /**
+ * Asserts the MotionEvent is resampled by checking an increment in history size and that the
+ * resampled coordinates are near the expected ones.
+ */
+ void assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original,
+ const MotionEvent& resampled,
+ const PointerCoords& expectedCoords);
+
+ void assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled);
+};
+
+MotionEvent ResamplerTest::buildMotionEvent(const int32_t action, const nsecs_t eventTime,
+ const std::vector<PointerBuilder>& pointerBuilders) {
+ MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
+ .downTime(0)
+ .eventTime(eventTime);
+ for (const PointerBuilder& pointerBuilder : pointerBuilders) {
+ motionEventBuilder.pointer(pointerBuilder);
+ }
+ return motionEventBuilder.build();
+}
+
+InputMessage ResamplerTest::createMessage(const uint32_t pointerCount, const nsecs_t eventTime,
+ const int32_t action,
+ const std::vector<PointerProperties>& properties,
+ const std::vector<PointerCoords>& coords) {
+ InputMessage message;
+ message.header.type = InputMessage::Type::MOTION;
+ message.body.motion.pointerCount = pointerCount;
+ message.body.motion.eventTime = eventTime;
+ message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
+ message.body.motion.downTime = 0;
+ for (uint32_t i = 0; i < pointerCount; ++i) {
+ message.body.motion.pointers[i].properties = properties[i];
+ message.body.motion.pointers[i].coords = coords[i];
+ }
+ return message;
+}
+
+void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall) {
+ EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId());
+ EXPECT_EQ(beforeCall.getAction(), afterCall.getAction());
+ EXPECT_EQ(beforeCall.getActionButton(), afterCall.getActionButton());
+ EXPECT_EQ(beforeCall.getButtonState(), afterCall.getButtonState());
+ EXPECT_EQ(beforeCall.getFlags(), afterCall.getFlags());
+ EXPECT_EQ(beforeCall.getEdgeFlags(), afterCall.getEdgeFlags());
+ EXPECT_EQ(beforeCall.getClassification(), afterCall.getClassification());
+ EXPECT_EQ(beforeCall.getPointerCount(), afterCall.getPointerCount());
+ EXPECT_EQ(beforeCall.getMetaState(), afterCall.getMetaState());
+ EXPECT_EQ(beforeCall.getSource(), afterCall.getSource());
+ EXPECT_EQ(beforeCall.getXPrecision(), afterCall.getXPrecision());
+ EXPECT_EQ(beforeCall.getYPrecision(), afterCall.getYPrecision());
+ EXPECT_EQ(beforeCall.getDownTime(), afterCall.getDownTime());
+ EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId());
+}
+
+void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original,
+ const MotionEvent& resampled,
+ const PointerCoords& expectedCoords) {
+ assertMotionEventMetaDataDidNotMutate(original, resampled);
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t resampledSampleSize = resampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize + 1, resampledSampleSize);
+ const PointerCoords& resampledCoords =
+ resampled.getSamplePointerCoords()[resampled.getHistorySize()];
+ EXPECT_TRUE(resampledCoords.isResampled);
+ EXPECT_NEAR(expectedCoords.getX(), resampledCoords.getX(), EPSILON);
+ EXPECT_NEAR(expectedCoords.getY(), resampledCoords.getY(), EPSILON);
+}
+
+void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled) {
+ assertMotionEventMetaDataDidNotMutate(original, notResampled);
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t notResampledSampleSize = notResampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize, notResampledSampleSize);
+}
+
+TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 0};
+ const MotionEvent originalMotionEvent = motionEvent;
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) {
+ MotionEvent motionFromFirstDevice =
+ InputStream{{{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 0};
+ mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr);
+ MotionEvent motionFromSecondDevice =
+ InputStream{{{11ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 1};
+ const MotionEvent originalMotionEvent = motionFromSecondDevice;
+ mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr);
+ // The MotionEvent should not be resampled because the second event came from a different device
+ // than the previous event.
+ assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice);
+}
+
+// Increments of 16 ms for display refresh rate
+// Increments of 6 ms for input frequency
+// Resampling latency is known to be 5 ms
+// Therefore, first resampling time will be 11 ms
+
+/**
+ * Timeline
+ * ----+----------------------+---------+---------+---------+----------
+ * 0ms 10ms 11ms 15ms 16ms
+ * DOWN MOVE | MSG |
+ * resample frame
+ * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available
+ * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame
+ * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between
+ * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the
+ * last one in the batch to consume.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 1.2f,
+ .y = 1.2f,
+ .isResampled = true});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(10'500'000ns, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+/**
+ * Tests extrapolation given two MotionEvents with a single sample.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) {
+ MotionEvent previousMotionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(10ms, previousMotionEvent, nullptr);
+
+ MotionEvent motionEvent =
+ InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = true});
+ // Integrity of the whole motionEvent
+ // History size should increment by 1
+ // Check if the resampled value is the last one
+ // Check if the resampleTime is correct
+ // Check if the PointerCoords are consistent with the other computations
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.2f,
+ .isResampled = true});
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.2f,
+ .isResampled = true});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{9ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {26ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(27ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {25ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(43ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 2.4f,
+ .y = 2.4f,
+ .isResampled = true});
+}
+} // namespace android
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index ec7b190..bf0e38e 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -270,7 +270,7 @@
hnd = attempt_to_load_updated_driver(cnx);
// If updated driver apk is set but fail to load, abort here.
- LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(),
+ LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace() && !hnd,
"couldn't find an OpenGL ES implementation from %s",
android::GraphicsEnv::getInstance().getDriverPath().c_str());
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 37db05c..6e35041 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -916,42 +916,72 @@
egl_context_t* const c = get_context(share_list);
share_list = c->context;
}
+
+ bool skip_telemetry = false;
+
+ auto findAttribute = [](const EGLint* attrib_ptr, GLint attribute, GLint* value) {
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr == attribute) {
+ if (value) {
+ *value = val;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ std::vector<EGLint> replacement_attrib_list;
+ GLint telemetry_value;
+ if (findAttribute(attrib_list, EGL_TELEMETRY_HINT_ANDROID, &telemetry_value)) {
+ skip_telemetry = (telemetry_value == android::GpuStatsInfo::SKIP_TELEMETRY);
+
+ // We need to remove EGL_TELEMETRY_HINT_ANDROID or the underlying drivers will
+ // complain about an unexpected attribute
+ const EGLint* attrib_ptr = attrib_list;
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr != EGL_TELEMETRY_HINT_ANDROID) {
+ replacement_attrib_list.push_back(attr);
+ replacement_attrib_list.push_back(val);
+ }
+ }
+ replacement_attrib_list.push_back(EGL_NONE);
+ attrib_list = replacement_attrib_list.data();
+ }
// b/111083885 - If we are presenting EGL 1.4 interface to apps
// error out on robust access attributes that are invalid
// in EGL 1.4 as the driver may be fine with them but dEQP expects
// tests to fail according to spec.
if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
- const EGLint* attrib_ptr = attrib_list;
- while (*attrib_ptr != EGL_NONE) {
- GLint attr = *attrib_ptr++;
- GLint value = *attrib_ptr++;
- if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
- // We are GL ES context with EGL 1.4, this is an invalid
- // attribute
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
- }
- };
+ if (findAttribute(attrib_list, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
+ nullptr)) {
+ // We are GL ES context with EGL 1.4, this is an invalid attribute
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
}
EGLContext context =
cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- };
+ GLint version_value;
+ if (findAttribute(attrib_list, EGL_CONTEXT_CLIENT_VERSION, &version_value)) {
+ if (version_value == 2 || version_value == 3) {
+ version = egl_connection_t::GLESv2_INDEX;
+ }
}
if (version == egl_connection_t::GLESv1_INDEX) {
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::GLES_1_IN_USE);
}
- android::GraphicsEnv::getInstance().setTargetStats(
- android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ if (!skip_telemetry) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ }
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
return c;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index faafa50..fcea0f3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2683,7 +2683,7 @@
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry.deviceId, pointer, targets);
+ tempTouchState, entry, targets);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -7100,9 +7100,11 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const {
+ LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
+ const DeviceId deviceId = entry.deviceId;
+ const PointerProperties& pointerProperties = entry.pointerProperties[0];
std::vector<PointerProperties> pointers{pointerProperties};
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7129,7 +7131,7 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointers);
+ deviceId, pointers, entry.eventTime);
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 698bdba..87dfd1d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -709,11 +709,23 @@
sp<InputReporterInterface> mReporter;
+ /**
+ * Slip the wallpaper touch if necessary.
+ *
+ * @param targetFlags the target flags
+ * @param oldWindowHandle the old window that the touch slipped out of
+ * @param newWindowHandle the new window that the touch is slipping into
+ * @param state the current touch state. This will be updated if necessary to reflect the new
+ * windows that are receiving touch.
+ * @param deviceId the device id of the current motion being processed
+ * @param pointerProperties the pointer properties of the current motion being processed
+ * @param targets the current targets to add the walpaper ones to
+ * @param eventTime the new downTime for the wallpaper target
+ */
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9d4bb3d..5a70dd5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,7 @@
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
+ std::optional<nsecs_t> firstDownTimeInTarget);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d418a9b..c2f174f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5711,6 +5711,273 @@
}
/**
+ * Three windows:
+ * 1) A window on the left, with flag dup_to_wallpaper
+ * 2) A window on the right, with flag slippery
+ * 3) A wallpaper window under the left window
+ * When touch slips from right window to left, the wallpaper should receive a similar slippery
+ * enter event. Later on, when another device becomes active, the wallpaper should receive
+ * consistent streams from the new device, and also from the old device.
+ * This test attempts to reproduce a crash in the dispatcher where the wallpaper target's downTime
+ * was not getting set during slippery entrance.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ auto firstFingerMoveFromDeviceA = AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE),
+ WithPointerCount(1), WithPointerId(0, 0));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Same test as above, but with enable_multi_device_same_window_stream flag set to false.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ // This device was already canceled, so MOVE events will not be arriving to the windows from it.
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->assertNoEvents();
+}
+
+/**
* Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
* down event to the right window. Device B sends a down event to the left window, and then a
* POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 555b80a..33724a9 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -26,12 +26,17 @@
using util::ProtoOutputStream;
-SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
- uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId)
- : mService(service), mUid(uid), mMem(*mem),
+SensorService::SensorDirectConnection::SensorDirectConnection(
+ const sp<SensorService>& service, uid_t uid, pid_t pid, const sensors_direct_mem_t* mem,
+ int32_t halChannelHandle, const String16& opPackageName, int deviceId)
+ : mService(service),
+ mUid(uid),
+ mPid(pid),
+ mMem(*mem),
mHalChannelHandle(halChannelHandle),
- mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
+ mOpPackageName(opPackageName),
+ mDeviceId(deviceId),
+ mDestroyed(false) {
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -62,10 +67,21 @@
void SensorService::SensorDirectConnection::dump(String8& result) const {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
- String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size());
- for (auto &i : mActivated) {
- result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
+ result.appendFormat("\t%s | HAL channel handle %d | uid %d | pid %d\n",
+ String8(mOpPackageName).c_str(), getHalChannelHandle(), mUid, mPid);
+ result.appendFormat("\tActivated sensor count: %zu\n", mActivated.size());
+ dumpSensorInfoWithLock(result, mActivated);
+
+ result.appendFormat("\tBackup sensor (opened but UID idle) count: %zu\n",
+ mActivatedBackup.size());
+ dumpSensorInfoWithLock(result, mActivatedBackup);
+}
+
+void SensorService::SensorDirectConnection::dumpSensorInfoWithLock(
+ String8& result, std::unordered_map<int, int> sensors) const {
+ for (auto& i : sensors) {
+ result.appendFormat("\t\t%s 0x%08x | rate %d\n", mService->getSensorName(i.first).c_str(),
+ i.first, i.second);
}
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index bfaf811..9f21731 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
#define ANDROID_SENSOR_DIRECT_CONNECTION_H
-#include <optional>
+#include <android-base/thread_annotations.h>
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <binder/BinderService.h>
@@ -37,15 +38,15 @@
class SensorService::SensorDirectConnection: public BnSensorEventConnection {
public:
- SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
- const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId);
+ SensorDirectConnection(const sp<SensorService>& service, uid_t uid, pid_t pid,
+ const sensors_direct_mem_t* mem, int32_t halChannelHandle,
+ const String16& opPackageName, int deviceId);
void dump(String8& result) const;
void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
const String16& getOpPackageName() const { return mOpPackageName; }
int32_t getHalChannelHandle() const;
- bool isEquivalent(const sensors_direct_mem_t *mem) const;
+ bool isEquivalent(const sensors_direct_mem_t* mem) const;
// Invoked when access to sensors for this connection has changed, e.g. lost or
// regained due to changes in the sensor restricted/privacy mode or the
@@ -94,8 +95,13 @@
// Recover sensor requests previously capped by capRates().
void uncapRates();
+ // Dumps a set of sensor infos.
+ void dumpSensorInfoWithLock(String8& result, std::unordered_map<int, int> sensors) const
+ EXCLUSIVE_LOCKS_REQUIRED(mConnectionLock);
+
const sp<SensorService> mService;
const uid_t mUid;
+ const pid_t mPid;
const sensors_direct_mem_t mMem;
const int32_t mHalChannelHandle;
const String16 mOpPackageName;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 3446f58..130c112 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -90,15 +90,14 @@
result.append("NORMAL\n");
}
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
- "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize,
- mMaxCacheSize);
+ "max cache size %d | has sensor access: %s\n",
+ mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize,
+ hasSensorAccess() ? "true" : "false");
for (auto& it : mSensorInfo) {
const FlushInfo& flushInfo = it.second;
- result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
- mService->getSensorName(it.first).c_str(),
- it.first,
- flushInfo.mFirstFlushPending ? "First flush pending" :
- "active",
+ result.appendFormat("\t %s 0x%08x | first flush pending: %s | pending flush events %d \n",
+ mService->getSensorName(it.first).c_str(), it.first,
+ flushInfo.mFirstFlushPending ? "true" : "false",
flushInfo.mPendingFlushEventsToSend);
}
#if DEBUG_CONNECTIONS
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 31b7f88..3895ffe 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -682,14 +682,14 @@
mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
const auto& activeConnections = connLock.getActiveConnections();
- result.appendFormat("%zd active connections\n", activeConnections.size());
+ result.appendFormat("%zd open event connections\n", activeConnections.size());
for (size_t i=0 ; i < activeConnections.size() ; i++) {
result.appendFormat("Connection Number: %zu \n", i);
activeConnections[i]->dump(result);
}
const auto& directConnections = connLock.getDirectConnections();
- result.appendFormat("%zd direct connections\n", directConnections.size());
+ result.appendFormat("%zd open direct connections\n", directConnections.size());
for (size_t i = 0 ; i < directConnections.size() ; i++) {
result.appendFormat("Direct connection %zu:\n", i);
directConnections[i]->dump(result);
@@ -1729,7 +1729,10 @@
ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
} else {
mem.handle = clone;
- conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ conn = new SensorDirectConnection(this, uid, pid, &mem, channelHandle, opPackageName,
+ deviceId);
}
if (conn == nullptr) {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 04491a2..4a7cff5 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -89,7 +89,9 @@
}
bool VSyncPredictor::validate(nsecs_t timestamp) const {
+ SFTRACE_CALL();
if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
+ SFTRACE_INSTANT("timestamp valid (first)");
return true;
}
@@ -98,7 +100,11 @@
(timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent &&
percent <= (kMaxPercent - kOutlierTolerancePercent)) {
- SFTRACE_FORMAT_INSTANT("timestamp is not aligned with model");
+ SFTRACE_FORMAT_INSTANT("timestamp not aligned with model. aValidTimestamp %.2fms ago"
+ ", timestamp %.2fms ago, idealPeriod=%.2 percent=%d",
+ (mClock->now() - aValidTimestamp) / 1e6f,
+ (mClock->now() - timestamp) / 1e6f,
+ idealPeriod() / 1e6f, percent);
return false;
}
@@ -148,7 +154,10 @@
// Add the timestamp to mTimestamps before clearing it so we could
// update mKnownTimestamp based on the new timestamp.
mTimestamps.push_back(timestamp);
- clearTimestamps();
+
+ // Do not clear timelines as we don't want to break the phase while
+ // we are still learning.
+ clearTimestamps(/* clearTimelines */ false);
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
@@ -235,7 +244,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -245,7 +254,7 @@
auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -425,6 +434,9 @@
timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
std::lock_guard lock(mMutex);
+ // do not clear the timelines on VRR displays if we didn't change the mode
+ const bool isVrr = modePtr->getVrrConfig().has_value();
+ const bool clearTimelines = !isVrr || mDisplayModePtr->getId() != modePtr->getId();
mDisplayModePtr = modePtr;
mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
@@ -438,8 +450,10 @@
mRateMap[idealPeriod()] = {idealPeriod(), 0};
}
- mTimelines.clear();
- clearTimestamps();
+ if (clearTimelines) {
+ mTimelines.clear();
+ }
+ clearTimestamps(clearTimelines);
}
Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
@@ -556,15 +570,19 @@
return mRateMap.find(idealPeriod())->second;
}
-void VSyncPredictor::clearTimestamps() {
- SFTRACE_CALL();
+void VSyncPredictor::clearTimestamps(bool clearTimelines) {
+ SFTRACE_FORMAT("%s: clearTimelines=%d", __func__, clearTimelines);
if (!mTimestamps.empty()) {
auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
if (mKnownTimestamp) {
mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
} else {
mKnownTimestamp = maxRb;
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp (maxRb) was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
}
mTimestamps.clear();
@@ -575,7 +593,7 @@
if (mTimelines.empty()) {
mLastCommittedVsync = TimePoint::fromNs(0);
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
- } else {
+ } else if (clearTimelines) {
while (mTimelines.size() > 1) {
mTimelines.pop_front();
}
@@ -596,9 +614,10 @@
}
void VSyncPredictor::resetModel() {
+ SFTRACE_CALL();
std::lock_guard lock(mMutex);
mRateMap[idealPeriod()] = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
}
void VSyncPredictor::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 6c8a2f2..2df3d04 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -127,7 +127,7 @@
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
- void clearTimestamps() REQUIRES(mMutex);
+ void clearTimestamps(bool clearTimelines) REQUIRES(mMutex);
const std::unique_ptr<Clock> mClock;
const PhysicalDisplayId mId;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 2455822..b974cd2 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -140,7 +140,9 @@
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
+ // kernel idle timer is not applicable for VRR
+ const bool supportKernelIdleTimer = mSupportKernelIdleTimer && !modePtr->getVrrConfig();
+ if (!supportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 3f89960..ef213f0 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -339,10 +339,13 @@
ALOGD("Unload builtin Vulkan driver.");
- // Close the opened device
- int err = hal_.dev_->common.close(
- const_cast<struct hw_device_t*>(&hal_.dev_->common));
- ALOG_ASSERT(!err, "hw_device_t::close() failed.");
+ if (hal_.dev_->common.close != nullptr)
+ {
+ // Close the opened device
+ int err = hal_.dev_->common.close(
+ const_cast<struct hw_device_t*>(&hal_.dev_->common));
+ ALOG_ASSERT(!err, "hw_device_t::close() failed.");
+ }
// Close the opened shared library in the hw_module_t
android_unload_sphal_library(hal_.dev_->common.module->dso);