Merge "AImageDecoder: respond to API feedback" into sc-dev am: 5487967eab am: 8a1229fd9a am: 583f3df56e
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/14411636
Change-Id: I4bf319bd5784e482039c9ee0e4609c38b7c409aa
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreport/OWNERS
+++ b/cmds/bugreport/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreportz/OWNERS
+++ b/cmds/bugreportz/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 336c5a2..8ac4ff8 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1641,6 +1641,10 @@
MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
} else {
RunCommand("LSMOD", {"lsmod"});
+ RunCommand("MODULES INFO",
+ {"sh", "-c", "cat /proc/modules | cut -d' ' -f1 | "
+ " while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; "
+ "done"}, CommandOptions::AS_ROOT);
}
if (android::base::GetBoolProperty("ro.logd.kernel", false)) {
@@ -1960,6 +1964,8 @@
RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"vcn_management"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
if (include_sensitive_info) {
// Carrier apps' services will be dumped below in dumpsys activity service all-non-platform.
RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
@@ -3183,6 +3189,11 @@
// Since we do not have user consent to share the bugreport it does not get
// copied over to the calling app but remains in the internal directory from
// where the user can manually pull it.
+ std::string final_path = GetPath(".zip");
+ bool copy_succeeded = android::os::CopyFileToFile(path_, final_path);
+ if (copy_succeeded) {
+ android::os::UnlinkAndLogOnError(path_);
+ }
return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
}
// Unknown result; must be a programming error.
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS
index 4f6a89e..97a63ca 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -1,5 +1,6 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index fc745d0..d6807ff 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -9,3 +9,4 @@
ngeoffray@google.com
rpl@google.com
toddke@google.com
+patb@google.com
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9881371..059bc41 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -133,6 +133,9 @@
* ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
* including the updates in a transaction was presented.
*
+ * Buffers which are replaced or removed from the scene in the transaction invoking
+ * this callback may be reused after this point.
+ *
* \param context Optional context provided by the client that is passed into
* the callback.
*
@@ -153,6 +156,13 @@
* are ready to be presented. This callback will be invoked before the
* ASurfaceTransaction_OnComplete callback.
*
+ * This callback does not mean buffers have been released! It simply means that any new
+ * transactions applied will not overwrite the transaction for which we are receiving
+ * a callback and instead will be included in the next frame. If you are trying to avoid
+ * dropping frames (overwriting transactions), and unable to use timestamps (Which provide
+ * a more efficient solution), then this method provides a method to pace your transaction
+ * application.
+ *
* \param context Optional context provided by the client that is passed into the callback.
*
* \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
@@ -358,6 +368,11 @@
* enum.
*
* Available since API level 29.
+ *
+ * @deprecated Use setCrop, setPosition, setBufferTransform, and setScale instead. Those functions
+ * provide well defined behavior and allows for more control by the apps. It also allows the caller
+ * to set different properties at different times, instead of having to specify all the desired
+ * properties at once.
*/
void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, const ARect& source,
diff --git a/include/input/Input.h b/include/input/Input.h
index 7b522bb..389b1bd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -70,6 +70,14 @@
*/
AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+ /**
+ * This flag indicates that the event will not cause a focus change if it is directed to an
+ * unfocused window, even if it an ACTION_DOWN. This is typically used with pointer
+ * gestures to allow the user to direct gestures to an unfocused window without bringing it
+ * into focus.
+ */
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
+
/* Motion event is inconsistent with previously sent motion events. */
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
};
@@ -348,8 +356,6 @@
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
- void scale(float globalScale);
-
// Scale the pointer coordinates according to a global scale and a
// window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
// axes, however the window scaling will not.
@@ -746,10 +752,14 @@
void scale(float globalScaleFactor);
- // Apply 3x3 perspective matrix transformation.
+ // Set 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
void transform(const std::array<float, 9>& matrix);
+ // Apply 3x3 perspective matrix transformation only to content (do not modify mTransform).
+ // Matrix is in row-major form and compatible with SkMatrix.
+ void applyTransform(const std::array<float, 9>& matrix);
+
#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ff33678..360dfbf 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -229,7 +229,7 @@
InputChannel(const InputChannel& other)
: mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
- virtual ~InputChannel();
+ ~InputChannel() override;
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index a97cf87..be260e8 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -112,7 +112,7 @@
"PersistableBundle.cpp",
"ProcessState.cpp",
"RpcAddress.cpp",
- "RpcConnection.cpp",
+ "RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
"Static.cpp",
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index fdcf94a..1dcb94c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -21,7 +21,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <binder/Stability.h>
#include <cutils/compiler.h>
#include <utils/Log.h>
@@ -136,15 +136,15 @@
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
-sp<BpBinder> BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
- LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
+sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) {
+ LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session");
// These are not currently tracked, since there is no UID or other
// identifier to track them with. However, if similar functionality is
- // needed, connection objects keep track of all BpBinder objects on a
- // per-connection basis.
+ // needed, session objects keep track of all BpBinder objects on a
+ // per-session basis.
- return sp<BpBinder>::make(SocketHandle{connection, address});
+ return sp<BpBinder>::make(RpcHandle{session, address});
}
BpBinder::BpBinder(Handle&& handle)
@@ -165,20 +165,20 @@
IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
}
-BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) {
- LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object");
+BpBinder::BpBinder(RpcHandle&& handle) : BpBinder(Handle(handle)) {
+ LOG_ALWAYS_FATAL_IF(rpcSession() == nullptr, "BpBinder created w/o session object");
}
bool BpBinder::isRpcBinder() const {
- return std::holds_alternative<SocketHandle>(mHandle);
+ return std::holds_alternative<RpcHandle>(mHandle);
}
const RpcAddress& BpBinder::rpcAddress() const {
- return std::get<SocketHandle>(mHandle).address;
+ return std::get<RpcHandle>(mHandle).address;
}
-const sp<RpcConnection>& BpBinder::rpcConnection() const {
- return std::get<SocketHandle>(mHandle).connection;
+const sp<RpcSession>& BpBinder::rpcSession() const {
+ return std::get<RpcHandle>(mHandle).session;
}
int32_t BpBinder::binderHandle() const {
@@ -273,7 +273,7 @@
status_t status;
if (CC_UNLIKELY(isRpcBinder())) {
- status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags);
+ status = rpcSession()->transact(rpcAddress(), code, data, reply, flags);
} else {
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
@@ -479,7 +479,7 @@
{
ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
if (CC_UNLIKELY(isRpcBinder())) {
- (void)rpcConnection()->sendDecStrong(rpcAddress());
+ (void)rpcSession()->sendDecStrong(rpcAddress());
return;
}
IF_ALOGV() {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index ef7fd44..18b77e6 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,19 +366,45 @@
pid_t IPCThreadState::getCallingPid() const
{
+ checkContextIsBinderForUse(__func__);
return mCallingPid;
}
const char* IPCThreadState::getCallingSid() const
{
+ checkContextIsBinderForUse(__func__);
return mCallingSid;
}
uid_t IPCThreadState::getCallingUid() const
{
+ checkContextIsBinderForUse(__func__);
return mCallingUid;
}
+IPCThreadState::SpGuard* IPCThreadState::pushGetCallingSpGuard(SpGuard* guard) {
+ SpGuard* orig = mServingStackPointerGuard;
+ mServingStackPointerGuard = guard;
+ return orig;
+}
+
+void IPCThreadState::restoreGetCallingSpGuard(SpGuard* guard) {
+ mServingStackPointerGuard = guard;
+}
+
+void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
+ if (mServingStackPointerGuard == nullptr) return;
+
+ if (!mServingStackPointer || mServingStackPointerGuard < mServingStackPointer) {
+ LOG_ALWAYS_FATAL("In context %s, %s does not make sense.",
+ mServingStackPointerGuard->context, use);
+ }
+
+ // in the case mServingStackPointer is deeper in the stack than the guard,
+ // we must be serving a binder transaction (maybe nested). This is a binder
+ // context, so we don't abort
+}
+
int64_t IPCThreadState::clearCallingIdentity()
{
// ignore mCallingSid for legacy reasons
@@ -847,15 +873,15 @@
}
IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self()),
- mServingStackPointer(nullptr),
- mWorkSource(kUnsetWorkSource),
- mPropagateWorkSource(false),
- mIsLooper(false),
- mStrictModePolicy(0),
- mLastTransactionBinderFlags(0),
- mCallRestriction(mProcess->mCallRestriction)
-{
+ : mProcess(ProcessState::self()),
+ mServingStackPointer(nullptr),
+ mServingStackPointerGuard(nullptr),
+ mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
+ mIsLooper(false),
+ mStrictModePolicy(0),
+ mLastTransactionBinderFlags(0),
+ mCallRestriction(mProcess->mCallRestriction) {
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index b503beb..6165911 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -123,16 +123,20 @@
std::string regStr = (reRegister) ? "Re-registering" : "Registering";
ALOGI("%s service %s", regStr.c_str(), name.c_str());
- if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
- ALOGE("Failed to register service %s", name.c_str());
+ if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags);
+ !status.isOk()) {
+ ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str());
return false;
}
if (!reRegister) {
- if (!manager->registerClientCallback(name, service,
- sp<android::os::IClientCallback>::fromExisting(this))
- .isOk()) {
- ALOGE("Failed to add client callback for service %s", name.c_str());
+ if (Status status =
+ manager->registerClientCallback(name, service,
+ sp<android::os::IClientCallback>::fromExisting(
+ this));
+ !status.isOk()) {
+ ALOGE("Failed to add client callback for service %s (%s)", name.c_str(),
+ status.toString8().c_str());
return false;
}
@@ -171,10 +175,10 @@
auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
for (auto& [name, entry] : mRegisteredServices) {
- bool success = manager->tryUnregisterService(name, entry.service).isOk();
+ Status status = manager->tryUnregisterService(name, entry.service);
- if (!success) {
- ALOGI("Failed to unregister service %s", name.c_str());
+ if (!status.isOk()) {
+ ALOGI("Failed to unregister service %s (%s)", name.c_str(), status.toString8().c_str());
return false;
}
entry.registered = false;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 5627a78..ee834ea 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -202,7 +202,7 @@
status_t status = writeInt32(1); // non-null
if (status != OK) return status;
RpcAddress address = RpcAddress::zero();
- status = mConnection->state()->onBinderLeaving(mConnection, binder, &address);
+ status = mSession->state()->onBinderLeaving(mSession, binder, &address);
if (status != OK) return status;
status = address.writeToParcel(this);
if (status != OK) return status;
@@ -273,8 +273,7 @@
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
if (isForRpc()) {
- LOG_ALWAYS_FATAL_IF(mConnection == nullptr,
- "RpcConnection required to read from remote parcel");
+ LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
int32_t isNull;
status_t status = readInt32(&isNull);
@@ -286,7 +285,7 @@
auto addr = RpcAddress::zero();
status_t status = addr.readFromParcel(*this);
if (status != OK) return status;
- binder = mConnection->state()->onBinderEntering(mConnection, addr);
+ binder = mSession->state()->onBinderEntering(mSession, addr);
}
return finishUnflattenBinder(binder, out);
@@ -568,20 +567,20 @@
LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
- markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
+ markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
}
}
-void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+void Parcel::markForRpc(const sp<RpcSession>& session) {
LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
"format must be set before data is written OR on IPC data");
- LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
- mConnection = connection;
+ LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
+ mSession = session;
}
bool Parcel::isForRpc() const {
- return mConnection != nullptr;
+ return mSession != nullptr;
}
void Parcel::updateWorkSourceRequestHeaderPosition() const {
@@ -2499,7 +2498,7 @@
mDataPos = 0;
ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
- mConnection = nullptr;
+ mSession = nullptr;
mObjects = nullptr;
mObjectsSize = 0;
mObjectsCapacity = 0;
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
deleted file mode 100644
index 4b3a53f..0000000
--- a/libs/binder/RpcConnection.cpp
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RpcConnection"
-
-#include <binder/RpcConnection.h>
-
-#include <inttypes.h>
-#include <unistd.h>
-
-#include <string_view>
-
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-#include <utils/String8.h>
-
-#include "RpcSocketAddress.h"
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
-#ifdef __GLIBC__
-extern "C" pid_t gettid();
-#endif
-
-namespace android {
-
-using base::unique_fd;
-
-RpcConnection::RpcConnection() {
- LOG_RPC_DETAIL("RpcConnection created %p", this);
-
- mState = std::make_unique<RpcState>();
-}
-RpcConnection::~RpcConnection() {
- LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
-
- std::lock_guard<std::mutex> _l(mSocketMutex);
- LOG_ALWAYS_FATAL_IF(mServers.size() != 0,
- "Should not be able to destroy a connection with servers in use.");
-}
-
-sp<RpcConnection> RpcConnection::make() {
- return sp<RpcConnection>::make();
-}
-
-bool RpcConnection::setupUnixDomainClient(const char* path) {
- return setupSocketClient(UnixSocketAddress(path));
-}
-
-#ifdef __BIONIC__
-
-bool RpcConnection::setupVsockClient(unsigned int cid, unsigned int port) {
- return setupSocketClient(VsockSocketAddress(cid, port));
-}
-
-#endif // __BIONIC__
-
-bool RpcConnection::setupInetClient(const char* addr, unsigned int port) {
- auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
- if (aiStart == nullptr) return false;
- for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
- InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
- if (setupSocketClient(socketAddress)) return true;
- }
- ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
- return false;
-}
-
-bool RpcConnection::addNullDebuggingClient() {
- unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
-
- if (serverFd == -1) {
- ALOGE("Could not connect to /dev/null: %s", strerror(errno));
- return false;
- }
-
- addClient(std::move(serverFd));
- return true;
-}
-
-sp<IBinder> RpcConnection::getRootObject() {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
- return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this));
-}
-
-status_t RpcConnection::getMaxThreads(size_t* maxThreads) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
- return state()->getMaxThreads(socket.fd(), sp<RpcConnection>::fromExisting(this), maxThreads);
-}
-
-status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this),
- (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
- : SocketUse::CLIENT);
- return state()->transact(socket.fd(), address, code, data,
- sp<RpcConnection>::fromExisting(this), reply, flags);
-}
-
-status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT_REFCOUNT);
- return state()->sendDecStrong(socket.fd(), address);
-}
-
-status_t RpcConnection::readId() {
- {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
- }
-
- int32_t id;
-
- ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
- status_t status =
- state()->getConnectionId(socket.fd(), sp<RpcConnection>::fromExisting(this), &id);
- if (status != OK) return status;
-
- LOG_RPC_DETAIL("RpcConnection %p has id %d", this, id);
- mId = id;
- return OK;
-}
-
-void RpcConnection::startThread(unique_fd client) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<RpcConnection> holdThis = sp<RpcConnection>::fromExisting(this);
- int fd = client.release();
- auto thread = std::thread([=] {
- holdThis->join(unique_fd(fd));
- {
- std::lock_guard<std::mutex> _l(holdThis->mSocketMutex);
- size_t erased = mThreads.erase(std::this_thread::get_id());
- LOG_ALWAYS_FATAL_IF(erased != 0, "Could not erase thread.");
- }
- });
- mThreads[thread.get_id()] = std::move(thread);
-}
-
-void RpcConnection::join(unique_fd client) {
- // must be registered to allow arbitrary client code executing commands to
- // be able to do nested calls (we can't only read from it)
- sp<ConnectionSocket> socket = assignServerToThisThread(std::move(client));
-
- while (true) {
- status_t error =
- state()->getAndExecuteCommand(socket->fd, sp<RpcConnection>::fromExisting(this));
-
- if (error != OK) {
- ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
- break;
- }
- }
-
- LOG_ALWAYS_FATAL_IF(!removeServerSocket(socket),
- "bad state: socket object guaranteed to be in list");
-}
-
-wp<RpcServer> RpcConnection::server() {
- return mForServer;
-}
-
-bool RpcConnection::setupSocketClient(const RpcSocketAddress& addr) {
- {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- LOG_ALWAYS_FATAL_IF(mClients.size() != 0,
- "Must only setup connection once, but already has %zu clients",
- mClients.size());
- }
-
- if (!setupOneSocketClient(addr, RPC_CONNECTION_ID_NEW)) return false;
-
- // TODO(b/185167543): we should add additional connections dynamically
- // instead of all at once.
- // TODO(b/186470974): first risk of blocking
- size_t numThreadsAvailable;
- if (status_t status = getMaxThreads(&numThreadsAvailable); status != OK) {
- ALOGE("Could not get max threads after initial connection to %s: %s",
- addr.toString().c_str(), statusToString(status).c_str());
- return false;
- }
-
- if (status_t status = readId(); status != OK) {
- ALOGE("Could not get connection id after initial connection to %s; %s",
- addr.toString().c_str(), statusToString(status).c_str());
- return false;
- }
-
- // we've already setup one client
- for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
- // TODO(b/185167543): avoid race w/ accept4 not being called on server
- for (size_t tries = 0; tries < 5; tries++) {
- if (setupOneSocketClient(addr, mId.value())) break;
- usleep(10000);
- }
- }
-
- return true;
-}
-
-bool RpcConnection::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
- unique_fd serverFd(
- TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- int savedErrno = errno;
- ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
- int savedErrno = errno;
- ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
- return false;
- }
-
- if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
- int savedErrno = errno;
- ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
- strerror(savedErrno));
- return false;
- }
-
- LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
-
- addClient(std::move(serverFd));
- return true;
-}
-
-void RpcConnection::addClient(unique_fd fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
- connection->fd = std::move(fd);
- mClients.push_back(connection);
-}
-
-void RpcConnection::setForServer(const wp<RpcServer>& server, int32_t connectionId) {
- mId = connectionId;
- mForServer = server;
-}
-
-sp<RpcConnection::ConnectionSocket> RpcConnection::assignServerToThisThread(unique_fd fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
- connection->fd = std::move(fd);
- connection->exclusiveTid = gettid();
- mServers.push_back(connection);
-
- return connection;
-}
-
-bool RpcConnection::removeServerSocket(const sp<ConnectionSocket>& socket) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- if (auto it = std::find(mServers.begin(), mServers.end(), socket); it != mServers.end()) {
- mServers.erase(it);
- return true;
- }
- return false;
-}
-
-RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
- : mConnection(connection) {
- pid_t tid = gettid();
- std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
-
- mConnection->mWaitingThreads++;
- while (true) {
- sp<ConnectionSocket> exclusive;
- sp<ConnectionSocket> available;
-
- // CHECK FOR DEDICATED CLIENT SOCKET
- //
- // A server/looper should always use a dedicated connection if available
- findSocket(tid, &exclusive, &available, mConnection->mClients, mConnection->mClientsOffset);
-
- // WARNING: this assumes a server cannot request its client to send
- // a transaction, as mServers is excluded below.
- //
- // Imagine we have more than one thread in play, and a single thread
- // sends a synchronous, then an asynchronous command. Imagine the
- // asynchronous command is sent on the first client socket. Then, if
- // we naively send a synchronous command to that same socket, the
- // thread on the far side might be busy processing the asynchronous
- // command. So, we move to considering the second available thread
- // for subsequent calls.
- if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
- mConnection->mClientsOffset =
- (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
- }
-
- // USE SERVING SOCKET (for nested transaction)
- //
- // asynchronous calls cannot be nested
- if (use != SocketUse::CLIENT_ASYNC) {
- // server sockets are always assigned to a thread
- findSocket(tid, &exclusive, nullptr /*available*/, mConnection->mServers,
- 0 /* index hint */);
- }
-
- // if our thread is already using a connection, prioritize using that
- if (exclusive != nullptr) {
- mSocket = exclusive;
- mReentrant = true;
- break;
- } else if (available != nullptr) {
- mSocket = available;
- mSocket->exclusiveTid = tid;
- break;
- }
-
- // in regular binder, this would usually be a deadlock :)
- LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0,
- "Not a client of any connection. You must create a connection to an "
- "RPC server to make any non-nested (e.g. oneway or on another thread) "
- "calls.");
-
- LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...",
- mConnection->mClients.size(), mConnection->mServers.size());
- mConnection->mSocketCv.wait(_l);
- }
- mConnection->mWaitingThreads--;
-}
-
-void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
- sp<ConnectionSocket>* available,
- std::vector<sp<ConnectionSocket>>& sockets,
- size_t socketsIndexHint) {
- LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
- "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
-
- if (*exclusive != nullptr) return; // consistent with break below
-
- for (size_t i = 0; i < sockets.size(); i++) {
- sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
-
- // take first available connection (intuition = caching)
- if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
- *available = socket;
- continue;
- }
-
- // though, prefer to take connection which is already inuse by this thread
- // (nested transactions)
- if (exclusive && socket->exclusiveTid == tid) {
- *exclusive = socket;
- break; // consistent with return above
- }
- }
-}
-
-RpcConnection::ExclusiveSocket::~ExclusiveSocket() {
- // reentrant use of a connection means something less deep in the call stack
- // is using this fd, and it retains the right to it. So, we don't give up
- // exclusive ownership, and no thread is freed.
- if (!mReentrant) {
- std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
- mSocket->exclusiveTid = std::nullopt;
- if (mConnection->mWaitingThreads > 0) {
- _l.unlock();
- mConnection->mSocketCv.notify_one();
- }
- }
-}
-
-} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index de7160e..9cc6e7f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -22,6 +22,7 @@
#include <thread>
#include <vector>
+#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <log/log.h>
@@ -32,6 +33,7 @@
namespace android {
+using base::ScopeGuard;
using base::unique_fd;
RpcServer::RpcServer() {}
@@ -49,8 +51,6 @@
return setupSocketServer(UnixSocketAddress(path));
}
-#ifdef __BIONIC__
-
bool RpcServer::setupVsockServer(unsigned int port) {
// realizing value w/ this type at compile time to avoid ubsan abort
constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
@@ -58,8 +58,6 @@
return setupSocketServer(VsockSocketAddress(kAnyCid, port));
}
-#endif // __BIONIC__
-
bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
const char* kAddr = "127.0.0.1";
@@ -111,85 +109,130 @@
void RpcServer::setRootObject(const sp<IBinder>& binder) {
std::lock_guard<std::mutex> _l(mLock);
- mRootObject = binder;
+ mRootObjectWeak = mRootObject = binder;
+}
+
+void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mRootObject.clear();
+ mRootObjectWeak = binder;
}
sp<IBinder> RpcServer::getRootObject() {
std::lock_guard<std::mutex> _l(mLock);
- return mRootObject;
+ bool hasWeak = mRootObjectWeak.unsafe_get();
+ sp<IBinder> ret = mRootObjectWeak.promote();
+ ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
+ return ret;
}
void RpcServer::join() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
-
- std::vector<std::thread> pool;
- {
- std::lock_guard<std::mutex> _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.get() == -1, "RpcServer must be setup to join.");
- }
-
while (true) {
- unique_fd clientFd(
- TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
-
- if (clientFd < 0) {
- ALOGE("Could not accept4 socket: %s", strerror(errno));
- continue;
- }
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
- // TODO(b/183988761): cannot trust this simple ID
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
- int32_t id;
- if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
- ALOGE("Could not read ID from fd %d", clientFd.get());
- continue;
- }
-
- {
- std::lock_guard<std::mutex> _l(mLock);
-
- sp<RpcConnection> connection;
- if (id == RPC_CONNECTION_ID_NEW) {
- // new client!
- LOG_ALWAYS_FATAL_IF(mConnectionIdCounter >= INT32_MAX, "Out of connection IDs");
- mConnectionIdCounter++;
-
- connection = RpcConnection::make();
- connection->setForServer(wp<RpcServer>::fromExisting(this), mConnectionIdCounter);
-
- mConnections[mConnectionIdCounter] = connection;
- } else {
- auto it = mConnections.find(id);
- if (it == mConnections.end()) {
- ALOGE("Cannot add thread, no record of connection with ID %d", id);
- continue;
- }
- connection = it->second;
- }
-
- connection->startThread(std::move(clientFd));
- }
+ (void)acceptOne();
}
}
-std::vector<sp<RpcConnection>> RpcServer::listConnections() {
- std::lock_guard<std::mutex> _l(mLock);
- std::vector<sp<RpcConnection>> connections;
- for (auto& [id, connection] : mConnections) {
- (void)id;
- connections.push_back(connection);
+bool RpcServer::acceptOne() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join.");
+
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
+
+ if (clientFd < 0) {
+ ALOGE("Could not accept4 socket: %s", strerror(errno));
+ return false;
}
- return connections;
+ LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ std::thread thread =
+ std::thread(&RpcServer::establishConnection, this,
+ std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+ mConnectingThreads[thread.get_id()] = std::move(thread);
+ }
+
+ return true;
+}
+
+std::vector<sp<RpcSession>> RpcServer::listSessions() {
+ std::lock_guard<std::mutex> _l(mLock);
+ std::vector<sp<RpcSession>> sessions;
+ for (auto& [id, session] : mSessions) {
+ (void)id;
+ sessions.push_back(session);
+ }
+ return sessions;
+}
+
+size_t RpcServer::numUninitializedSessions() {
+ std::lock_guard<std::mutex> _l(mLock);
+ return mConnectingThreads.size();
+}
+
+void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
+ LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object");
+
+ // TODO(b/183988761): cannot trust this simple ID
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ bool idValid = true;
+ int32_t id;
+ if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+ ALOGE("Could not read ID from fd %d", clientFd.get());
+ idValid = false;
+ }
+
+ std::thread thisThread;
+ sp<RpcSession> session;
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+
+ auto threadId = mConnectingThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+ "Must establish connection on owned thread");
+ thisThread = std::move(threadId->second);
+ ScopeGuard detachGuard = [&]() { thisThread.detach(); };
+ mConnectingThreads.erase(threadId);
+
+ if (!idValid) {
+ return;
+ }
+
+ if (id == RPC_SESSION_ID_NEW) {
+ LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+ mSessionIdCounter++;
+
+ session = RpcSession::make();
+ session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+
+ mSessions[mSessionIdCounter] = session;
+ } else {
+ auto it = mSessions.find(id);
+ if (it == mSessions.end()) {
+ ALOGE("Cannot add thread, no record of session with ID %d", id);
+ return;
+ }
+ session = it->second;
+ }
+
+ detachGuard.Disable();
+ session->preJoin(std::move(thisThread));
+ }
+
+ // avoid strong cycle
+ server = nullptr;
+ //
+ //
+ // DO NOT ACCESS MEMBER VARIABLES BELOW
+ //
+
+ session->join(std::move(clientFd));
}
bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
-
- {
- std::lock_guard<std::mutex> _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcServer can only have one server.");
- }
+ LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
@@ -216,4 +259,39 @@
return true;
}
+void RpcServer::onSessionTerminating(const sp<RpcSession>& session) {
+ auto id = session->mId;
+ LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
+ LOG_RPC_DETAIL("Dropping session %d", *id);
+
+ std::lock_guard<std::mutex> _l(mLock);
+ auto it = mSessions.find(*id);
+ LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %d", *id);
+ LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %d", *id);
+ (void)mSessions.erase(it);
+}
+
+bool RpcServer::hasServer() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ return mServer.ok();
+}
+
+unique_fd RpcServer::releaseServer() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ return std::move(mServer);
+}
+
+bool RpcServer::setupExternalServer(base::unique_fd serverFd) {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ if (mServer.ok()) {
+ ALOGE("Each RpcServer can only have one server.");
+ return false;
+ }
+ mServer = std::move(serverFd);
+ return true;
+}
+
} // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
new file mode 100644
index 0000000..05fa49e
--- /dev/null
+++ b/libs/binder/RpcSession.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcSession"
+
+#include <binder/RpcSession.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string_view>
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/Stability.h>
+#include <utils/String8.h>
+
+#include "RpcSocketAddress.h"
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#ifdef __GLIBC__
+extern "C" pid_t gettid();
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcSession::RpcSession() {
+ LOG_RPC_DETAIL("RpcSession created %p", this);
+
+ mState = std::make_unique<RpcState>();
+}
+RpcSession::~RpcSession() {
+ LOG_RPC_DETAIL("RpcSession destroyed %p", this);
+
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mServerConnections.size() != 0,
+ "Should not be able to destroy a session with servers in use.");
+}
+
+sp<RpcSession> RpcSession::make() {
+ return sp<RpcSession>::make();
+}
+
+bool RpcSession::setupUnixDomainClient(const char* path) {
+ return setupSocketClient(UnixSocketAddress(path));
+}
+
+bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
+ return setupSocketClient(VsockSocketAddress(cid, port));
+}
+
+bool RpcSession::setupInetClient(const char* addr, unsigned int port) {
+ auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+ if (aiStart == nullptr) return false;
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
+ if (setupSocketClient(socketAddress)) return true;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
+ return false;
+}
+
+bool RpcSession::addNullDebuggingClient() {
+ unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
+
+ if (serverFd == -1) {
+ ALOGE("Could not connect to /dev/null: %s", strerror(errno));
+ return false;
+ }
+
+ addClientConnection(std::move(serverFd));
+ return true;
+}
+
+sp<IBinder> RpcSession::getRootObject() {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ return state()->getRootObject(connection.fd(), sp<RpcSession>::fromExisting(this));
+}
+
+status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads);
+}
+
+status_t RpcSession::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+ (flags & IBinder::FLAG_ONEWAY) ? ConnectionUse::CLIENT_ASYNC
+ : ConnectionUse::CLIENT);
+ return state()->transact(connection.fd(), address, code, data,
+ sp<RpcSession>::fromExisting(this), reply, flags);
+}
+
+status_t RpcSession::sendDecStrong(const RpcAddress& address) {
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+ ConnectionUse::CLIENT_REFCOUNT);
+ return state()->sendDecStrong(connection.fd(), address);
+}
+
+status_t RpcSession::readId() {
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
+ }
+
+ int32_t id;
+
+ ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+ status_t status =
+ state()->getSessionId(connection.fd(), sp<RpcSession>::fromExisting(this), &id);
+ if (status != OK) return status;
+
+ LOG_RPC_DETAIL("RpcSession %p has id %d", this, id);
+ mId = id;
+ return OK;
+}
+
+void RpcSession::preJoin(std::thread thread) {
+ LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ mThreads[thread.get_id()] = std::move(thread);
+ }
+}
+
+void RpcSession::join(unique_fd client) {
+ // must be registered to allow arbitrary client code executing commands to
+ // be able to do nested calls (we can't only read from it)
+ sp<RpcConnection> connection = assignServerToThisThread(std::move(client));
+
+ while (true) {
+ status_t error =
+ state()->getAndExecuteCommand(connection->fd, sp<RpcSession>::fromExisting(this));
+
+ if (error != OK) {
+ ALOGI("Binder connection thread closing w/ status %s", statusToString(error).c_str());
+ break;
+ }
+ }
+
+ LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
+ "bad state: connection object guaranteed to be in list");
+
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ auto it = mThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(it == mThreads.end());
+ it->second.detach();
+ mThreads.erase(it);
+ }
+}
+
+void RpcSession::terminateLocked() {
+ // TODO(b/185167543):
+ // - kindly notify other side of the connection of termination (can't be
+ // locked)
+ // - prevent new client/servers from being added
+ // - stop all threads which are currently reading/writing
+ // - terminate RpcState?
+
+ if (mTerminated) return;
+
+ sp<RpcServer> server = mForServer.promote();
+ if (server) {
+ server->onSessionTerminating(sp<RpcSession>::fromExisting(this));
+ }
+}
+
+wp<RpcServer> RpcSession::server() {
+ return mForServer;
+}
+
+bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mClientConnections.size() != 0,
+ "Must only setup session once, but already has %zu clients",
+ mClientConnections.size());
+ }
+
+ if (!setupOneSocketClient(addr, RPC_SESSION_ID_NEW)) return false;
+
+ // TODO(b/185167543): we should add additional sessions dynamically
+ // instead of all at once.
+ // TODO(b/186470974): first risk of blocking
+ size_t numThreadsAvailable;
+ if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) {
+ ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
+ statusToString(status).c_str());
+ return false;
+ }
+
+ if (status_t status = readId(); status != OK) {
+ ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(),
+ statusToString(status).c_str());
+ return false;
+ }
+
+ // we've already setup one client
+ for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+ // TODO(b/185167543): shutdown existing connections?
+ if (!setupOneSocketClient(addr, mId.value())) return false;
+ }
+
+ return true;
+}
+
+bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
+ for (size_t tries = 0; tries < 5; tries++) {
+ if (tries > 0) usleep(10000);
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ if (errno == ECONNRESET) {
+ ALOGW("Connection reset on %s", addr.toString().c_str());
+ continue;
+ }
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
+ int savedErrno = errno;
+ ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+ addClientConnection(std::move(serverFd));
+ return true;
+ }
+
+ ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
+ return false;
+}
+
+void RpcSession::addClientConnection(unique_fd fd) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcConnection> session = sp<RpcConnection>::make();
+ session->fd = std::move(fd);
+ mClientConnections.push_back(session);
+}
+
+void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) {
+ mId = sessionId;
+ mForServer = server;
+}
+
+sp<RpcSession::RpcConnection> RpcSession::assignServerToThisThread(unique_fd fd) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ sp<RpcConnection> session = sp<RpcConnection>::make();
+ session->fd = std::move(fd);
+ session->exclusiveTid = gettid();
+ mServerConnections.push_back(session);
+
+ return session;
+}
+
+bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) {
+ std::lock_guard<std::mutex> _l(mMutex);
+ if (auto it = std::find(mServerConnections.begin(), mServerConnections.end(), connection);
+ it != mServerConnections.end()) {
+ mServerConnections.erase(it);
+ if (mServerConnections.size() == 0) {
+ terminateLocked();
+ }
+ return true;
+ }
+ return false;
+}
+
+RpcSession::ExclusiveConnection::ExclusiveConnection(const sp<RpcSession>& session,
+ ConnectionUse use)
+ : mSession(session) {
+ pid_t tid = gettid();
+ std::unique_lock<std::mutex> _l(mSession->mMutex);
+
+ mSession->mWaitingThreads++;
+ while (true) {
+ sp<RpcConnection> exclusive;
+ sp<RpcConnection> available;
+
+ // CHECK FOR DEDICATED CLIENT SOCKET
+ //
+ // A server/looper should always use a dedicated session if available
+ findConnection(tid, &exclusive, &available, mSession->mClientConnections,
+ mSession->mClientConnectionsOffset);
+
+ // WARNING: this assumes a server cannot request its client to send
+ // a transaction, as mServerConnections is excluded below.
+ //
+ // Imagine we have more than one thread in play, and a single thread
+ // sends a synchronous, then an asynchronous command. Imagine the
+ // asynchronous command is sent on the first client connection. Then, if
+ // we naively send a synchronous command to that same connection, the
+ // thread on the far side might be busy processing the asynchronous
+ // command. So, we move to considering the second available thread
+ // for subsequent calls.
+ if (use == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+ mSession->mClientConnectionsOffset =
+ (mSession->mClientConnectionsOffset + 1) % mSession->mClientConnections.size();
+ }
+
+ // USE SERVING SOCKET (for nested transaction)
+ //
+ // asynchronous calls cannot be nested
+ if (use != ConnectionUse::CLIENT_ASYNC) {
+ // server connections are always assigned to a thread
+ findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServerConnections,
+ 0 /* index hint */);
+ }
+
+ // if our thread is already using a session, prioritize using that
+ if (exclusive != nullptr) {
+ mConnection = exclusive;
+ mReentrant = true;
+ break;
+ } else if (available != nullptr) {
+ mConnection = available;
+ mConnection->exclusiveTid = tid;
+ break;
+ }
+
+ // in regular binder, this would usually be a deadlock :)
+ LOG_ALWAYS_FATAL_IF(mSession->mClientConnections.size() == 0,
+ "Not a client of any session. You must create a session to an "
+ "RPC server to make any non-nested (e.g. oneway or on another thread) "
+ "calls.");
+
+ LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...",
+ mSession->mClientConnections.size(), mSession->mServerConnections.size());
+ mSession->mAvailableConnectionCv.wait(_l);
+ }
+ mSession->mWaitingThreads--;
+}
+
+void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ sp<RpcConnection>* available,
+ std::vector<sp<RpcConnection>>& sockets,
+ size_t socketsIndexHint) {
+ LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
+ "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
+
+ if (*exclusive != nullptr) return; // consistent with break below
+
+ for (size_t i = 0; i < sockets.size(); i++) {
+ sp<RpcConnection>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
+
+ // take first available session (intuition = caching)
+ if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
+ *available = socket;
+ continue;
+ }
+
+ // though, prefer to take session which is already inuse by this thread
+ // (nested transactions)
+ if (exclusive && socket->exclusiveTid == tid) {
+ *exclusive = socket;
+ break; // consistent with return above
+ }
+ }
+}
+
+RpcSession::ExclusiveConnection::~ExclusiveConnection() {
+ // reentrant use of a session means something less deep in the call stack
+ // is using this fd, and it retains the right to it. So, we don't give up
+ // exclusive ownership, and no thread is freed.
+ if (!mReentrant) {
+ std::unique_lock<std::mutex> _l(mSession->mMutex);
+ mConnection->exclusiveTid = std::nullopt;
+ if (mSession->mWaitingThreads > 0) {
+ _l.unlock();
+ mSession->mAvailableConnectionCv.notify_one();
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h
index c6a06cf..c7ba5d9 100644
--- a/libs/binder/RpcSocketAddress.h
+++ b/libs/binder/RpcSocketAddress.h
@@ -24,9 +24,7 @@
#include <sys/types.h>
#include <sys/un.h>
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif
+#include "vm_sockets.h"
namespace android {
@@ -59,8 +57,6 @@
sockaddr_un mAddr;
};
-#ifdef __BIONIC__
-
class VsockSocketAddress : public RpcSocketAddress {
public:
VsockSocketAddress(unsigned int cid, unsigned int port)
@@ -80,8 +76,6 @@
sockaddr_vm mAddr;
};
-#endif // __BIONIC__
-
class InetSocketAddress : public RpcSocketAddress {
public:
InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port)
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 19dea7e..e5a6026 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,7 +18,9 @@
#include "RpcState.h"
+#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
#include "Debug.h"
@@ -28,19 +30,21 @@
namespace android {
+using base::ScopeGuard;
+
RpcState::RpcState() {}
RpcState::~RpcState() {}
-status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder,
+status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
RpcAddress* outAddress) {
bool isRemote = binder->remoteBinder();
bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
- if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) {
+ if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) {
// We need to be able to send instructions over the socket for how to
// connect to a different server, and we also need to let the host
// process know that this is happening.
- ALOGE("Cannot send binder from unrelated binder RPC connection.");
+ ALOGE("Cannot send binder from unrelated binder RPC session.");
return INVALID_OPERATION;
}
@@ -91,8 +95,7 @@
return OK;
}
-sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
- const RpcAddress& address) {
+sp<IBinder> RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address) {
std::unique_lock<std::mutex> _l(mNodeMutex);
if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -106,7 +109,7 @@
// We have timesRecd RPC refcounts, but we only need to hold on to one
// when we keep the object. All additional dec strongs are sent
// immediately, we wait to send the last one in BpBinder::onLastDecStrong.
- (void)connection->sendDecStrong(address);
+ (void)session->sendDecStrong(address);
return binder;
}
@@ -114,9 +117,9 @@
auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");
- // Currently, all binders are assumed to be part of the same connection (no
+ // Currently, all binders are assumed to be part of the same session (no
// device global binders in the RPC world).
- sp<IBinder> binder = BpBinder::create(connection, it->first);
+ sp<IBinder> binder = BpBinder::create(session, it->first);
it->second.binder = binder;
it->second.timesRecd = 1;
return binder;
@@ -183,6 +186,27 @@
}
}
+RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+ // The maximum size for regular binder is 1MB for all concurrent
+ // transactions. A very small proportion of transactions are even
+ // larger than a page, but we need to avoid allocating too much
+ // data on behalf of an arbitrary client, or we could risk being in
+ // a position where a single additional allocation could run out of
+ // memory.
+ //
+ // Note, this limit may not reflect the total amount of data allocated for a
+ // transaction (in some cases, additional fixed size amounts are added),
+ // though for rough consistency, we should avoid cases where this data type
+ // is used for multiple dynamic allocations for a single transaction.
+ constexpr size_t kMaxTransactionAllocation = 100 * 1000;
+ if (size == 0) return;
+ if (size > kMaxTransactionAllocation) {
+ ALOGW("Transaction requested too much data allocation %zu", size);
+ return;
+ }
+ mData.reset(new (std::nothrow) uint8_t[size]);
+}
+
bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
@@ -232,14 +256,13 @@
return true;
}
-sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
- const sp<RpcConnection>& connection) {
+sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session) {
Parcel data;
- data.markForRpc(connection);
+ data.markForRpc(session);
Parcel reply;
- status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
- connection, &reply, 0);
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, session,
+ &reply, 0);
if (status != OK) {
ALOGE("Error getting root object: %s", statusToString(status).c_str());
return nullptr;
@@ -248,14 +271,14 @@
return reply.readStrongBinder();
}
-status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
size_t* maxThreadsOut) {
Parcel data;
- data.markForRpc(connection);
+ data.markForRpc(session);
Parcel reply;
status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
- connection, &reply, 0);
+ session, &reply, 0);
if (status != OK) {
ALOGE("Error getting max threads: %s", statusToString(status).c_str());
return status;
@@ -273,29 +296,29 @@
return OK;
}
-status_t RpcState::getConnectionId(const base::unique_fd& fd, const sp<RpcConnection>& connection,
- int32_t* connectionIdOut) {
+status_t RpcState::getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+ int32_t* sessionIdOut) {
Parcel data;
- data.markForRpc(connection);
+ data.markForRpc(session);
Parcel reply;
- status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID, data,
- connection, &reply, 0);
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
+ session, &reply, 0);
if (status != OK) {
- ALOGE("Error getting connection ID: %s", statusToString(status).c_str());
+ ALOGE("Error getting session ID: %s", statusToString(status).c_str());
return status;
}
- int32_t connectionId;
- status = reply.readInt32(&connectionId);
+ int32_t sessionId;
+ status = reply.readInt32(&sessionId);
if (status != OK) return status;
- *connectionIdOut = connectionId;
+ *sessionIdOut = sessionId;
return OK;
}
status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
- const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
+ const Parcel& data, const sp<RpcSession>& session, Parcel* reply,
uint32_t flags) {
uint64_t asyncNumber = 0;
@@ -328,7 +351,11 @@
.asyncNumber = asyncNumber,
};
- std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+ CommandData transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+ if (!transactionData.valid()) {
+ return NO_MEMORY;
+ }
+
memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());
@@ -355,7 +382,7 @@
LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
- return waitForReply(fd, connection, reply);
+ return waitForReply(fd, session, reply);
}
static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -367,7 +394,7 @@
LOG_ALWAYS_FATAL_IF(objectsCount, 0);
}
-status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
Parcel* reply) {
RpcWireHeader command;
while (true) {
@@ -377,13 +404,16 @@
if (command.command == RPC_COMMAND_REPLY) break;
- status_t status = processServerCommand(fd, connection, command);
+ status_t status = processServerCommand(fd, session, command);
if (status != OK) return status;
}
- uint8_t* data = new uint8_t[command.bodySize];
+ CommandData data(command.bodySize);
+ if (!data.valid()) {
+ return NO_MEMORY;
+ }
- if (!rpcRec(fd, "reply body", data, command.bodySize)) {
+ if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) {
return DEAD_OBJECT;
}
@@ -393,13 +423,14 @@
terminate();
return BAD_VALUE;
}
- RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
+ RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
if (rpcReply->status != OK) return rpcReply->status;
+ data.release();
reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
nullptr, 0, cleanup_reply_data);
- reply->markForRpc(connection);
+ reply->markForRpc(session);
return OK;
}
@@ -430,8 +461,7 @@
return OK;
}
-status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection) {
+status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, const sp<RpcSession>& session) {
LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
RpcWireHeader command;
@@ -439,15 +469,26 @@
return DEAD_OBJECT;
}
- return processServerCommand(fd, connection, command);
+ return processServerCommand(fd, session, command);
}
-status_t RpcState::processServerCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
+ IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
+ IPCThreadState::SpGuard spGuard{"processing binder RPC command"};
+ IPCThreadState::SpGuard* origGuard;
+ if (kernelBinderState != nullptr) {
+ origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
+ }
+ ScopeGuard guardUnguard = [&]() {
+ if (kernelBinderState != nullptr) {
+ kernelBinderState->restoreGetCallingSpGuard(origGuard);
+ }
+ };
+
switch (command.command) {
case RPC_COMMAND_TRANSACT:
- return processTransact(fd, connection, command);
+ return processTransact(fd, session, command);
case RPC_COMMAND_DEC_STRONG:
return processDecStrong(fd, command);
}
@@ -456,21 +497,24 @@
// RPC-binder-level wire protocol is not self synchronizing, we have no way
// to understand where the current command ends and the next one begins. We
// also can't consider it a fatal error because this would allow any client
- // to kill us, so ending the connection for misbehaving client.
- ALOGE("Unknown RPC command %d - terminating connection", command.command);
+ // to kill us, so ending the session for misbehaving client.
+ ALOGE("Unknown RPC command %d - terminating session", command.command);
terminate();
return DEAD_OBJECT;
}
-status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
- std::vector<uint8_t> transactionData(command.bodySize);
+ CommandData transactionData(command.bodySize);
+ if (!transactionData.valid()) {
+ return NO_MEMORY;
+ }
if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
return DEAD_OBJECT;
}
- return processTransactInternal(fd, connection, std::move(transactionData));
+ return processTransactInternal(fd, session, std::move(transactionData));
}
static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -482,9 +526,8 @@
(void)objectsCount;
}
-status_t RpcState::processTransactInternal(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
- std::vector<uint8_t>&& transactionData) {
+status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
+ CommandData transactionData) {
if (transactionData.size() < sizeof(RpcWireTransaction)) {
ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
sizeof(RpcWireTransaction), transactionData.size());
@@ -505,7 +548,6 @@
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %s.", addr.toString().c_str());
- dump();
replyStatus = BAD_VALUE;
} else {
target = it->second.binder.promote();
@@ -515,7 +557,7 @@
// However, for local binders, it indicates a misbehaving client
// (any binder which is being transacted on should be holding a
// strong ref count), so in either case, terminating the
- // connection.
+ // session.
ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
addr.toString().c_str());
terminate();
@@ -545,7 +587,7 @@
}
Parcel reply;
- reply.markForRpc(connection);
+ reply.markForRpc(session);
if (replyStatus == OK) {
Parcel data;
@@ -556,14 +598,14 @@
transactionData.size() - offsetof(RpcWireTransaction, data),
nullptr /*object*/, 0 /*objectCount*/,
do_nothing_to_transact_data);
- data.markForRpc(connection);
+ data.markForRpc(session);
if (target) {
replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
} else {
LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
- sp<RpcServer> server = connection->server().promote();
+ sp<RpcServer> server = session->server().promote();
if (server) {
// special case for 'zero' address (special server commands)
switch (transaction->code) {
@@ -575,13 +617,13 @@
replyStatus = reply.writeInt32(server->getMaxThreads());
break;
}
- case RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID: {
- // only connections w/ services can be the source of a
- // connection ID (so still guarded by non-null server)
+ case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+ // only sessions w/ services can be the source of a
+ // session ID (so still guarded by non-null server)
//
- // connections associated with servers must have an ID
+ // sessions associated with servers must have an ID
// (hence abort)
- int32_t id = connection->getPrivateAccessorForId().get().value();
+ int32_t id = session->getPrivateAccessorForId().get().value();
replyStatus = reply.writeInt32(id);
break;
}
@@ -635,11 +677,11 @@
// justification for const_cast (consider avoiding priority_queue):
// - AsyncTodo operator< doesn't depend on 'data' object
// - gotta go fast
- std::vector<uint8_t> data = std::move(
+ CommandData data = std::move(
const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
it->second.asyncTodo.pop();
_l.unlock();
- return processTransactInternal(fd, connection, std::move(data));
+ return processTransactInternal(fd, session, std::move(data));
}
}
return OK;
@@ -649,7 +691,10 @@
.status = replyStatus,
};
- std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
+ CommandData replyData(sizeof(RpcWireReply) + reply.dataSize());
+ if (!replyData.valid()) {
+ return NO_MEMORY;
+ }
memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());
@@ -676,7 +721,10 @@
status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
- std::vector<uint8_t> commandData(command.bodySize);
+ CommandData commandData(command.bodySize);
+ if (!commandData.valid()) {
+ return NO_MEMORY;
+ }
if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
return DEAD_OBJECT;
}
@@ -695,7 +743,6 @@
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
- dump();
return OK;
}
@@ -728,7 +775,7 @@
}
_l.unlock();
- tempHold = nullptr; // destructor may make binder calls on this connection
+ tempHold = nullptr; // destructor may make binder calls on this session
return OK;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 825fd7c..31f8a22 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -18,9 +18,10 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <map>
+#include <optional>
#include <queue>
namespace android {
@@ -43,7 +44,7 @@
/**
* Abstracts away management of ref counts and the wire format from
- * RpcConnection
+ * RpcSession
*/
class RpcState {
public:
@@ -51,71 +52,83 @@
~RpcState();
// TODO(b/182940634): combine some special transactions into one "getServerInfo" call?
- sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
- status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+ sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session);
+ status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
size_t* maxThreadsOut);
- status_t getConnectionId(const base::unique_fd& fd, const sp<RpcConnection>& connection,
- int32_t* connectionIdOut);
+ status_t getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+ int32_t* sessionIdOut);
[[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
uint32_t code, const Parcel& data,
- const sp<RpcConnection>& connection, Parcel* reply,
- uint32_t flags);
+ const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
[[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
[[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection);
+ const sp<RpcSession>& session);
/**
* Called by Parcel for outgoing binders. This implies one refcount of
* ownership to the outgoing binder.
*/
- [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection,
- const sp<IBinder>& binder, RpcAddress* outAddress);
+ [[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
+ RpcAddress* outAddress);
/**
* Called by Parcel for incoming binders. This either returns the refcount
* to the process, if this process already has one, or it takes ownership of
* that refcount
*/
- sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address);
+ sp<IBinder> onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address);
size_t countBinders();
void dump();
private:
/**
- * Called when reading or writing data to a connection fails to clean up
- * data associated with the connection in order to cleanup binders.
+ * Called when reading or writing data to a session fails to clean up
+ * data associated with the session in order to cleanup binders.
* Specifically, we have a strong dependency cycle, since BpBinder is
* OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true).
*
- * BpBinder -> RpcConnection -> RpcState
+ * BpBinder -> RpcSession -> RpcState
* ^-----------------------------/
*
* In the success case, eventually all refcounts should be propagated over
- * the connection, though this could also be called to eagerly cleanup
- * the connection.
+ * the session, though this could also be called to eagerly cleanup
+ * the session.
*
- * WARNING: RpcState is responsible for calling this when the connection is
+ * WARNING: RpcState is responsible for calling this when the session is
* no longer recoverable.
*/
void terminate();
+ // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
+ // large allocations to avoid being requested from allocating too much data.
+ struct CommandData {
+ explicit CommandData(size_t size);
+ bool valid() { return mSize == 0 || mData != nullptr; }
+ size_t size() { return mSize; }
+ uint8_t* data() { return mData.get(); }
+ uint8_t* release() { return mData.release(); }
+
+ private:
+ std::unique_ptr<uint8_t[]> mData;
+ size_t mSize;
+ };
+
[[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
size_t size);
[[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
- [[nodiscard]] status_t waitForReply(const base::unique_fd& fd,
- const sp<RpcConnection>& connection, Parcel* reply);
+ [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
+ Parcel* reply);
[[nodiscard]] status_t processServerCommand(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ const sp<RpcSession>& session,
const RpcWireHeader& command);
- [[nodiscard]] status_t processTransact(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
+ [[nodiscard]] status_t processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command);
[[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
- const sp<RpcConnection>& connection,
- std::vector<uint8_t>&& transactionData);
+ const sp<RpcSession>& session,
+ CommandData transactionData);
[[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
const RpcWireHeader& command);
@@ -150,7 +163,7 @@
// async transaction queue, _only_ for local binder
struct AsyncTodo {
- std::vector<uint8_t> data; // most convenient format, to move it here
+ CommandData data;
uint64_t asyncNumber = 0;
bool operator<(const AsyncTodo& o) const {
@@ -168,7 +181,7 @@
std::mutex mNodeMutex;
bool mTerminated = false;
- // binders known by both sides of a connection
+ // binders known by both sides of a session
std::map<RpcAddress, BinderNode> mNodeForAddress;
};
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index a7e8a52..c5fa008 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -48,10 +48,10 @@
enum : uint32_t {
RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1,
- RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID = 2,
+ RPC_SPECIAL_TRANSACT_GET_SESSION_ID = 2,
};
-constexpr int32_t RPC_CONNECTION_ID_NEW = -1;
+constexpr int32_t RPC_SESSION_ID_NEW = -1;
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index ad618f9..61bf018 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -28,7 +28,7 @@
// ---------------------------------------------------------------------------
namespace android {
-class RpcConnection;
+class RpcSession;
class RpcState;
namespace internal {
class Stability;
@@ -41,11 +41,11 @@
{
public:
static sp<BpBinder> create(int32_t handle);
- static sp<BpBinder> create(const sp<RpcConnection>& connection, const RpcAddress& address);
+ static sp<BpBinder> create(const sp<RpcSession>& session, const RpcAddress& address);
/**
* Return value:
- * true - this is associated with a socket RpcConnection
+ * true - this is associated with a socket RpcSession
* false - (usual) binder over e.g. /dev/binder
*/
bool isRpcBinder() const;
@@ -133,7 +133,7 @@
// valid if isRpcBinder
const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
- const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); }
+ const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
const BpBinder* mBinder;
};
@@ -148,19 +148,19 @@
struct BinderHandle {
int32_t handle;
};
- struct SocketHandle {
- sp<RpcConnection> connection;
+ struct RpcHandle {
+ sp<RpcSession> session;
RpcAddress address;
};
- using Handle = std::variant<BinderHandle, SocketHandle>;
+ using Handle = std::variant<BinderHandle, RpcHandle>;
int32_t binderHandle() const;
const RpcAddress& rpcAddress() const;
- const sp<RpcConnection>& rpcConnection() const;
+ const sp<RpcSession>& rpcSession() const;
explicit BpBinder(Handle&& handle);
BpBinder(BinderHandle&& handle, int32_t trackedUid);
- explicit BpBinder(SocketHandle&& handle);
+ explicit BpBinder(RpcHandle&& handle);
virtual ~BpBinder();
virtual void onFirstRef();
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 23a0cb0..5220b62 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -81,6 +81,32 @@
*/
uid_t getCallingUid() const;
+ /**
+ * Make it an abort to rely on getCalling* for a section of
+ * execution.
+ *
+ * Usage:
+ * IPCThreadState::SpGuard guard { "..." };
+ * auto* orig = pushGetCallingSpGuard(&guard);
+ * {
+ * // will abort if you call getCalling*, unless you are
+ * // serving a nested binder transaction
+ * }
+ * restoreCallingSpGuard(orig);
+ */
+ struct SpGuard {
+ const char* context;
+ };
+ SpGuard* pushGetCallingSpGuard(SpGuard* guard);
+ void restoreGetCallingSpGuard(SpGuard* guard);
+ /**
+ * Used internally by getCalling*. Can also be used to assert that
+ * you are in a binder context (getCalling* is valid). This is
+ * intentionally not exposed as a boolean API since code should be
+ * written to know its environment.
+ */
+ void checkContextIsBinderForUse(const char* use) const;
+
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
@@ -203,6 +229,7 @@
Parcel mOut;
status_t mLastError;
const void* mServingStackPointer;
+ SpGuard* mServingStackPointerGuard;
pid_t mCallingPid;
const char* mCallingSid;
uid_t mCallingUid;
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 9659732..f3ba830 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -50,8 +50,12 @@
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
/**
* Force the service to persist, even when it has 0 clients.
- * If setting this flag from the server side, make sure to do so before calling registerService,
- * or there may be a race with the default dynamic shutdown.
+ * If setting this flag from the server side, make sure to do so before calling
+ * registerService, or there may be a race with the default dynamic shutdown.
+ *
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism such as ctl.start.
*/
void forcePersist(bool persist);
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9578372..5aaaa0c 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -50,7 +50,7 @@
class IBinder;
class IPCThreadState;
class ProcessState;
-class RpcConnection;
+class RpcSession;
class String8;
class TextOutput;
@@ -103,7 +103,7 @@
// Whenever possible, markForBinder should be preferred. This method is
// called automatically on reply Parcels for RPC transactions.
- void markForRpc(const sp<RpcConnection>& connection);
+ void markForRpc(const sp<RpcSession>& session);
// Whether this Parcel is written for RPC transactions (after calls to
// markForBinder or markForRpc).
@@ -1136,7 +1136,7 @@
release_func mOwner;
- sp<RpcConnection> mConnection;
+ sp<RpcSession> mSession;
class Blob {
public:
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
deleted file mode 100644
index 87984d7..0000000
--- a/libs/binder/include/binder/RpcConnection.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <android-base/unique_fd.h>
-#include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <map>
-#include <optional>
-#include <thread>
-#include <vector>
-
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
-namespace android {
-
-class Parcel;
-class RpcServer;
-class RpcSocketAddress;
-class RpcState;
-
-/**
- * This represents a multi-threaded/multi-socket connection between a client
- * and a server.
- */
-class RpcConnection final : public virtual RefBase {
-public:
- static sp<RpcConnection> make();
-
- /**
- * This should be called once per thread, matching 'join' in the remote
- * process.
- */
- [[nodiscard]] bool setupUnixDomainClient(const char* path);
-
-#ifdef __BIONIC__
- /**
- * Connects to an RPC server at the CVD & port.
- */
- [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
-#endif // __BIONIC__
-
- /**
- * Connects to an RPC server at the given address and port.
- */
- [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
-
- /**
- * For debugging!
- *
- * Sets up an empty socket. All queries to this socket which require a
- * response will never be satisfied. All data sent here will be
- * unceremoniously cast down the bottomless pit, /dev/null.
- */
- [[nodiscard]] bool addNullDebuggingClient();
-
- /**
- * Query the other side of the connection for the root object hosted by that
- * process's RpcServer (if one exists)
- */
- sp<IBinder> getRootObject();
-
- /**
- * Query the other side of the connection for the maximum number of threads
- * it supports (maximum number of concurrent non-nested synchronous transactions)
- */
- status_t getMaxThreads(size_t* maxThreads);
-
- [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
- [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
-
- ~RpcConnection();
-
- wp<RpcServer> server();
-
- // internal only
- const std::unique_ptr<RpcState>& state() { return mState; }
-
- class PrivateAccessorForId {
- private:
- friend class RpcConnection;
- friend class RpcState;
- explicit PrivateAccessorForId(const RpcConnection* connection) : mConnection(connection) {}
-
- const std::optional<int32_t> get() { return mConnection->mId; }
-
- const RpcConnection* mConnection;
- };
- PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); }
-
-private:
- friend PrivateAccessorForId;
- friend sp<RpcConnection>;
- friend RpcServer;
- RpcConnection();
-
- status_t readId();
-
- void startThread(base::unique_fd client);
- void join(base::unique_fd client);
-
- struct ConnectionSocket : public RefBase {
- base::unique_fd fd;
-
- // whether this or another thread is currently using this fd to make
- // or receive transactions.
- std::optional<pid_t> exclusiveTid;
- };
-
- bool setupSocketClient(const RpcSocketAddress& address);
- bool setupOneSocketClient(const RpcSocketAddress& address, int32_t connectionId);
- void addClient(base::unique_fd fd);
- void setForServer(const wp<RpcServer>& server, int32_t connectionId);
- sp<ConnectionSocket> assignServerToThisThread(base::unique_fd fd);
- bool removeServerSocket(const sp<ConnectionSocket>& socket);
-
- enum class SocketUse {
- CLIENT,
- CLIENT_ASYNC,
- CLIENT_REFCOUNT,
- };
-
- // RAII object for connection socket
- class ExclusiveSocket {
- public:
- explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use);
- ~ExclusiveSocket();
- const base::unique_fd& fd() { return mSocket->fd; }
-
- private:
- static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
- sp<ConnectionSocket>* available,
- std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint);
-
- sp<RpcConnection> mConnection; // avoid deallocation
- sp<ConnectionSocket> mSocket;
-
- // whether this is being used for a nested transaction (being on the same
- // thread guarantees we won't write in the middle of a message, the way
- // the wire protocol is constructed guarantees this is safe).
- bool mReentrant = false;
- };
-
- // On the other side of a connection, for each of mClients here, there should
- // be one of mServers on the other side (and vice versa).
- //
- // For the simplest connection, a single server with one client, you would
- // have:
- // - the server has a single 'mServers' and a thread listening on this
- // - the client has a single 'mClients' and makes calls to this
- // - here, when the client makes a call, the server can call back into it
- // (nested calls), but outside of this, the client will only ever read
- // calls from the server when it makes a call itself.
- //
- // For a more complicated case, the client might itself open up a thread to
- // serve calls to the server at all times (e.g. if it hosts a callback)
-
- wp<RpcServer> mForServer; // maybe null, for client connections
-
- // TODO(b/183988761): this shouldn't be guessable
- std::optional<int32_t> mId;
-
- std::unique_ptr<RpcState> mState;
-
- std::mutex mSocketMutex; // for all below
-
- std::condition_variable mSocketCv; // for mWaitingThreads
- size_t mWaitingThreads = 0;
- size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
- std::vector<sp<ConnectionSocket>> mClients;
- std::vector<sp<ConnectionSocket>> mServers;
-
- // TODO(b/185167543): use for reverse connections (allow client to also
- // serve calls on a connection).
- // TODO(b/185167543): allow sharing between different connections in a
- // process? (or combine with mServers)
- std::map<std::thread::id, std::thread> mThreads;
-};
-
-} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 81ea3a7..8f0c6fd 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -17,11 +17,12 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <mutex>
+#include <thread>
// WARNING: This is a feature which is still in development, and it is subject
// to radical change. Any production use of this may subject your code to any
@@ -48,21 +49,19 @@
static sp<RpcServer> make();
/**
- * This represents a connection for responses, e.g.:
+ * This represents a session for responses, e.g.:
*
* process A serves binder a
- * process B opens a connection to process A
+ * process B opens a session to process A
* process B makes binder b and sends it to A
- * A uses this 'back connection' to send things back to B
+ * A uses this 'back session' to send things back to B
*/
[[nodiscard]] bool setupUnixDomainServer(const char* path);
-#ifdef __BIONIC__
/**
* Creates an RPC server at the current port.
*/
[[nodiscard]] bool setupVsockServer(unsigned int port);
-#endif // __BIONIC__
/**
* Creates an RPC server at the current port using IPv4.
@@ -75,10 +74,26 @@
*/
[[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+ /**
+ * If setup*Server has been successful, return true. Otherwise return false.
+ */
+ [[nodiscard]] bool hasServer();
+
+ /**
+ * If hasServer(), return the server FD. Otherwise return invalid FD.
+ */
+ [[nodiscard]] base::unique_fd releaseServer();
+
+ /**
+ * Set up server using an external FD previously set up by releaseServer().
+ * Return false if there's already a server.
+ */
+ bool setupExternalServer(base::unique_fd serverFd);
+
void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
/**
- * This must be called before adding a client connection.
+ * This must be called before adding a client session.
*
* If this is not specified, this will be a single-threaded server.
*
@@ -91,39 +106,59 @@
/**
* The root object can be retrieved by any client, without any
* authentication. TODO(b/183988761)
+ *
+ * Holds a strong reference to the root object.
*/
void setRootObject(const sp<IBinder>& binder);
+ /**
+ * Holds a weak reference to the root object.
+ */
+ void setRootObjectWeak(const wp<IBinder>& binder);
sp<IBinder> getRootObject();
/**
- * You must have at least one client connection before calling this.
+ * You must have at least one client session before calling this.
*
* TODO(b/185167543): way to shut down?
*/
void join();
/**
+ * Accept one connection on this server. You must have at least one client
+ * session before calling this.
+ */
+ [[nodiscard]] bool acceptOne();
+
+ /**
* For debugging!
*/
- std::vector<sp<RpcConnection>> listConnections();
+ std::vector<sp<RpcSession>> listSessions();
+ size_t numUninitializedSessions();
~RpcServer();
+ // internal use only
+
+ void onSessionTerminating(const sp<RpcSession>& session);
+
private:
friend sp<RpcServer>;
RpcServer();
+ void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
bool setupSocketServer(const RpcSocketAddress& address);
bool mAgreedExperimental = false;
bool mStarted = false; // TODO(b/185167543): support dynamically added clients
size_t mMaxThreads = 1;
- base::unique_fd mServer; // socket we are accepting connections on
+ base::unique_fd mServer; // socket we are accepting sessions on
std::mutex mLock; // for below
+ std::map<std::thread::id, std::thread> mConnectingThreads;
sp<IBinder> mRootObject;
- std::map<int32_t, sp<RpcConnection>> mConnections;
- int32_t mConnectionIdCounter = 0;
+ wp<IBinder> mRootObjectWeak;
+ std::map<int32_t, sp<RpcSession>> mSessions;
+ int32_t mSessionIdCounter = 0;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
new file mode 100644
index 0000000..bcc213c
--- /dev/null
+++ b/libs/binder/include/binder/RpcSession.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <optional>
+#include <thread>
+#include <vector>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+class RpcServer;
+class RpcSocketAddress;
+class RpcState;
+
+/**
+ * This represents a session (group of connections) between a client
+ * and a server. Multiple connections are needed for multiple parallel "binder"
+ * calls which may also have nested calls.
+ */
+class RpcSession final : public virtual RefBase {
+public:
+ static sp<RpcSession> make();
+
+ /**
+ * This should be called once per thread, matching 'join' in the remote
+ * process.
+ */
+ [[nodiscard]] bool setupUnixDomainClient(const char* path);
+
+ /**
+ * Connects to an RPC server at the CVD & port.
+ */
+ [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
+
+ /**
+ * Connects to an RPC server at the given address and port.
+ */
+ [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
+
+ /**
+ * For debugging!
+ *
+ * Sets up an empty connection. All queries to this connection which require a
+ * response will never be satisfied. All data sent here will be
+ * unceremoniously cast down the bottomless pit, /dev/null.
+ */
+ [[nodiscard]] bool addNullDebuggingClient();
+
+ /**
+ * Query the other side of the session for the root object hosted by that
+ * process's RpcServer (if one exists)
+ */
+ sp<IBinder> getRootObject();
+
+ /**
+ * Query the other side of the session for the maximum number of threads
+ * it supports (maximum number of concurrent non-nested synchronous transactions)
+ */
+ status_t getRemoteMaxThreads(size_t* maxThreads);
+
+ [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+ [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+ ~RpcSession();
+
+ wp<RpcServer> server();
+
+ // internal only
+ const std::unique_ptr<RpcState>& state() { return mState; }
+
+ class PrivateAccessorForId {
+ private:
+ friend class RpcSession;
+ friend class RpcState;
+ explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {}
+
+ const std::optional<int32_t> get() { return mSession->mId; }
+
+ const RpcSession* mSession;
+ };
+ PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); }
+
+private:
+ friend PrivateAccessorForId;
+ friend sp<RpcSession>;
+ friend RpcServer;
+ RpcSession();
+
+ status_t readId();
+
+ // transfer ownership of thread
+ void preJoin(std::thread thread);
+ // join on thread passed to preJoin
+ void join(base::unique_fd client);
+ void terminateLocked();
+
+ struct RpcConnection : public RefBase {
+ base::unique_fd fd;
+
+ // whether this or another thread is currently using this fd to make
+ // or receive transactions.
+ std::optional<pid_t> exclusiveTid;
+ };
+
+ bool setupSocketClient(const RpcSocketAddress& address);
+ bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId);
+ void addClientConnection(base::unique_fd fd);
+ void setForServer(const wp<RpcServer>& server, int32_t sessionId);
+ sp<RpcConnection> assignServerToThisThread(base::unique_fd fd);
+ bool removeServerConnection(const sp<RpcConnection>& connection);
+
+ enum class ConnectionUse {
+ CLIENT,
+ CLIENT_ASYNC,
+ CLIENT_REFCOUNT,
+ };
+
+ // RAII object for session connection
+ class ExclusiveConnection {
+ public:
+ explicit ExclusiveConnection(const sp<RpcSession>& session, ConnectionUse use);
+ ~ExclusiveConnection();
+ const base::unique_fd& fd() { return mConnection->fd; }
+
+ private:
+ static void findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+ sp<RpcConnection>* available,
+ std::vector<sp<RpcConnection>>& sockets,
+ size_t socketsIndexHint);
+
+ sp<RpcSession> mSession; // avoid deallocation
+ sp<RpcConnection> mConnection;
+
+ // whether this is being used for a nested transaction (being on the same
+ // thread guarantees we won't write in the middle of a message, the way
+ // the wire protocol is constructed guarantees this is safe).
+ bool mReentrant = false;
+ };
+
+ // On the other side of a session, for each of mClientConnections here, there should
+ // be one of mServerConnections on the other side (and vice versa).
+ //
+ // For the simplest session, a single server with one client, you would
+ // have:
+ // - the server has a single 'mServerConnections' and a thread listening on this
+ // - the client has a single 'mClientConnections' and makes calls to this
+ // - here, when the client makes a call, the server can call back into it
+ // (nested calls), but outside of this, the client will only ever read
+ // calls from the server when it makes a call itself.
+ //
+ // For a more complicated case, the client might itself open up a thread to
+ // serve calls to the server at all times (e.g. if it hosts a callback)
+
+ wp<RpcServer> mForServer; // maybe null, for client sessions
+
+ // TODO(b/183988761): this shouldn't be guessable
+ std::optional<int32_t> mId;
+
+ std::unique_ptr<RpcState> mState;
+
+ std::mutex mMutex; // for all below
+
+ std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
+ size_t mWaitingThreads = 0;
+ // hint index into clients, ++ when sending an async transaction
+ size_t mClientConnectionsOffset = 0;
+ std::vector<sp<RpcConnection>> mClientConnections;
+ std::vector<sp<RpcConnection>> mServerConnections;
+
+ // TODO(b/185167543): use for reverse sessions (allow client to also
+ // serve calls on a session).
+ // TODO(b/185167543): allow sharing between different sessions in a
+ // process? (or combine with mServerConnections)
+ std::map<std::thread::id, std::thread> mThreads;
+ bool mTerminated = false;
+};
+
+} // namespace android
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index a90b4aa..2a66941 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -35,7 +35,7 @@
* \return EX_NONE on success.
*/
__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService(
- AIBinder* binder, const char* instance);
+ AIBinder* binder, const char* instance) __INTRODUCED_IN(29);
/**
* Gets a binder object with this specific instance name. Will return nullptr immediately if the
@@ -47,7 +47,8 @@
*
* \param instance identifier of the service used to lookup the service.
*/
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance)
+ __INTRODUCED_IN(29);
/**
* Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
@@ -59,7 +60,8 @@
*
* \param instance identifier of the service used to lookup the service.
*/
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
+ __INTRODUCED_IN(29);
/**
* Registers a lazy service with the default service manager under the 'instance' name.
@@ -135,6 +137,10 @@
/**
* Prevent lazy services without client from shutting down their process
*
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism.
+ *
* \param persist 'true' if the process should not exit.
*/
void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31);
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index c0f7c99..ec231b2 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -63,6 +63,9 @@
"libbinder",
"libutils",
],
+ static_libs: [
+ "libgmock",
+ ],
compile_multilib: "32",
multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -101,6 +104,9 @@
"libbinder",
"libutils",
],
+ static_libs: [
+ "libgmock",
+ ],
test_suites: ["device-tests", "vts"],
require_root: true,
}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 814e094..41daccc 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -18,7 +18,7 @@
oneway void sendString(@utf8InCpp String str);
@utf8InCpp String doubleString(@utf8InCpp String str);
- // number of known RPC binders to process, RpcState::countBinders by connection
+ // number of known RPC binders to process, RpcState::countBinders by session
int[] countBinders();
// Caller sends server, callee pings caller's server and returns error code.
@@ -36,7 +36,7 @@
// should always return the same binder
IBinder alwaysGiveMeTheSameBinder();
- // Idea is that the server will not hold onto the session, the remote connection
+ // Idea is that the server will not hold onto the session, the remote session
// object must. This is to test lifetimes of binder objects, and consequently, also
// identity (since by assigning sessions names, we can make sure a section always
// references the session it was originally opened with).
@@ -55,4 +55,6 @@
oneway void sleepMsAsync(int ms);
void die(boolean cleanup);
+
+ void useKernelBinderCallingId();
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5676bd1..45b2776 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <thread>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <binder/Binder.h>
@@ -41,6 +42,13 @@
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
+using testing::Not;
+
+// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
+MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
+ *result_listener << statusToString(arg);
+ return expected == arg;
+}
static ::testing::AssertionResult IsPageAligned(void *buf) {
if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
@@ -65,6 +73,7 @@
BINDER_LIB_TEST_REGISTER_SERVER,
BINDER_LIB_TEST_ADD_SERVER,
BINDER_LIB_TEST_ADD_POLL_SERVER,
+ BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION,
BINDER_LIB_TEST_CALL_BACK,
BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
BINDER_LIB_TEST_DELAYED_CALL_BACK,
@@ -205,19 +214,16 @@
protected:
sp<IBinder> addServerEtc(int32_t *idPtr, int code)
{
- int ret;
int32_t id;
Parcel data, reply;
sp<IBinder> binder;
- ret = m_server->transact(code, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR));
EXPECT_FALSE(binder != nullptr);
binder = reply.readStrongBinder();
EXPECT_TRUE(binder != nullptr);
- ret = reply.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
if (idPtr)
*idPtr = id;
return binder;
@@ -401,29 +407,25 @@
};
TEST_F(BinderLibTest, NopTransaction) {
- status_t ret;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, NopTransactionOneway) {
- status_t ret;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, NopTransactionClear) {
- status_t ret;
Parcel data, reply;
// make sure it accepts the transaction flag
- ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, Freeze) {
- status_t ret;
Parcel data, reply, replypid;
std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
@@ -442,9 +444,8 @@
return;
}
- ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
- EXPECT_EQ(NO_ERROR, ret);
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));
}
@@ -468,42 +469,36 @@
TEST_F(BinderLibTest, SetError) {
int32_t testValue[] = { 0, -123, 123 };
for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
- status_t ret;
Parcel data, reply;
data.writeInt32(testValue[i]);
- ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply);
- EXPECT_EQ(testValue[i], ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply),
+ StatusEq(testValue[i]));
}
}
TEST_F(BinderLibTest, GetId) {
- status_t ret;
int32_t id;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
- ret = reply.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
+ EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
}
TEST_F(BinderLibTest, PtrSize) {
- status_t ret;
int32_t ptrsize;
Parcel data, reply;
sp<IBinder> server = addServer();
ASSERT_TRUE(server != nullptr);
- ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
- ret = reply.readInt32(&ptrsize);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
+ EXPECT_THAT(reply.readInt32(&ptrsize), StatusEq(NO_ERROR));
RecordProperty("TestPtrSize", sizeof(void *));
RecordProperty("ServerPtrSize", sizeof(void *));
}
TEST_F(BinderLibTest, IndirectGetId2)
{
- status_t ret;
int32_t id;
int32_t count;
Parcel data, reply;
@@ -521,22 +516,19 @@
datai.appendTo(&data);
}
- ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
- ret = reply.readInt32(&id);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
- ret = reply.readInt32(&count);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
BinderLibTestBundle replyi(&reply);
EXPECT_TRUE(replyi.isValid());
- ret = replyi.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(serverId[i], id);
EXPECT_EQ(replyi.dataSize(), replyi.dataPosition());
}
@@ -546,7 +538,6 @@
TEST_F(BinderLibTest, IndirectGetId3)
{
- status_t ret;
int32_t id;
int32_t count;
Parcel data, reply;
@@ -571,15 +562,13 @@
datai.appendTo(&data);
}
- ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
- ret = reply.readInt32(&id);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
- ret = reply.readInt32(&count);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
@@ -587,18 +576,15 @@
BinderLibTestBundle replyi(&reply);
EXPECT_TRUE(replyi.isValid());
- ret = replyi.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(serverId[i], id);
- ret = replyi.readInt32(&counti);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(replyi.readInt32(&counti), StatusEq(NO_ERROR));
EXPECT_EQ(1, counti);
BinderLibTestBundle replyi2(&replyi);
EXPECT_TRUE(replyi2.isValid());
- ret = replyi2.readInt32(&id);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(replyi2.readInt32(&id), StatusEq(NO_ERROR));
EXPECT_EQ(0, id);
EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition());
@@ -610,16 +596,31 @@
TEST_F(BinderLibTest, CallBack)
{
- status_t ret;
Parcel data, reply;
sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
data.writeStrongBinder(callBack);
- ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
+}
+
+TEST_F(BinderLibTest, NoBinderCallContextGuard) {
+ IPCThreadState::SpGuard spGuard{"NoBinderCallContext"};
+ IPCThreadState::SpGuard *origGuard = IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+ // yes, this test uses threads, but it's careful and uses fork in addServer
+ EXPECT_DEATH({ IPCThreadState::self()->getCallingPid(); },
+ "In context NoBinderCallContext, getCallingPid does not make sense.");
+
+ IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+}
+
+TEST_F(BinderLibTest, BinderCallContextGuard) {
+ sp<IBinder> binder = addServer();
+ Parcel data, reply;
+ EXPECT_THAT(binder->transact(BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION, data, &reply),
+ StatusEq(DEAD_OBJECT));
}
TEST_F(BinderLibTest, AddServer)
@@ -630,7 +631,6 @@
TEST_F(BinderLibTest, DeathNotificationStrongRef)
{
- status_t ret;
sp<IBinder> sbinder;
sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
@@ -638,20 +638,17 @@
{
sp<IBinder> binder = addServer();
ASSERT_TRUE(binder != nullptr);
- ret = binder->linkToDeath(testDeathRecipient);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(binder->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
sbinder = binder;
}
{
Parcel data, reply;
- ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(0, ret);
+ EXPECT_THAT(sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY),
+ StatusEq(OK));
}
IPCThreadState::self()->flushCommands();
- ret = testDeathRecipient->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = sbinder->unlinkToDeath(testDeathRecipient);
- EXPECT_EQ(DEAD_OBJECT, ret);
+ EXPECT_THAT(testDeathRecipient->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(sbinder->unlinkToDeath(testDeathRecipient), StatusEq(DEAD_OBJECT));
}
TEST_F(BinderLibTest, DeathNotificationMultiple)
@@ -674,8 +671,9 @@
callBack[i] = new BinderLibTestCallBack();
data.writeStrongBinder(target);
data.writeStrongBinder(callBack[i]);
- ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data,
+ &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
{
Parcel data, reply;
@@ -683,8 +681,9 @@
passiveclient[i] = addServer();
ASSERT_TRUE(passiveclient[i] != nullptr);
data.writeStrongBinder(target);
- ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data,
+ &reply, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
}
{
@@ -694,10 +693,8 @@
}
for (int i = 0; i < clientcount; i++) {
- ret = callBack[i]->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack[i]->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callBack[i]->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack[i]->getResult(), StatusEq(NO_ERROR));
}
}
@@ -712,8 +709,7 @@
sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
- ret = target->linkToDeath(testDeathRecipient);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(target->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
{
Parcel data, reply;
@@ -750,14 +746,13 @@
callback = new BinderLibTestCallBack();
data.writeStrongBinder(target);
data.writeStrongBinder(callback);
- ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply,
+ TF_ONE_WAY),
+ StatusEq(NO_ERROR));
}
- ret = callback->waitEvent(5);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callback->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callback->waitEvent(5), StatusEq(NO_ERROR));
+ EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, PassFile) {
@@ -773,17 +768,14 @@
Parcel data, reply;
uint8_t writebuf[1] = { write_value };
- ret = data.writeFileDescriptor(pipefd[1], true);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(data.writeFileDescriptor(pipefd[1], true), StatusEq(NO_ERROR));
- ret = data.writeInt32(sizeof(writebuf));
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(data.writeInt32(sizeof(writebuf)), StatusEq(NO_ERROR));
- ret = data.write(writebuf, sizeof(writebuf));
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(data.write(writebuf, sizeof(writebuf)), StatusEq(NO_ERROR));
- ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
}
ret = read(pipefd[0], buf, sizeof(buf));
@@ -864,11 +856,10 @@
}
TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
- status_t ret;
Parcel data, reply;
- ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
const flat_binder_object *fb = reply.readObject(false);
ASSERT_TRUE(fb != nullptr);
@@ -888,8 +879,8 @@
wp<IBinder> keepFreedBinder;
{
Parcel data, reply;
- ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
freedHandle = freed->handle;
/* Add a weak ref to the freed binder so the driver does not
@@ -950,7 +941,6 @@
}
TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
- status_t ret;
Parcel data, reply;
sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
for (int i = 0; i < 2; i++) {
@@ -964,13 +954,12 @@
datai.appendTo(&data);
}
- ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, OnewayQueueing)
{
- status_t ret;
Parcel data, data2;
sp<IBinder> pollServer = addPollServer();
@@ -983,25 +972,21 @@
data2.writeStrongBinder(callBack2);
data2.writeInt32(0); // delay in us
- ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
// The delay ensures that this second transaction will end up on the async_todo list
// (for a single-threaded server)
- ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY),
+ StatusEq(NO_ERROR));
// The server will ensure that the two transactions are handled in the expected order;
// If the ordering is not as expected, an error will be returned through the callbacks.
- ret = callBack->waitEvent(2);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callBack->waitEvent(2), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
- ret = callBack2->waitEvent(2);
- EXPECT_EQ(NO_ERROR, ret);
- ret = callBack2->getResult();
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(callBack2->waitEvent(2), StatusEq(NO_ERROR));
+ EXPECT_THAT(callBack2->getResult(), StatusEq(NO_ERROR));
}
TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
@@ -1120,8 +1105,8 @@
ASSERT_TRUE(server != nullptr);
Parcel data, reply;
- status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+ StatusEq(NO_ERROR));
int policy = reply.readInt32();
int priority = reply.readInt32();
@@ -1140,8 +1125,8 @@
EXPECT_EQ(0, sched_setscheduler(getpid(), SCHED_RR, ¶m));
Parcel data, reply;
- status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+ StatusEq(NO_ERROR));
int policy = reply.readInt32();
int priority = reply.readInt32();
@@ -1158,10 +1143,9 @@
std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 };
data.writeUint64Vector(testValue);
- status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply);
- EXPECT_EQ(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR));
std::vector<uint64_t> readValue;
- ret = reply.readUint64Vector(&readValue);
+ EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK));
EXPECT_EQ(readValue, testValue);
}
@@ -1186,19 +1170,18 @@
memcpy(parcelData, &obj, sizeof(obj));
data.setDataSize(sizeof(obj));
- status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply);
// Either the kernel should reject this transaction (if it's correct), but
// if it's not, the server implementation should return an error if it
// finds an object in the received Parcel.
- EXPECT_NE(NO_ERROR, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+ Not(StatusEq(NO_ERROR)));
}
TEST_F(BinderLibTest, GotSid) {
sp<IBinder> server = addServer();
Parcel data;
- status_t ret = server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr);
- EXPECT_EQ(OK, ret);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
}
class BinderLibTestService : public BBinder
@@ -1298,6 +1281,18 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return ret;
}
+ case BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION: {
+ IPCThreadState::SpGuard spGuard{"GuardInBinderTransaction"};
+ IPCThreadState::SpGuard *origGuard =
+ IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+ // if the guard works, this should abort
+ (void)IPCThreadState::self()->getCallingPid();
+
+ IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+ return NO_ERROR;
+ }
+
case BINDER_LIB_TEST_GETPID:
reply->writeInt32(getpid());
return NO_ERROR;
@@ -1525,6 +1520,11 @@
{
binderLibTestServiceName += String16(binderserversuffix);
+ // Testing to make sure that calls that we are serving can use getCallin*
+ // even though we don't here.
+ IPCThreadState::SpGuard spGuard{"main server thread"};
+ (void)IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
BinderLibTestService* testServicePtr;
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index f64bc5b..a457e67 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,8 +18,8 @@
#include <android-base/logging.h>
#include <benchmark/benchmark.h>
#include <binder/Binder.h>
-#include <binder/RpcConnection.h>
#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <thread>
@@ -30,8 +30,8 @@
using android::IBinder;
using android::interface_cast;
using android::OK;
-using android::RpcConnection;
using android::RpcServer;
+using android::RpcSession;
using android::sp;
using android::binder::Status;
@@ -46,17 +46,17 @@
}
};
-static sp<RpcConnection> gConnection = RpcConnection::make();
+static sp<RpcSession> gSession = RpcSession::make();
void BM_getRootObject(benchmark::State& state) {
while (state.KeepRunning()) {
- CHECK(gConnection->getRootObject() != nullptr);
+ CHECK(gSession->getRootObject() != nullptr);
}
}
BENCHMARK(BM_getRootObject);
void BM_pingTransaction(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
while (state.KeepRunning()) {
@@ -66,7 +66,7 @@
BENCHMARK(BM_pingTransaction);
void BM_repeatString(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -95,7 +95,7 @@
BENCHMARK(BM_repeatString);
void BM_repeatBinder(benchmark::State& state) {
- sp<IBinder> binder = gConnection->getRootObject();
+ sp<IBinder> binder = gSession->getRootObject();
CHECK(binder != nullptr);
sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
CHECK(iface != nullptr);
@@ -128,7 +128,7 @@
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
- if (gConnection->setupUnixDomainClient(addr.c_str())) goto success;
+ if (gSession->setupUnixDomainClient(addr.c_str())) goto success;
}
LOG(FATAL) << "Could not connect.";
success:
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 50bff91..3f94df2 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -23,10 +23,11 @@
#include <android/binder_libbinder.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
-#include <binder/RpcConnection.h>
#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -34,14 +35,11 @@
#include <iostream>
#include <thread>
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif //__BIONIC__
-
#include <sys/prctl.h>
#include <unistd.h>
-#include "../RpcState.h" // for debugging
+#include "../RpcState.h" // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
namespace android {
@@ -52,6 +50,19 @@
EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
}
+TEST(BinderRpc, SetExternalServer) {
+ base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+ int sinkFd = sink.get();
+ auto server = RpcServer::make();
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ ASSERT_FALSE(server->hasServer());
+ ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
+ ASSERT_TRUE(server->hasServer());
+ base::unique_fd retrieved = server->releaseServer();
+ ASSERT_FALSE(server->hasServer());
+ ASSERT_EQ(sinkFd, retrieved.get());
+}
+
using android::binder::Status;
#define EXPECT_OK(status) \
@@ -94,12 +105,12 @@
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
out->clear();
- for (auto connection : spServer->listConnections()) {
- size_t count = connection->state()->countBinders();
+ for (auto session : spServer->listSessions()) {
+ size_t count = session->state()->countBinders();
if (count != 1) {
// this is called when there is only one binder held remaining,
// so to aid debugging
- connection->state()->dump();
+ session->state()->dump();
}
out->push_back(count);
}
@@ -181,6 +192,13 @@
_exit(1);
}
}
+ Status useKernelBinderCallingId() override {
+ // this is WRONG! It does not make sense when using RPC binder, and
+ // because it is SO wrong, and so much code calls this, it should abort!
+
+ (void)IPCThreadState::self()->getCallingPid();
+ return Status::ok();
+ }
};
sp<IBinder> MyBinderRpcTest::mHeldBinder;
@@ -225,61 +243,60 @@
return temp + "/binderRpcTest_" + std::to_string(id++);
};
-struct ProcessConnection {
+struct ProcessSession {
// reference to process hosting a socket server
Process host;
- struct ConnectionInfo {
- sp<RpcConnection> connection;
+ struct SessionInfo {
+ sp<RpcSession> session;
sp<IBinder> root;
};
- // client connection objects associated with other process
- // each one represents a separate connection
- std::vector<ConnectionInfo> connections;
+ // client session objects associated with other process
+ // each one represents a separate session
+ std::vector<SessionInfo> sessions;
- ProcessConnection(ProcessConnection&&) = default;
- ~ProcessConnection() {
- for (auto& connection : connections) {
- connection.root = nullptr;
+ ProcessSession(ProcessSession&&) = default;
+ ~ProcessSession() {
+ for (auto& session : sessions) {
+ session.root = nullptr;
}
- for (auto& info : connections) {
- sp<RpcConnection>& connection = info.connection;
+ for (auto& info : sessions) {
+ sp<RpcSession>& session = info.session;
- EXPECT_NE(nullptr, connection);
- EXPECT_NE(nullptr, connection->state());
- EXPECT_EQ(0, connection->state()->countBinders())
- << (connection->state()->dump(), "dump:");
+ EXPECT_NE(nullptr, session);
+ EXPECT_NE(nullptr, session->state());
+ EXPECT_EQ(0, session->state()->countBinders()) << (session->state()->dump(), "dump:");
- wp<RpcConnection> weakConnection = connection;
- connection = nullptr;
- EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+ wp<RpcSession> weakSession = session;
+ session = nullptr;
+ EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
}
}
};
-// Process connection where the process hosts IBinderRpcTest, the server used
+// Process session where the process hosts IBinderRpcTest, the server used
// for most testing here
-struct BinderRpcTestProcessConnection {
- ProcessConnection proc;
+struct BinderRpcTestProcessSession {
+ ProcessSession proc;
- // pre-fetched root object (for first connection)
+ // pre-fetched root object (for first session)
sp<IBinder> rootBinder;
- // pre-casted root object (for first connection)
+ // pre-casted root object (for first session)
sp<IBinderRpcTest> rootIface;
- // whether connection should be invalidated by end of run
+ // whether session should be invalidated by end of run
bool expectInvalid = false;
- BinderRpcTestProcessConnection(BinderRpcTestProcessConnection&&) = default;
- ~BinderRpcTestProcessConnection() {
+ BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
+ ~BinderRpcTestProcessSession() {
if (!expectInvalid) {
std::vector<int32_t> remoteCounts;
- // calling over any connections counts across all connections
+ // calling over any sessions counts across all sessions
EXPECT_OK(rootIface->countBinders(&remoteCounts));
- EXPECT_EQ(remoteCounts.size(), proc.connections.size());
+ EXPECT_EQ(remoteCounts.size(), proc.sessions.size());
for (auto remoteCount : remoteCounts) {
EXPECT_EQ(remoteCount, 1);
}
@@ -292,19 +309,15 @@
enum class SocketType {
UNIX,
-#ifdef __BIONIC__
VSOCK,
-#endif // __BIONIC__
INET,
};
static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
switch (info.param) {
case SocketType::UNIX:
return "unix_domain_socket";
-#ifdef __BIONIC__
case SocketType::VSOCK:
return "vm_socket";
-#endif // __BIONIC__
case SocketType::INET:
return "inet_socket";
default:
@@ -316,10 +329,10 @@
public:
// This creates a new process serving an interface on a certain number of
// threads.
- ProcessConnection createRpcTestSocketServerProcess(
- size_t numThreads, size_t numConnections,
+ ProcessSession createRpcTestSocketServerProcess(
+ size_t numThreads, size_t numSessions,
const std::function<void(const sp<RpcServer>&)>& configure) {
- CHECK_GE(numConnections, 1) << "Must have at least one connection to a server";
+ CHECK_GE(numSessions, 1) << "Must have at least one session to a server";
SocketType socketType = GetParam();
@@ -328,78 +341,72 @@
static unsigned int vsockPort = 3456;
vsockPort++;
- auto ret = ProcessConnection{
+ auto ret = ProcessSession{
.host = Process([&](Pipe* pipe) {
sp<RpcServer> server = RpcServer::make();
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
server->setMaxThreads(numThreads);
+ unsigned int outPort = 0;
+
switch (socketType) {
case SocketType::UNIX:
CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
break;
-#ifdef __BIONIC__
case SocketType::VSOCK:
CHECK(server->setupVsockServer(vsockPort));
break;
-#endif // __BIONIC__
case SocketType::INET: {
- unsigned int outPort = 0;
CHECK(server->setupInetServer(0, &outPort));
CHECK_NE(0, outPort);
- CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort,
- sizeof(outPort)));
break;
}
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
+ CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort, sizeof(outPort)));
+
configure(server);
server->join();
}),
};
- unsigned int inetPort = 0;
+ // always read socket, so that we have waited for the server to start
+ unsigned int outPort = 0;
+ CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &outPort, sizeof(outPort)));
if (socketType == SocketType::INET) {
- CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &inetPort,
- sizeof(inetPort)));
- CHECK_NE(0, inetPort);
+ CHECK_NE(0, outPort);
}
- for (size_t i = 0; i < numConnections; i++) {
- sp<RpcConnection> connection = RpcConnection::make();
- for (size_t tries = 0; tries < 10; tries++) {
- usleep(10000);
- switch (socketType) {
- case SocketType::UNIX:
- if (connection->setupUnixDomainClient(addr.c_str())) goto success;
- break;
-#ifdef __BIONIC__
- case SocketType::VSOCK:
- if (connection->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
- break;
-#endif // __BIONIC__
- case SocketType::INET:
- if (connection->setupInetClient("127.0.0.1", inetPort)) goto success;
- break;
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- }
+ for (size_t i = 0; i < numSessions; i++) {
+ sp<RpcSession> session = RpcSession::make();
+ switch (socketType) {
+ case SocketType::UNIX:
+ if (session->setupUnixDomainClient(addr.c_str())) goto success;
+ break;
+ case SocketType::VSOCK:
+ if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+ break;
+ case SocketType::INET:
+ if (session->setupInetClient("127.0.0.1", outPort)) goto success;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
}
LOG_ALWAYS_FATAL("Could not connect");
success:
- ret.connections.push_back({connection, connection->getRootObject()});
+ ret.sessions.push_back({session, session->getRootObject()});
}
return ret;
}
- BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads,
- size_t numConnections = 1) {
- BinderRpcTestProcessConnection ret{
- .proc = createRpcTestSocketServerProcess(numThreads, numConnections,
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads,
+ size_t numSessions = 1) {
+ BinderRpcTestProcessSession ret{
+ .proc = createRpcTestSocketServerProcess(numThreads, numSessions,
[&](const sp<RpcServer>& server) {
sp<MyBinderRpcTest> service =
new MyBinderRpcTest;
@@ -408,7 +415,7 @@
}),
};
- ret.rootBinder = ret.proc.connections.at(0).root;
+ ret.rootBinder = ret.proc.sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
@@ -421,7 +428,7 @@
server->setRootObject(nullptr);
});
- EXPECT_EQ(nullptr, proc.connections.at(0).root);
+ EXPECT_EQ(nullptr, proc.sessions.at(0).root);
}
TEST_P(BinderRpc, Ping) {
@@ -436,11 +443,11 @@
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
-TEST_P(BinderRpc, MultipleConnections) {
- auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*connections*/);
- for (auto connection : proc.proc.connections) {
- ASSERT_NE(nullptr, connection.root);
- EXPECT_EQ(OK, connection.root->pingBinder());
+TEST_P(BinderRpc, MultipleSessions) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*sessions*/);
+ for (auto session : proc.proc.sessions) {
+ ASSERT_NE(nullptr, session.root);
+ EXPECT_EQ(OK, session.root->pingBinder());
}
}
@@ -582,7 +589,7 @@
// These are behavioral differences form regular binder, where certain usecases
// aren't supported.
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
auto proc1 = createRpcTestSocketServerProcess(1);
auto proc2 = createRpcTestSocketServerProcess(1);
@@ -591,12 +598,12 @@
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
-TEST_P(BinderRpc, CannotMixBindersBetweenTwoConnectionsToTheSameServer) {
- auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*connections*/);
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*sessions*/);
sp<IBinder> outBinder;
EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(proc.proc.connections.at(1).root, &outBinder)
+ proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder)
.transactionError());
}
@@ -888,6 +895,19 @@
}
}
+TEST_P(BinderRpc, UseKernelBinderCallingId) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ // we can't allocate IPCThreadState so actually the first time should
+ // succeed :(
+ EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+
+ // second time! we catch the error :)
+ EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
+
+ proc.expectInvalid = true;
+}
+
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
auto proc = createRpcTestSocketServerProcess(1);
@@ -935,13 +955,42 @@
INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
::testing::ValuesIn({
SocketType::UNIX,
+// TODO(b/185269356): working on host
#ifdef __BIONIC__
SocketType::VSOCK,
-#endif // __BIONIC__
+#endif
SocketType::INET,
}),
PrintSocketType);
+class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+
+TEST_P(BinderRpcServerRootObject, WeakRootObject) {
+ using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
+ auto setRootObject = [](bool isStrong) -> SetFn {
+ return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
+ };
+
+ auto server = RpcServer::make();
+ auto [isStrong1, isStrong2] = GetParam();
+ auto binder1 = sp<BBinder>::make();
+ IBinder* binderRaw1 = binder1.get();
+ setRootObject(isStrong1)(server.get(), binder1);
+ EXPECT_EQ(binderRaw1, server->getRootObject());
+ binder1.clear();
+ EXPECT_EQ((isStrong1 ? binderRaw1 : nullptr), server->getRootObject());
+
+ auto binder2 = sp<BBinder>::make();
+ IBinder* binderRaw2 = binder2.get();
+ setRootObject(isStrong2)(server.get(), binder2);
+ EXPECT_EQ(binderRaw2, server->getRootObject());
+ binder2.clear();
+ EXPECT_EQ((isStrong2 ? binderRaw2 : nullptr), server->getRootObject());
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
+ ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
} // namespace android
int main(int argc, char** argv) {
diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
similarity index 100%
rename from libs/binder/parcel_fuzzer/Android.bp
rename to libs/binder/tests/parcel_fuzzer/Android.bp
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.cpp
rename to libs/binder/tests/parcel_fuzzer/binder.cpp
diff --git a/libs/binder/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.h
rename to libs/binder/tests/parcel_fuzzer/binder.h
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder_ndk.cpp
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
similarity index 97%
rename from libs/binder/parcel_fuzzer/binder_ndk.h
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.h
index e69d9c1..cf24ab9 100644
--- a/libs/binder/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -23,7 +23,7 @@
// libbinder_ndk doesn't export this header which breaks down its API for NDK
// and APEX users, but we need access to it to fuzz.
-#include "../ndk/parcel_internal.h"
+#include "../../ndk/parcel_internal.h"
class NdkParcelAdapter {
public:
diff --git a/libs/binder/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.cpp
rename to libs/binder/tests/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/parcel_fuzzer/hwbinder.h b/libs/binder/tests/parcel_fuzzer/hwbinder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.h
rename to libs/binder/tests/parcel_fuzzer/hwbinder.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
similarity index 95%
rename from libs/binder/parcel_fuzzer/main.cpp
rename to libs/binder/tests/parcel_fuzzer/main.cpp
index 332e2ad..a47b753 100644
--- a/libs/binder/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -23,7 +23,7 @@
#include <iostream>
#include <android-base/logging.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
@@ -33,7 +33,7 @@
#include <sys/time.h>
using android::fillRandomParcel;
-using android::RpcConnection;
+using android::RpcSession;
using android::sp;
void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
@@ -61,9 +61,9 @@
P p;
if constexpr (std::is_same_v<P, android::Parcel>) {
if (provider.ConsumeBool()) {
- auto connection = sp<RpcConnection>::make();
- CHECK(connection->addNullDebuggingClient());
- p.markForRpc(connection);
+ auto session = sp<RpcSession>::make();
+ CHECK(session->addNullDebuggingClient());
+ p.markForRpc(session);
fillRandomParcelData(&p, std::move(provider));
} else {
fillRandomParcel(&p, std::move(provider));
diff --git a/libs/binder/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/parcel_fuzzer.h
rename to libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_fd.cpp
rename to libs/binder/tests/parcel_fuzzer/random_fd.cpp
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_parcel.cpp
rename to libs/binder/tests/parcel_fuzzer/random_parcel.cpp
diff --git a/libs/binder/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.cpp
rename to libs/binder/tests/parcel_fuzzer/util.cpp
diff --git a/libs/binder/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.h
rename to libs/binder/tests/parcel_fuzzer/util.h
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
new file mode 100644
index 0000000..1c75306
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+ name: "binder_rpc_fuzzer",
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
+
+ srcs: [
+ "main.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder",
+ ],
+ },
+ },
+}
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
new file mode 100644
index 0000000..3603ebe
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <sys/resource.h>
+#include <sys/un.h>
+
+namespace android {
+
+static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
+ "/binderRpcFuzzerSocket_" + std::to_string(getpid());
+
+size_t getHardMemoryLimit() {
+ struct rlimit limit;
+ CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
+ return limit.rlim_max;
+}
+
+void setMemoryLimit(size_t cur, size_t max) {
+ const struct rlimit kLimit = {
+ .rlim_cur = cur,
+ .rlim_max = max,
+ };
+ CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+}
+
+class SomeBinder : public BBinder {
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
+ (void)flags;
+
+ if ((code & 1) == 0) {
+ sp<IBinder> binder;
+ (void)data.readStrongBinder(&binder);
+ if (binder != nullptr) {
+ (void)binder->pingBinder();
+ }
+ }
+ if ((code & 2) == 0) {
+ (void)data.readInt32();
+ }
+ if ((code & 4) == 0) {
+ (void)reply->writeStrongBinder(sp<BBinder>::make());
+ }
+
+ return OK;
+ }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size > 50000) return 0;
+
+ unlink(kSock.c_str());
+
+ sp<RpcServer> server = RpcServer::make();
+ server->setRootObject(sp<SomeBinder>::make());
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ CHECK(server->setupUnixDomainServer(kSock.c_str()));
+
+ static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024;
+ size_t hardLimit = getHardMemoryLimit();
+ setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
+
+ std::thread serverThread([=] { (void)server->acceptOne(); });
+
+ sockaddr_un addr{
+ .sun_family = AF_UNIX,
+ };
+ CHECK_LT(kSock.size(), sizeof(addr.sun_path));
+ memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
+
+ base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ CHECK_NE(clientFd.get(), -1);
+ CHECK_EQ(0,
+ TEMP_FAILURE_RETRY(
+ connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
+ << strerror(errno);
+
+ serverThread.join();
+
+ // TODO(b/182938024): fuzz multiple sessions, instead of just one
+
+#if 0
+ // make fuzzer more productive locally by forcing it to create a new session
+ int32_t id = -1;
+ CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
+#endif
+
+ CHECK(base::WriteFully(clientFd, data, size));
+
+ clientFd.reset();
+
+ // TODO(b/185167543): better way to force a server to shutdown
+ while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+ usleep(1);
+ }
+
+ setMemoryLimit(hardLimit, hardLimit);
+
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
similarity index 100%
rename from libs/binder/tests/fuzzers/Android.bp
rename to libs/binder/tests/unit_fuzzers/Android.bp
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/TextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
similarity index 100%
rename from libs/binder/tests/fuzzers/commonFuzzHelpers.h
rename to libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
diff --git a/libs/binder/vm_sockets.h b/libs/binder/vm_sockets.h
new file mode 100644
index 0000000..7d9732b
--- /dev/null
+++ b/libs/binder/vm_sockets.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#pragma once
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#else
+
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY (-1U)
+#define VMADDR_PORT_ANY (-1U)
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_LOCAL 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION (-1U)
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
+struct sockaddr_vm {
+ sa_family_t svm_family;
+ // NOLINTNEXTLINE(google-runtime-int)
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ // NOLINTNEXTLINE(google-runtime-int)
+ unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
+ sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
+
+#endif
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 37fb844..08800f7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -213,7 +213,8 @@
// for this is the scale is calculated based on the requested size and buffer size.
// If there's no buffer, the scale will always be 1.
if (mLastBufferInfo.hasBuffer) {
- setMatrix(&t, mLastBufferInfo);
+ t.setDestinationFrame(mSurfaceControl,
+ Rect(0, 0, newSize.getWidth(), newSize.getHeight()));
}
applyTransaction = true;
}
@@ -416,8 +417,8 @@
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
- setMatrix(t, mLastBufferInfo);
- t->setCrop(mSurfaceControl, crop);
+ t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+ t->setBufferCrop(mSurfaceControl, crop);
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
if (!bufferItem.mIsAutoTimestamp) {
@@ -543,19 +544,6 @@
return mSize != bufferSize;
}
-void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
- const BufferInfo& bufferInfo) {
- uint32_t bufWidth = bufferInfo.crop.getWidth();
- uint32_t bufHeight = bufferInfo.crop.getHeight();
-
- float sx = mSize.width / static_cast<float>(bufWidth);
- float sy = mSize.height / static_cast<float>(bufHeight);
-
- t->setMatrix(mSurfaceControl, sx, 0, 0, sy);
- // Update position based on crop.
- t->setPosition(mSurfaceControl, bufferInfo.crop.left * sx * -1, bufferInfo.crop.top * sy * -1);
-}
-
void BLASTBufferQueue::setTransactionCompleteCallback(
uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
std::lock_guard _lock{mMutex};
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 53721cf..0436758 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -123,11 +123,11 @@
return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
}
- status_t captureDisplay(uint64_t displayOrLayerStack,
+ status_t captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+ SAFE_PARCEL(data.writeUint64, displayId.value);
SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
@@ -281,9 +281,14 @@
NO_ERROR) {
std::vector<uint64_t> rawIds;
if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
- std::vector<PhysicalDisplayId> displayIds(rawIds.size());
- std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
- [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
+ std::vector<PhysicalDisplayId> displayIds;
+ displayIds.reserve(rawIds.size());
+
+ for (const uint64_t rawId : rawIds) {
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+ displayIds.push_back(*id);
+ }
+ }
return displayIds;
}
}
@@ -1296,12 +1301,15 @@
}
case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- uint64_t displayOrLayerStack = 0;
+ uint64_t value;
+ SAFE_PARCEL(data.readUint64, &value);
+ const auto id = DisplayId::fromValue(value);
+ if (!id) return BAD_VALUE;
+
sp<IScreenCaptureListener> captureListener;
- SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
SAFE_PARCEL(data.readStrongBinder, &captureListener);
- return captureDisplay(displayOrLayerStack, captureListener);
+ return captureDisplay(*id, captureListener);
}
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1368,9 +1376,9 @@
}
case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- PhysicalDisplayId displayId(data.readUint64());
- sp<IBinder> display = getPhysicalDisplayToken(displayId);
- reply->writeStrongBinder(display);
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64());
+ if (!id) return BAD_VALUE;
+ reply->writeStrongBinder(getPhysicalDisplayToken(*id));
return NO_ERROR;
}
case GET_DISPLAY_STATE: {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 267db76..e65c721 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -64,6 +64,8 @@
fixedTransformHint(ui::Transform::ROT_INVALID),
frameNumber(0),
autoRefresh(false),
+ bufferCrop(Rect::INVALID_RECT),
+ destinationFrame(Rect::INVALID_RECT),
releaseBufferListener(nullptr) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
@@ -167,6 +169,7 @@
SAFE_PARCEL(output.write, stretchEffect);
SAFE_PARCEL(output.write, bufferCrop);
+ SAFE_PARCEL(output.write, destinationFrame);
return NO_ERROR;
}
@@ -296,6 +299,7 @@
SAFE_PARCEL(input.read, stretchEffect);
SAFE_PARCEL(input.read, bufferCrop);
+ SAFE_PARCEL(input.read, destinationFrame);
return NO_ERROR;
}
@@ -543,6 +547,10 @@
what |= eBufferCropChanged;
bufferCrop = other.bufferCrop;
}
+ if (other.what & eDestinationFrameChanged) {
+ what |= eDestinationFrameChanged;
+ destinationFrame = other.destinationFrame;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 11b8eba..1bb6393 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1675,6 +1675,21 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDestinationFrame(
+ const sp<SurfaceControl>& sc, const Rect& destinationFrame) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eDestinationFrameChanged;
+ s->destinationFrame = destinationFrame;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -2110,12 +2125,12 @@
return s->captureDisplay(captureArgs, captureListener);
}
-status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(displayOrLayerStack, captureListener);
+ return s->captureDisplay(displayId, captureListener);
}
status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index cb04689..c420e4a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -114,6 +114,11 @@
using EventRegistrationFlags = Flags<EventRegistration>;
+ template <typename T>
+ struct SpHash {
+ size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
+ };
+
/*
* Create a connection with SurfaceFlinger.
*/
@@ -237,24 +242,17 @@
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
*/
- virtual status_t captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) = 0;
+ virtual status_t captureDisplay(const DisplayCaptureArgs&,
+ const sp<IScreenCaptureListener>&) = 0;
- virtual status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener) = 0;
-
- template <class AA>
- struct SpHash {
- size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); }
- };
+ virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
* This requires READ_FRAME_BUFFER permission. This function will fail if there
* is a secure window on screen
*/
- virtual status_t captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) = 0;
+ virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
/* Clears the frame statistics for animations.
*
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 3947f22..16430b3 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -104,7 +104,7 @@
eHasListenerCallbacksChanged = 0x20000000,
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
- /* was eFrameChanged, now available 0x1'00000000, */
+ eDestinationFrameChanged = 0x1'00000000,
eCachedBufferChanged = 0x2'00000000,
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
@@ -228,6 +228,7 @@
StretchEffect stretchEffect;
Rect bufferCrop;
+ Rect destinationFrame;
// Listens to when the buffer is safe to be released. This is used for blast
// layers only. The callback includes a release fence as well as the graphic
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 35757be..86f1b63 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -554,6 +554,8 @@
const StretchEffect& stretchEffect);
Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
+ Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
+ const Rect& destinationFrame);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -621,12 +623,9 @@
class ScreenshotClient {
public:
- static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener);
- static status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener);
- static status_t captureLayers(const LayerCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener);
+ static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+ static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
};
// ---------------------------------------------------------------------------
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index ea8c295..7002adf 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -752,21 +752,19 @@
}
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
- status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
- return NO_ERROR;
- }
void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+
+ status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
return NO_ERROR;
}
- virtual status_t captureLayers(
- const LayerCaptureArgs& /* captureArgs */,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+ status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
return NO_ERROR;
}
+ status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
+ return NO_ERROR;
+ }
+
status_t clearAnimationFrameStats() override { return NO_ERROR; }
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index deb4679..e8c05e5 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -253,10 +253,6 @@
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
}
-void PointerCoords::scale(float globalScaleFactor) {
- scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
-}
-
void PointerCoords::applyOffset(float xOffset, float yOffset) {
setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);
setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
@@ -569,6 +565,24 @@
}
}
+void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
+ // Determine how the origin is transformed by the matrix so that we
+ // can transform orientation vectors.
+ vec2 origin = transformPoint(matrix, 0, 0);
+
+ // Apply the transformation to all samples.
+ size_t numSamples = mSamplePointerCoords.size();
+ for (size_t i = 0; i < numSamples; i++) {
+ PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+ float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ transformAngle(matrix, orientation, origin.x, origin.y));
+ vec2 xy = transformPoint(matrix, c.getX(), c.getY());
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+ }
+}
+
#ifdef __linux__
static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
float dsdx, dtdx, tx, dtdy, dsdy, ty;
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 15ab428..32b72ba 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -636,28 +636,54 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
-TEST_F(MotionEventTest, RawCompatTransform) {
- auto createTouchDownEvent = [](int x, int y, ui::Transform transform) {
- std::vector<PointerProperties> pointerProperties;
- pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
- std::vector<PointerCoords> pointerCoords;
- pointerCoords.emplace_back().clear();
- pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
- MotionEvent event;
- event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
- /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
- /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
- /* buttonState */ 0, MotionClassification::NONE, transform,
- /* xPrecision */ 0, /* yPrecision */ 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
- /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
- pointerProperties.data(), pointerCoords.data());
- return event;
- };
+MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) {
+ std::vector<PointerProperties> pointerProperties;
+ pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.emplace_back().clear();
+ pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ MotionEvent event;
+ event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
+ /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ /* buttonState */ 0, MotionClassification::NONE, transform,
+ /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
+ /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
+ pointerProperties.data(), pointerCoords.data());
+ return event;
+}
+TEST_F(MotionEventTest, ApplyTransform) {
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform identity;
+ ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+ xform.set(xform.tx() + 20, xform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, xform);
+ ASSERT_EQ(700, event.getRawX(0));
+ ASSERT_EQ(60, event.getRawY(0));
+ ASSERT_NE(event.getRawX(0), event.getX(0));
+ ASSERT_NE(event.getRawY(0), event.getY(0));
+
+ MotionEvent changedEvent = createTouchDownEvent(60, 100, identity);
+ const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
+ xform[0][1], xform[1][1], xform[2][1],
+ xform[0][2], xform[1][2], xform[2][2]};
+ changedEvent.applyTransform(rowMajor);
+
+ // transformContent effectively rotates the raw coordinates, so those should now include
+ // both rotation AND offset
+ ASSERT_EQ(720, changedEvent.getRawX(0));
+ ASSERT_EQ(100, changedEvent.getRawY(0));
+
+ // The transformed output should be the same then
+ ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001);
+ ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001);
+}
+
+TEST_F(MotionEventTest, RawCompatTransform) {
{
// Make sure raw is raw regardless of transform translation.
ui::Transform xform;
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0edb213..e7bd977 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -306,8 +306,9 @@
// Fortunately, these events are small so sending packets across the
// socket should be atomic across processes.
DisplayEventReceiver::Event event;
- event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
- PhysicalDisplayId(0), systemTime()};
+ event.header =
+ DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ PhysicalDisplayId::fromPort(0), systemTime()};
injectEvent(event);
}
}
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d93a84c..d5e7cb2 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -556,6 +556,7 @@
int32_t* _Nonnull outBytesPerPixel,
int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
+
/**
* Get the system wide unique id for an AHardwareBuffer.
*
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 6ab93df..48d76cc 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -238,63 +238,70 @@
if (previousCount) {
ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
}
- const nsecs_t timeBefore = systemTime();
- // The dimensions should not matter, so long as we draw inside them.
- const Rect displayRect(0, 0, 1080, 2340);
- DisplaySettings display{
- .physicalDisplay = displayRect,
- .clip = displayRect,
- .maxLuminance = 500,
- .outputDataspace = kDestDataSpace,
- };
- const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ // The loop is beneficial for debugging and should otherwise be optimized out by the compiler.
+ // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer
+ // does not impact the shader compilation counts by triggering different behaviors in RE/Skia.
+ for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) {
+ const nsecs_t timeBefore = systemTime();
+ // The dimensions should not matter, so long as we draw inside them.
+ const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight);
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = kDestDataSpace,
+ };
- sp<GraphicBuffer> dstBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usage, "primeShaderCache_dst");
+ const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
- ExternalTexture::Usage::WRITEABLE);
- // This buffer will be the source for the call to drawImageLayers. Draw
- // something to it as a placeholder for what an app draws. We should draw
- // something, but the details are not important. Make use of the shadow layer drawing step
- // to populate it.
- sp<GraphicBuffer> srcBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usage, "drawImageLayer_src");
+ sp<GraphicBuffer> dstBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usage, "primeShaderCache_dst");
- const auto srcTexture =
- std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
- ExternalTexture::Usage::READABLE |
- ExternalTexture::Usage::WRITEABLE);
+ const auto dstTexture =
+ std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
+ ExternalTexture::Usage::WRITEABLE);
+ // This buffer will be the source for the call to drawImageLayers. Draw
+ // something to it as a placeholder for what an app draws. We should draw
+ // something, but the details are not important. Make use of the shadow layer drawing step
+ // to populate it.
+ sp<GraphicBuffer> srcBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usage, "drawImageLayer_src");
- drawSolidLayers(renderengine, display, dstTexture);
- drawShadowLayers(renderengine, display, srcTexture);
- drawBlurLayers(renderengine, display, dstTexture);
- // The majority of shaders are related to sampling images.
- drawImageLayers(renderengine, display, dstTexture, srcTexture);
+ const auto srcTexture =
+ std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE |
+ ExternalTexture::Usage::WRITEABLE);
- // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
- const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
+ drawSolidLayers(renderengine, display, dstTexture);
+ drawShadowLayers(renderengine, display, srcTexture);
+ drawBlurLayers(renderengine, display, dstTexture);
+ // The majority of shaders are related to sampling images.
+ drawImageLayers(renderengine, display, dstTexture, srcTexture);
- sp<GraphicBuffer> externalBuffer =
- new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
- usageExternal, "primeShaderCache_external");
- const auto externalTexture =
- std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
- ExternalTexture::Usage::READABLE);
- // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
- // between 6 and 8 will occur in real uses.
- drawImageLayers(renderengine, display, dstTexture, externalTexture);
+ // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
- // Draw layers for b/185569240.
- drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+ sp<GraphicBuffer> externalBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+ 1, usageExternal, "primeShaderCache_external");
+ const auto externalTexture =
+ std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE);
+ // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
+ // between 6 and 8 will occur in real uses.
+ drawImageLayers(renderengine, display, dstTexture, externalTexture);
- const nsecs_t timeAfter = systemTime();
- const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- const int shadersCompiled = renderengine->reportShadersCompiled();
- ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+ // Draw layers for b/185569240.
+ drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ const int shadersCompiled = renderengine->reportShadersCompiled();
+ ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+ }
}
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 540e36a..6d2af4a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -513,14 +513,9 @@
}
ATRACE_CALL();
- // We need to switch the currently bound context if the buffer is protected but the current
- // context is not. The current state must then be restored after the buffer is cached.
- const bool protectedContextState = mInProtectedContext;
- if (!useProtectedContext(protectedContextState || isProtectedBuffer)) {
- ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
- return;
- }
-
+ // If we were to support caching protected buffers then we will need to switch the currently
+ // bound context if we are not already using the protected context (and subsequently switch
+ // back after the buffer is cached).
auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
@@ -534,8 +529,6 @@
isRenderable);
cache.insert({buffer->getId(), imageTextureRef});
}
- // restore the original state of the protected context if necessary
- useProtectedContext(protectedContextState);
}
void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
@@ -567,10 +560,14 @@
const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
bool requiresLinearEffect) {
const auto stretchEffect = layer->stretchEffect;
+ // The given surface will be stretched by HWUI via matrix transformation
+ // which gets similar results for most surfaces
+ // Determine later on if we need to leverage the stertch shader within
+ // surface flinger
if (stretchEffect.hasEffect()) {
const auto targetBuffer = layer->source.buffer.buffer;
- const auto graphicsBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicsBuffer && shader) {
+ const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+ if (graphicBuffer && shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
}
@@ -660,33 +657,6 @@
int mSaveCount;
};
-void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect,
- SkCanvas* canvas, const SkPaint& paint) {
- float top = bounds.top();
- float left = bounds.left();
- float bottom = bounds.bottom();
- float right = bounds.right();
- // Adjust the drawing bounds based on the stretch itself.
- float stretchOffsetX =
- round(bounds.width() * stretchEffect.getStretchWidthMultiplier());
- float stretchOffsetY =
- round(bounds.height() * stretchEffect.getStretchHeightMultiplier());
- if (stretchEffect.vectorY < 0.f) {
- top -= stretchOffsetY;
- } else if (stretchEffect.vectorY > 0.f){
- bottom += stretchOffsetY;
- }
-
- if (stretchEffect.vectorX < 0.f) {
- left -= stretchOffsetX;
- } else if (stretchEffect.vectorX > 0.f) {
- right += stretchOffsetX;
- }
-
- auto stretchBounds = SkRect::MakeLTRB(left, top, right, bottom);
- canvas->drawRect(stretchBounds, paint);
-}
-
static SkRRect getBlurRRect(const BlurRegion& region) {
const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
@@ -926,12 +896,9 @@
// TODO(b/175915334): consider relaxing this restriction to enable more flexible
// composition - using a well-defined invalid color is long-term less error-prone.
if (layer->shadow.length > 0) {
- const auto rect = layer->geometry.roundedCornersRadius > 0
- ? getSkRect(layer->geometry.roundedCornersCrop)
- : bounds.rect();
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
- drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+ drawShadow(canvas, bounds, layer->shadow);
continue;
}
@@ -1063,17 +1030,7 @@
paint.setAntiAlias(true);
canvas->drawRRect(bounds, paint);
} else {
- auto& stretchEffect = layer->stretchEffect;
- // TODO (njawad) temporarily disable manipulation of geometry
- // the layer bounds will be updated in HWUI instead of RenderEngine
- // in a subsequent CL
- // Keep the method call in a dead code path to make -Werror happy
- // with unused methods
- if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) {
- drawStretch(bounds.rect(), stretchEffect, canvas, paint);
- } else {
- canvas->drawRect(bounds.rect(), paint);
- }
+ canvas->drawRect(bounds.rect(), paint);
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
@@ -1220,17 +1177,14 @@
return mGrContext->maxRenderTargetSize();
}
-void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& settings) {
ATRACE_CALL();
const float casterZ = settings.length / 2.0f;
- const auto shadowShape = cornerRadius > 0
- ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
- : SkPath::Rect(casterRect);
const auto flags =
settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
- SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+ SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
getSkPoint3(settings.lightPos), settings.lightRadius,
getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
flags);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index d9caf35..1f528b7 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -98,7 +98,7 @@
base::unique_fd flush();
bool waitFence(base::unique_fd fenceFd);
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
- void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+ void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& shadowSettings);
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
@@ -120,14 +120,15 @@
// Identifier used or various mappings of layers to various
// textures or shaders
- using LayerId = uint64_t;
+ using GraphicBufferId = uint64_t;
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
- std::unordered_map<LayerId, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
- // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
- std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
GUARDED_BY(mRenderingMutex);
- std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>>
+ // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+ std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ GUARDED_BY(mRenderingMutex);
+ std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
mProtectedTextureCache GUARDED_BY(mRenderingMutex);
std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
index 9b62789..4ac5c40 100644
--- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -69,9 +69,8 @@
// and the other way around.
uniform float uInterpolationStrength;
- float easeInCubic(float t, float d) {
- float tmp = t * d;
- return tmp * tmp * tmp;
+ float easeIn(float t, float d) {
+ return t * d;
}
float computeOverscrollStart(
@@ -84,7 +83,7 @@
) {
float offsetPos = uStretchAffectedDist - inPos;
float posBasedVariation = mix(
- 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
float stretchIntensity = overscroll * posBasedVariation;
return distanceStretched - (offsetPos / (1. + stretchIntensity));
}
@@ -100,7 +99,7 @@
) {
float offsetPos = inPos - reverseStretchDist;
float posBasedVariation = mix(
- 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
float stretchIntensity = (-overscroll) * posBasedVariation;
return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index c87a836..9009ce4 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -101,6 +101,7 @@
}
void RenderEngineThreaded::primeCache() {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
@@ -131,6 +132,7 @@
}
void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ ATRACE_CALL();
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -146,6 +148,7 @@
}
void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ ATRACE_CALL();
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -162,6 +165,7 @@
void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
@@ -175,6 +179,7 @@
}
void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
@@ -247,6 +252,7 @@
const bool useFramebufferCache,
base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
+ ATRACE_CALL();
std::promise<status_t> resultPromise;
std::future<status_t> resultFuture = resultPromise.get_future();
{
@@ -264,6 +270,7 @@
}
void RenderEngineThreaded::cleanFramebufferCache() {
+ ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 240738d..0a49008 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -231,6 +231,10 @@
mFlags |= SENSOR_FLAG_WAKE_UP;
}
break;
+ case SENSOR_TYPE_DEVICE_ORIENTATION:
+ mStringType = SENSOR_STRING_TYPE_DEVICE_ORIENTATION;
+ mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+ break;
case SENSOR_TYPE_DYNAMIC_SENSOR_META:
mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 74d17ce..5a118ec 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -137,7 +137,6 @@
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
- "Size.cpp",
"StaticDisplayInfo.cpp",
],
diff --git a/libs/ui/Size.cpp b/libs/ui/Size.cpp
deleted file mode 100644
index d2996d1..0000000
--- a/libs/ui/Size.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2019 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 <ui/Size.h>
-
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index f196ab9..9120972 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -38,12 +38,22 @@
uint64_t value;
+ // For deserialization.
+ static constexpr std::optional<DisplayId> fromValue(uint64_t);
+
+ // As above, but also upcast to Id.
+ template <typename Id>
+ static constexpr std::optional<Id> fromValue(uint64_t value) {
+ if (const auto id = Id::tryCast(DisplayId(value))) {
+ return id;
+ }
+ return {};
+ }
+
protected:
explicit constexpr DisplayId(uint64_t id) : value(id) {}
};
-static_assert(sizeof(DisplayId) == sizeof(uint64_t));
-
inline bool operator==(DisplayId lhs, DisplayId rhs) {
return lhs.value == rhs.value;
}
@@ -80,11 +90,8 @@
// TODO(b/162612135) Remove default constructor
PhysicalDisplayId() = default;
- // TODO(b/162612135) Remove constructor
- explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
-
constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
private:
@@ -96,10 +103,9 @@
explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
};
-static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
-
struct VirtualDisplayId : DisplayId {
using BaseId = uint32_t;
+
// Flag indicating that this virtual display is backed by the GPU.
static constexpr uint64_t FLAG_GPU = 1ULL << 61;
@@ -163,10 +169,23 @@
explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
};
+constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) {
+ if (const auto id = fromValue<PhysicalDisplayId>(value)) {
+ return id;
+ }
+ if (const auto id = fromValue<VirtualDisplayId>(value)) {
+ return id;
+ }
+ return {};
+}
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
-static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
} // namespace android
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index f1e8252..ecc192d 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -23,103 +23,70 @@
#include <type_traits>
#include <utility>
-namespace android {
-namespace ui {
+namespace android::ui {
-// Forward declare a few things.
-struct Size;
-bool operator==(const Size& lhs, const Size& rhs);
-
-/**
- * A simple value type representing a two-dimensional size
- */
+// A simple value type representing a two-dimensional size.
struct Size {
- int32_t width;
- int32_t height;
+ int32_t width = -1;
+ int32_t height = -1;
- // Special values
- static const Size INVALID;
- static const Size EMPTY;
+ constexpr Size() = default;
- // ------------------------------------------------------------------------
- // Construction
- // ------------------------------------------------------------------------
-
- Size() : Size(INVALID) {}
template <typename T>
- Size(T&& w, T&& h)
- : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
- height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
-
- // ------------------------------------------------------------------------
- // Accessors
- // ------------------------------------------------------------------------
+ constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}
int32_t getWidth() const { return width; }
int32_t getHeight() const { return height; }
- template <typename T>
- void setWidth(T&& v) {
- width = Size::clamp<int32_t, T>(std::forward<T>(v));
- }
- template <typename T>
- void setHeight(T&& v) {
- height = Size::clamp<int32_t, T>(std::forward<T>(v));
- }
-
- // ------------------------------------------------------------------------
- // Assignment
- // ------------------------------------------------------------------------
-
- void set(const Size& size) { *this = size; }
- template <typename T>
- void set(T&& w, T&& h) {
- set(Size(std::forward<T>(w), std::forward<T>(h)));
- }
-
- // Sets the value to INVALID
- void makeInvalid() { set(INVALID); }
-
- // Sets the value to EMPTY
- void clear() { set(EMPTY); }
-
- // ------------------------------------------------------------------------
- // Semantic checks
- // ------------------------------------------------------------------------
-
// Valid means non-negative width and height
bool isValid() const { return width >= 0 && height >= 0; }
// Empty means zero width and height
- bool isEmpty() const { return *this == EMPTY; }
+ bool isEmpty() const;
- // ------------------------------------------------------------------------
- // Clamp Helpers
- // ------------------------------------------------------------------------
-
- // Note: We use only features available in C++11 here for compatibility with
- // external targets which include this file directly or indirectly and which
- // themselves use C++11.
-
- // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
template <typename T>
- using remove_cv_reference_t =
- typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+ void setWidth(T v) {
+ width = clamp<int32_t>(v);
+ }
+
+ template <typename T>
+ void setHeight(T v) {
+ height = clamp<int32_t>(v);
+ }
+
+ void set(Size size) { *this = size; }
+
+ template <typename T>
+ void set(T w, T h) {
+ set(Size(w, h));
+ }
+
+ // Sets the value to kInvalidSize
+ void makeInvalid();
+
+ // Sets the value to kEmptySize
+ void clear();
+
+ // TODO: Replace with std::remove_cvref_t in C++20.
+ template <typename T>
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
// Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
// clamping the input value to the output range if necessary.
template <typename ToType, typename FromType>
- static Size::remove_cv_reference_t<ToType>
- clamp(typename std::enable_if<
- std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
- std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
- FromType>::type v) {
- using BareToType = remove_cv_reference_t<ToType>;
- using BareFromType = remove_cv_reference_t<FromType>;
- static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
- static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
- static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
- static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
+ static constexpr remove_cvref_t<ToType> clamp(FromType v) {
+ using BareToType = remove_cvref_t<ToType>;
+ using ToLimits = std::numeric_limits<BareToType>;
+
+ using BareFromType = remove_cvref_t<FromType>;
+ using FromLimits = std::numeric_limits<BareFromType>;
+
+ static_assert(ToLimits::is_specialized && FromLimits::is_specialized);
+
+ constexpr auto toHighest = ToLimits::max();
+ constexpr auto toLowest = ToLimits::lowest();
+ constexpr auto fromHighest = FromLimits::max();
+ constexpr auto fromLowest = FromLimits::lowest();
// Get the closest representation of [toLowest, toHighest] in type
// FromType to use to clamp the input value before conversion.
@@ -127,37 +94,35 @@
// std::common_type<...> is used to get a value-preserving type for the
// top end of the range.
using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+ using CommonLimits = std::numeric_limits<CommonHighestType>;
// std::make_signed<std::common_type<...>> is used to get a
// value-preserving type for the bottom end of the range, except this is
// a bit trickier for non-integer types like float.
- using CommonLowestType =
- std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
- std::make_signed_t<std::conditional_t<
- std::numeric_limits<CommonHighestType>::is_integer,
- CommonHighestType, int /* not used */>>,
- CommonHighestType>;
+ using CommonLowestType = std::conditional_t<
+ CommonLimits::is_integer,
+ std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType,
+ int /* not used */>>,
+ CommonHighestType>;
// We can then compute the clamp range in a way that can be later
// trivially converted to either the 'from' or 'to' types, and be
- // representabile in either.
- static constexpr auto commonClampHighest =
- std::min(static_cast<CommonHighestType>(fromHighest),
- static_cast<CommonHighestType>(toHighest));
- static constexpr auto commonClampLowest =
- std::max(static_cast<CommonLowestType>(fromLowest),
- static_cast<CommonLowestType>(toLowest));
+ // representable in either.
+ constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest),
+ static_cast<CommonHighestType>(toHighest));
+ constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest),
+ static_cast<CommonLowestType>(toLowest));
- static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
- static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+ constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+ constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
// A clamp is needed only if the range we are clamping to is not the
// same as the range of the input.
- static constexpr bool isClampNeeded =
+ constexpr bool isClampNeeded =
(fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
// If a clamp is not needed, the conversion is just a trivial cast.
- if (!isClampNeeded) {
+ if constexpr (!isClampNeeded) {
return static_cast<BareToType>(v);
}
@@ -170,34 +135,46 @@
// Otherwise clamping is done by using the already computed endpoints
// for each type.
- return (v <= fromClampLowest)
- ? toClampLowest
- : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
+ if (v <= fromClampLowest) {
+ return toClampLowest;
+ }
+
+ return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);
}
};
-// ------------------------------------------------------------------------
-// Comparisons
-// ------------------------------------------------------------------------
+constexpr Size kInvalidSize;
+constexpr Size kEmptySize{0, 0};
-inline bool operator==(const Size& lhs, const Size& rhs) {
+inline void Size::makeInvalid() {
+ set(kInvalidSize);
+}
+
+inline void Size::clear() {
+ set(kEmptySize);
+}
+
+inline bool operator==(Size lhs, Size rhs) {
return lhs.width == rhs.width && lhs.height == rhs.height;
}
-inline bool operator!=(const Size& lhs, const Size& rhs) {
- return !operator==(lhs, rhs);
+inline bool Size::isEmpty() const {
+ return *this == kEmptySize;
}
-inline bool operator<(const Size& lhs, const Size& rhs) {
+inline bool operator!=(Size lhs, Size rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(Size lhs, Size rhs) {
// Orders by increasing width, then height.
if (lhs.width != rhs.width) return lhs.width < rhs.width;
return lhs.height < rhs.height;
}
// Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Size& size, ::std::ostream* os) {
- *os << "Size(" << size.width << ", " << size.height << ")";
+inline void PrintTo(Size size, std::ostream* stream) {
+ *stream << "Size(" << size.width << ", " << size.height << ')';
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 1d908b8..8ddee7e 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -32,6 +32,9 @@
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
}
TEST(DisplayIdTest, createPhysicalIdFromPort) {
@@ -43,6 +46,9 @@
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
}
TEST(DisplayIdTest, createGpuVirtualId) {
@@ -52,6 +58,9 @@
EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
}
TEST(DisplayIdTest, createHalVirtualId) {
@@ -61,6 +70,9 @@
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
}
} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 5f75aea..acef47f 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -93,9 +93,8 @@
}
{
- const auto& s = Size::INVALID;
- EXPECT_FALSE(s.isValid());
- EXPECT_FALSE(s.isEmpty());
+ EXPECT_FALSE(kInvalidSize.isValid());
+ EXPECT_FALSE(kInvalidSize.isEmpty());
}
{
@@ -112,9 +111,8 @@
}
{
- const auto& s = Size::EMPTY;
- EXPECT_TRUE(s.isValid());
- EXPECT_TRUE(s.isEmpty());
+ EXPECT_TRUE(kEmptySize.isValid());
+ EXPECT_TRUE(kEmptySize.isEmpty());
}
{
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index d6fc695..d205231 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,7 +34,10 @@
sp<IBinder> displayToken = nullptr;
sp<SurfaceControl> surfaceControl = nullptr;
if (it == mDisplays.end()) {
- displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+ if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+ displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+ }
+
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
return nullptr;
@@ -157,7 +160,11 @@
HwDisplayConfig activeConfig;
HwDisplayState activeState;
- auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+ sp<IBinder> displayToken;
+ if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+ displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+ }
+
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
} else {
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 6ed9593..0539e4f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -176,10 +176,11 @@
}
return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
", source=0x%08x, displayId=%" PRId32 ", action=%s, "
- "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
+ "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
- flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+ flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState,
+ repeatCount, policyFlags);
}
void KeyEntry::recycle() {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 16cb7d7..8bc877f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -283,27 +283,6 @@
return it != map.end() ? it->second : V{};
}
-/**
- * Find the entry in std::unordered_map by value, and remove it.
- * If more than one entry has the same value, then all matching
- * key-value pairs will be removed.
- *
- * Return true if at least one value has been removed.
- */
-template <typename K, typename V>
-static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
- bool removed = false;
- for (auto it = map.begin(); it != map.end();) {
- if (it->second == value) {
- it = map.erase(it);
- removed = true;
- } else {
- it++;
- }
- }
- return removed;
-}
-
static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
if (first == second) {
return true;
@@ -507,8 +486,8 @@
drainInboundQueueLocked();
}
- while (!mConnectionsByFd.empty()) {
- sp<Connection> connection = mConnectionsByFd.begin()->second;
+ while (!mConnectionsByToken.empty()) {
+ sp<Connection> connection = mConnectionsByToken.begin()->second;
removeInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -2907,6 +2886,12 @@
ATRACE_NAME(message.c_str());
}
+ if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
+ (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
+ // Skip reporting pointer down outside focus to the policy.
+ break;
+ }
+
dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
inputTarget.inputChannel->getConnectionToken());
@@ -3297,86 +3282,78 @@
delete dispatchEntry;
}
-int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
- InputDispatcher* d = static_cast<InputDispatcher*>(data);
+int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
+ std::scoped_lock _l(mLock);
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",
+ connectionToken.get(), events);
+ return 0; // remove the callback
+ }
- { // acquire lock
- std::scoped_lock _l(d->mLock);
-
- if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
- ALOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x",
- fd, events);
- return 0; // remove the callback
+ bool notify;
+ if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
+ if (!(events & ALOOPER_EVENT_INPUT)) {
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ connection->getInputChannelName().c_str(), events);
+ return 1;
}
- bool notify;
- sp<Connection> connection = d->mConnectionsByFd[fd];
- if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
- if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x",
- connection->getInputChannelName().c_str(), events);
+ nsecs_t currentTime = now();
+ bool gotOne = false;
+ status_t status = OK;
+ for (;;) {
+ Result<InputPublisher::ConsumerResponse> result =
+ connection->inputPublisher.receiveConsumerResponse();
+ if (!result.ok()) {
+ status = result.error().code();
+ break;
+ }
+
+ if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+ const InputPublisher::Finished& finish =
+ std::get<InputPublisher::Finished>(*result);
+ finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
+ finish.consumeTime);
+ } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+ // TODO(b/167947340): Report this data to LatencyTracker
+ }
+ gotOne = true;
+ }
+ if (gotOne) {
+ runCommandsLockedInterruptible();
+ if (status == WOULD_BLOCK) {
return 1;
}
-
- nsecs_t currentTime = now();
- bool gotOne = false;
- status_t status = OK;
- for (;;) {
- Result<InputPublisher::ConsumerResponse> result =
- connection->inputPublisher.receiveConsumerResponse();
- if (!result.ok()) {
- status = result.error().code();
- break;
- }
-
- if (std::holds_alternative<InputPublisher::Finished>(*result)) {
- const InputPublisher::Finished& finish =
- std::get<InputPublisher::Finished>(*result);
- d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
- finish.handled, finish.consumeTime);
- } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
- // TODO(b/167947340): Report this data to LatencyTracker
- }
- gotOne = true;
- }
- if (gotOne) {
- d->runCommandsLockedInterruptible();
- if (status == WOULD_BLOCK) {
- return 1;
- }
- }
-
- notify = status != DEAD_OBJECT || !connection->monitor;
- if (notify) {
- ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
- connection->getInputChannelName().c_str(), statusToString(status).c_str(),
- status);
- }
- } else {
- // Monitor channels are never explicitly unregistered.
- // We do it automatically when the remote endpoint is closed so don't warn about them.
- const bool stillHaveWindowHandle =
- d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
- nullptr;
- notify = !connection->monitor && stillHaveWindowHandle;
- if (notify) {
- ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x",
- connection->getInputChannelName().c_str(), events);
- }
}
- // Remove the channel.
- d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
- return 0; // remove the callback
- } // release lock
+ notify = status != DEAD_OBJECT || !connection->monitor;
+ if (notify) {
+ ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
+ connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+ status);
+ }
+ } else {
+ // Monitor channels are never explicitly unregistered.
+ // We do it automatically when the remote endpoint is closed so don't warn about them.
+ const bool stillHaveWindowHandle =
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr;
+ notify = !connection->monitor && stillHaveWindowHandle;
+ if (notify) {
+ ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
+ connection->getInputChannelName().c_str(), events);
+ }
+ }
+
+ // Remove the channel.
+ removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
+ return 0; // remove the callback
}
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
- for (const auto& [fd, connection] : mConnectionsByFd) {
+ for (const auto& [token, connection] : mConnectionsByToken) {
synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
@@ -4342,11 +4319,11 @@
std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
const sp<IBinder>& token) const {
- size_t count = mInputChannelsByToken.count(token);
- if (count == 0) {
+ auto connectionIt = mConnectionsByToken.find(token);
+ if (connectionIt == mConnectionsByToken.end()) {
return nullptr;
}
- return mInputChannelsByToken.at(token);
+ return connectionIt->second->inputChannel;
}
void InputDispatcher::updateWindowHandlesForDisplayLocked(
@@ -4770,6 +4747,40 @@
return true;
}
+// Binder call
+bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
+ sp<IBinder> fromToken;
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+ if (toWindowHandle == nullptr) {
+ ALOGW("Could not find window associated with token=%p", destChannelToken.get());
+ return false;
+ }
+
+ const int32_t displayId = toWindowHandle->getInfo()->displayId;
+
+ auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt == mTouchStatesByDisplay.end()) {
+ ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
+ displayId);
+ return false;
+ }
+
+ TouchState& state = touchStateIt->second;
+ if (state.windows.size() != 1) {
+ ALOGW("Cannot transfer touch state because there are %zu windows being touched",
+ state.windows.size());
+ return false;
+ }
+ const TouchedWindow& touchedWindow = state.windows[0];
+ fromToken = touchedWindow.windowHandle->getToken();
+ } // release lock
+
+ return transferTouchFocus(fromToken, destChannelToken);
+}
+
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
if (DEBUG_FOCUS) {
ALOGD("Resetting and dropping all events (%s).", reason);
@@ -4996,13 +5007,13 @@
dump += INDENT "ReplacedKeys: <empty>\n";
}
- if (!mConnectionsByFd.empty()) {
+ if (!mConnectionsByToken.empty()) {
dump += INDENT "Connections:\n";
- for (const auto& pair : mConnectionsByFd) {
- const sp<Connection>& connection = pair.second;
+ for (const auto& [token, connection] : mConnectionsByToken) {
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
"status=%s, monitor=%s, responsive=%s\n",
- pair.first, connection->getInputChannelName().c_str(),
+ connection->inputChannel->getFd().get(),
+ connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(), connection->getStatusLabel(),
toString(connection->monitor), toString(connection->responsive));
@@ -5050,14 +5061,23 @@
}
}
+class LooperEventCallback : public LooperCallback {
+public:
+ LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
+ int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); }
+
+private:
+ std::function<int(int events)> mCallback;
+};
+
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATION
ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
- std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
- status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+ status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
return base::Error(result) << "Failed to open input channel pair with name " << name;
@@ -5065,13 +5085,20 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
-
+ const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
- mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+ sp<Connection> connection =
+ new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mConnectionsByToken.emplace(token, connection);
+
+ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+ this, std::placeholders::_1, token);
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock
// Wake the looper because some connections have changed.
@@ -5099,18 +5126,21 @@
}
sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
-
+ const sp<IBinder>& token = serverChannel->getConnectionToken();
const int fd = serverChannel->getFd();
- mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+
+ if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mConnectionsByToken.emplace(token, connection);
+ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+ this, std::placeholders::_1, token);
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(),
- displayId, toString(isGestureMonitor), pid);
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
}
// Wake the looper because some connections have changed.
@@ -5143,7 +5173,6 @@
}
removeConnectionLocked(connection);
- mInputChannelsByToken.erase(connectionToken);
if (connection->monitor) {
removeMonitorChannelLocked(connectionToken);
@@ -5301,9 +5330,8 @@
return nullptr;
}
- for (const auto& pair : mConnectionsByFd) {
- const sp<Connection>& connection = pair.second;
- if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
+ for (const auto& [token, connection] : mConnectionsByToken) {
+ if (token == inputConnectionToken) {
return connection;
}
}
@@ -5321,7 +5349,7 @@
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- removeByValue(mConnectionsByFd, connection);
+ mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
}
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7ab4fd7..6edc5f1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,60 +81,60 @@
*/
class InputDispatcher : public android::InputDispatcherInterface {
protected:
- virtual ~InputDispatcher();
+ ~InputDispatcher() override;
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
- virtual bool waitForIdle() override;
- virtual status_t start() override;
- virtual status_t stop() override;
+ void dump(std::string& dump) override;
+ void monitor() override;
+ bool waitForIdle() override;
+ status_t start() override;
+ status_t stop() override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
- virtual void notifyKey(const NotifyKeyArgs* args) override;
- virtual void notifyMotion(const NotifyMotionArgs* args) override;
- virtual void notifySwitch(const NotifySwitchArgs* args) override;
- virtual void notifySensor(const NotifySensorArgs* args) override;
- virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ void notifyKey(const NotifyKeyArgs* args) override;
+ void notifyMotion(const NotifyMotionArgs* args) override;
+ void notifySwitch(const NotifySwitchArgs* args) override;
+ void notifySensor(const NotifySensorArgs* args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
- virtual android::os::InputEventInjectionResult injectInputEvent(
+ android::os::InputEventInjectionResult injectInputEvent(
const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
- virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+ std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
- virtual void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
- handlesPerDisplay) override;
- virtual void setFocusedApplication(
+ void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+ handlesPerDisplay) override;
+ void setFocusedApplication(
int32_t displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
- virtual void setFocusedDisplay(int32_t displayId) override;
- virtual void setInputDispatchMode(bool enabled, bool frozen) override;
- virtual void setInputFilterEnabled(bool enabled) override;
- virtual void setInTouchMode(bool inTouchMode) override;
- virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
- virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
+ void setFocusedDisplay(int32_t displayId) override;
+ void setInputDispatchMode(bool enabled, bool frozen) override;
+ void setInputFilterEnabled(bool enabled) override;
+ void setInTouchMode(bool inTouchMode) override;
+ void setMaximumObscuringOpacityForTouch(float opacity) override;
+ void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop = false) override;
+ bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
+ bool transferTouch(const sp<IBinder>& destChannelToken) override;
- virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
- virtual void setFocusedWindow(const FocusRequest&) override;
- virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
- bool isGestureMonitor,
- const std::string& name,
- int32_t pid) override;
- virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
- virtual status_t pilferPointers(const sp<IBinder>& token) override;
- virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
- virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+ void setFocusedWindow(const FocusRequest&) override;
+ base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name,
+ int32_t pid) override;
+ status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
+ status_t pilferPointers(const sp<IBinder>& token) override;
+ void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+ bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
@@ -211,9 +211,6 @@
bool addPortalWindows = false,
bool ignoreDragWindow = false) REQUIRES(mLock);
- // All registered connections mapped by channel file descriptor.
- std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
-
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
@@ -225,8 +222,10 @@
struct StrongPointerHash {
std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
};
- std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, StrongPointerHash<IBinder>>
- mInputChannelsByToken GUARDED_BY(mLock);
+
+ // All registered connections mapped by input channel token.
+ std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
+ GUARDED_BY(mLock);
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
@@ -544,7 +543,7 @@
bool notify) REQUIRES(mLock);
void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
- static int handleReceiveCallback(int fd, int events, void* data);
+ int handleReceiveCallback(int events, sp<IBinder> connectionToken);
// The action sent should only be of type AMOTION_EVENT_*
void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index b601dfc..7f85e53 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -151,6 +151,14 @@
*/
virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) = 0;
+
+ /**
+ * Transfer touch focus to the provided channel, no matter where the current touch is.
+ *
+ * Return true on success, false if there was no on-going touch.
+ */
+ virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0;
+
/**
* Sets focus on the specified window.
*/
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 2d0fdf7..e91f84e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -413,8 +413,7 @@
}
}
-void InputReader::dispatchExternalStylusState(const StylusState& state) {
- std::scoped_lock _l(mLock);
+void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
device->updateExternalStylusState(state);
@@ -945,7 +944,7 @@
}
void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
- mReader->dispatchExternalStylusState(state);
+ mReader->dispatchExternalStylusStateLocked(state);
}
InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1405671..bc79ccf 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -203,7 +203,7 @@
void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
- void dispatchExternalStylusState(const StylusState& state);
+ void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
// The PointerController that is shared among all the input devices that need it.
std::weak_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 13712ee..fb65484 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2456,6 +2456,12 @@
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
+ uint32_t flags = 0;
+
+ if (!PointerGesture::canGestureAffectWindowFocus(mPointerGesture.currentGestureMode)) {
+ flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+
// Update last coordinates of pointers that have moved so that we observe the new
// pointer positions at the same time as other pointers that have just gone up.
bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
@@ -2485,8 +2491,8 @@
BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
mPointerGesture.downTime);
@@ -2504,7 +2510,7 @@
uint32_t id = upGestureIdBits.clearFirstMarkedBit();
dispatchMotion(when, readTime, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2517,7 +2523,7 @@
// Send motion events for all pointers that moved.
if (moveNeeded) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
@@ -2538,7 +2544,7 @@
}
dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
- 0, 0, metaState, buttonState, 0,
+ 0, flags, metaState, buttonState, 0,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2548,8 +2554,8 @@
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
@@ -2573,7 +2579,7 @@
const int32_t displayId = mPointerController->getDisplayId();
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 5146299..920f842 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -590,6 +590,27 @@
QUIET,
};
+ // When a gesture is sent to an unfocused window, return true if it can bring that window
+ // into focus, false otherwise.
+ static bool canGestureAffectWindowFocus(Mode mode) {
+ switch (mode) {
+ case Mode::TAP:
+ case Mode::TAP_DRAG:
+ case Mode::BUTTON_CLICK_OR_DRAG:
+ // Taps can affect window focus.
+ return true;
+ case Mode::FREEFORM:
+ case Mode::HOVER:
+ case Mode::NEUTRAL:
+ case Mode::PRESS:
+ case Mode::QUIET:
+ case Mode::SWIPE:
+ // Most gestures can be performed on an unfocused window, so they should not
+ // not affect window focus.
+ return false;
+ }
+ }
+
// Time the first finger went down.
nsecs_t firstTouchTime;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 9687c83..855453e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1225,6 +1225,11 @@
return *this;
}
+ MotionEventBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
MotionEvent build() {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
@@ -1244,7 +1249,7 @@
MotionEvent event;
ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
- mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
mButtonState, MotionClassification::NONE, identityTransform,
/* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime,
@@ -1260,6 +1265,7 @@
int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
int32_t mActionButton{0};
int32_t mButtonState{0};
+ int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
@@ -1708,7 +1714,13 @@
0 /*expectedFlags*/);
}
-TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+using TransferFunction =
+ std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+
+class TransferTouchFixture : public InputDispatcherTest,
+ public ::testing::WithParamInterface<TransferFunction> {};
+
+TEST_P(TransferTouchFixture, TransferTouch_OnePointer) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a couple of windows
@@ -1729,8 +1741,10 @@
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- // Transfer touch focus to the second window
- mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ // Transfer touch to the second window
+ TransferFunction f = GetParam();
+ const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
@@ -1745,7 +1759,7 @@
secondWindow->consumeMotionUp();
}
-TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
@@ -1780,7 +1794,9 @@
secondWindow->assertNoEvents();
// Transfer touch focus to the second window
- mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ TransferFunction f = GetParam();
+ bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
// The first window gets cancel and the second gets down and pointer down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
@@ -1807,6 +1823,21 @@
secondWindow->consumeMotionUp();
}
+// For the cases of single pointer touch and two pointers non-split touch, the api's
+// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
+// for the case where there are multiple pointers split across several windows.
+INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
+ ::testing::Values(
+ [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
+ sp<IBinder> destChannelToken) {
+ return dispatcher->transferTouch(destChannelToken);
+ },
+ [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
+ sp<IBinder> to) {
+ return dispatcher->transferTouchFocus(from, to,
+ false /*isDragAndDrop*/);
+ }));
+
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -1877,6 +1908,82 @@
secondWindow->consumeMotionUp();
}
+// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api.
+// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving
+// touch is not supported, so the touch should continue on those windows and the transferred-to
+// window should get nothing.
+TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->setFrame(Rect(0, 0, 600, 400));
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+ secondWindow->setFrame(Rect(0, 400, 600, 800));
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+ PointF pointInFirst = {300, 200};
+ PointF pointInSecond = {300, 600};
+
+ // Send down to the first window
+ NotifyMotionArgs firstDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInFirst});
+ mDispatcher->notifyMotion(&firstDownMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Send down to the second window
+ NotifyMotionArgs secondDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&secondDownMotionArgs);
+ // The first window gets a move and the second a down
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionDown();
+
+ // Transfer touch focus to the second window
+ const bool transferred = mDispatcher->transferTouch(secondWindow->getToken());
+ // The 'transferTouch' call should not succeed, because there are 2 touched windows
+ ASSERT_FALSE(transferred);
+ firstWindow->assertNoEvents();
+ secondWindow->assertNoEvents();
+
+ // The rest of the dispatch should proceed as normal
+ // Send pointer up to the second window
+ NotifyMotionArgs pointerUpMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&pointerUpMotionArgs);
+ // The first window gets MOVE and the second gets pointer up
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionUp();
+
+ // Send up event to the first window
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets nothing and the second gets up
+ firstWindow->consumeMotionUp();
+ secondWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -3105,6 +3212,25 @@
mFakePolicy->assertOnPointerDownWasNotCalled();
}
+// Have two windows, one with focus. Injecting a trusted DOWN MotionEvent with the flag
+// NO_FOCUS_CHANGE on the unfocused window should not call the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, NoFocusChangeFlag) {
+ const MotionEvent event =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(20).y(20))
+ .addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
+ // Ensure that the unfocused window did not receive any FOCUS events.
+ mUnfocusedWindow->assertNoEvents();
+}
+
// These tests ensures we can send touch events to a single client when there are multiple input
// windows that point to the same client token.
class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
index 5fd361c..c564b7b 100644
--- a/services/stats/android.frameworks.stats@1.0-service.xml
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -1,5 +1,5 @@
<manifest version="1.0" type="framework">
- <hal>
+ <hal max-level="5">
<name>android.frameworks.stats</name>
<transport>hwbinder</transport>
<version>1.0</version>
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 24b3599..fcf8299 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -295,12 +295,72 @@
return true;
}
+bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) {
+ if (mCurrentState.destinationFrame == destinationFrame) return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.destinationFrame = destinationFrame;
+
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+void BufferStateLayer::updateGeometry() {
+ if (mCurrentState.destinationFrame.isEmpty()) {
+ // If destination frame is not set, use the requested transform set via
+ // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
+ mCurrentState.transform = mRequestedTransform;
+ return;
+ }
+
+ Rect destRect = mCurrentState.destinationFrame;
+ int32_t destW = destRect.width();
+ int32_t destH = destRect.height();
+ if (destRect.left < 0) {
+ destRect.left = 0;
+ destRect.right = destW;
+ }
+ if (destRect.top < 0) {
+ destRect.top = 0;
+ destRect.bottom = destH;
+ }
+
+ if (!mCurrentState.buffer) {
+ ui::Transform t;
+ t.set(destRect.left, destRect.top);
+ mCurrentState.transform = t;
+ return;
+ }
+
+ uint32_t bufferWidth = mCurrentState.buffer->getBuffer()->getWidth();
+ uint32_t bufferHeight = mCurrentState.buffer->getBuffer()->getHeight();
+ // Undo any transformations on the buffer.
+ if (mCurrentState.bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (mCurrentState.transformToDisplayInverse) {
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ float sx = destW / static_cast<float>(bufferWidth);
+ float sy = destH / static_cast<float>(bufferHeight);
+ ui::Transform t;
+ t.set(sx, 0, 0, sy);
+ t.set(destRect.left, destRect.top);
+ mCurrentState.transform = t;
+ return;
+}
+
bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
- if (mCurrentState.transform.dsdx() == matrix.dsdx &&
- mCurrentState.transform.dtdy() == matrix.dtdy &&
- mCurrentState.transform.dtdx() == matrix.dtdx &&
- mCurrentState.transform.dsdy() == matrix.dsdy) {
+ if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+ mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
return false;
}
@@ -313,7 +373,7 @@
return false;
}
- mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+ mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -323,11 +383,11 @@
}
bool BufferStateLayer::setPosition(float x, float y) {
- if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+ if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
return false;
}
- mCurrentState.transform.set(x, y);
+ mRequestedTransform.set(x, y);
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -523,35 +583,35 @@
Rect BufferStateLayer::getBufferSize(const State& s) const {
// for buffer state layers we use the display frame size as the buffer size.
- if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
- return Rect(getActiveWidth(s), getActiveHeight(s));
- }
if (mBufferInfo.mBuffer == nullptr) {
return Rect::INVALID_RECT;
}
- // if the display frame is not defined, use the parent bounds as the buffer size.
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- Rect parentBounds = Rect(p->getBounds(Region()));
- if (!parentBounds.isEmpty()) {
- return parentBounds;
+ uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
}
}
- return Rect::INVALID_RECT;
+ return Rect(0, 0, bufWidth, bufHeight);
}
FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
- const State& s(getDrawingState());
- // for buffer state layers we use the display frame size as the buffer size.
- if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
- return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ if (mBufferInfo.mBuffer == nullptr) {
+ return parentBounds;
}
- // if the display frame is not defined, use the parent bounds as the buffer size.
- return parentBounds;
+ return getBufferSize(getDrawingState()).toFloatRect();
}
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index a273230..2e48452 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -86,6 +86,8 @@
void setAutoRefresh(bool autoRefresh) override;
bool setBufferCrop(const Rect& bufferCrop) override;
+ bool setDestinationFrame(const Rect& destinationFrame) override;
+ void updateGeometry() override;
// -----------------------------------------------------------------------
@@ -178,6 +180,10 @@
// - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
std::atomic<int32_t> mPendingBufferTransactions{0};
+ // Contains requested position and matrix updates. This will be applied if the client does
+ // not specify a destination frame.
+ ui::Transform mRequestedTransform;
+
// TODO(marissaw): support sticky transform for LEGACY camera mode
class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 633668e..317d333 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -47,7 +47,7 @@
std::optional<Physical> physical;
// Size of the display in pixels
- ui::Size pixels = ui::Size::INVALID;
+ ui::Size pixels = ui::kInvalidSize;
// Pixel format of the display
ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 4976213..9fba7aa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,6 +163,9 @@
// Enables (or disables) composition on this output
virtual void setCompositionEnabled(bool) = 0;
+ // Enables (or disables) layer caching on this output
+ virtual void setLayerCachingEnabled(bool) = 0;
+
// Sets the projection state to use
virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
index a8d372c..4110346 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -24,21 +24,17 @@
struct ANativeWindow;
-namespace android {
-
-namespace compositionengine {
-
-class Display;
+namespace android::compositionengine {
/**
* A parameter object for creating RenderSurface instances
*/
struct RenderSurfaceCreationArgs {
// The initial width of the surface
- int32_t displayWidth;
+ int32_t displayWidth = -1;
// The initial height of the surface
- int32_t displayHeight;
+ int32_t displayHeight = -1;
// The ANativeWindow for the buffer queue for this surface
sp<ANativeWindow> nativeWindow;
@@ -46,22 +42,16 @@
// The DisplaySurface for this surface
sp<DisplaySurface> displaySurface;
- size_t maxTextureCacheSize;
+ // The maximum size of the renderengine::ExternalTexture cache
+ size_t maxTextureCacheSize = 0;
+
+private:
+ friend class RenderSurfaceCreationArgsBuilder;
+
+ // Not defaulted to disable aggregate initialization.
+ RenderSurfaceCreationArgs() {}
};
-/**
- * A helper for setting up a RenderSurfaceCreationArgs value in-line.
- * Prefer this builder over raw structure initialization.
- *
- * Instead of:
- *
- * RenderSurfaceCreationArgs{1000, 1000, nativeWindow, displaySurface}
- *
- * Prefer:
- *
- * RenderSurfaceCreationArgsBuilder().setDisplayWidth(1000).setDisplayHeight(1000)
- * .setNativeWindow(nativeWindow).setDisplaySurface(displaySurface).Build();
- */
class RenderSurfaceCreationArgsBuilder {
public:
RenderSurfaceCreationArgs build() { return std::move(mArgs); }
@@ -75,11 +65,11 @@
return *this;
}
RenderSurfaceCreationArgsBuilder& setNativeWindow(sp<ANativeWindow> nativeWindow) {
- mArgs.nativeWindow = nativeWindow;
+ mArgs.nativeWindow = std::move(nativeWindow);
return *this;
}
RenderSurfaceCreationArgsBuilder& setDisplaySurface(sp<DisplaySurface> displaySurface) {
- mArgs.displaySurface = displaySurface;
+ mArgs.displaySurface = std::move(displaySurface);
return *this;
}
@@ -92,5 +82,4 @@
RenderSurfaceCreationArgs mArgs;
};
-} // namespace compositionengine
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index eeb20fc..2893c3f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -20,6 +20,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/impl/ClientCompositionRequestCache.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
#include <memory>
@@ -28,21 +29,18 @@
namespace android::compositionengine::impl {
-namespace planner {
-class Planner;
-} // namespace planner
-
// The implementation class contains the common implementation, but does not
// actually contain the final output state.
class Output : public virtual compositionengine::Output {
public:
- Output();
+ Output() = default;
~Output() override;
// compositionengine::Output overrides
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
+ void setLayerCachingEnabled(bool) override;
void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) override;
void setDisplaySize(const ui::Size&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 2488c66..2ffd472 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -78,9 +78,10 @@
void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
- bool isPeekingThrough);
+ bool isPeekingThrough, bool skipLayer);
void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
Hwc2::IComposerClient::Composition to) const;
+ bool isClientCompositionForced(bool isPeekingThrough) const;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index b98043b..3f670a1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -125,6 +125,9 @@
// Set to true when overridden info has been sent to HW composer
bool stateOverridden = false;
+
+ // True when this layer was skipped as part of SF-side layer caching.
+ bool layerSkipped = false;
};
// The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index b09f1d1..864251f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -36,8 +36,7 @@
class Flattener {
public:
- Flattener(Predictor& predictor, bool enableHolePunch = false)
- : mEnableHolePunch(enableHolePunch), mPredictor(predictor) {}
+ Flattener(bool enableHolePunch = false) : mEnableHolePunch(enableHolePunch) {}
void setDisplaySize(ui::Size size) { mDisplaySize = size; }
@@ -64,7 +63,6 @@
void buildCachedSets(std::chrono::steady_clock::time_point now);
const bool mEnableHolePunch;
- Predictor& mPredictor;
ui::Size mDisplaySize;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index c2037a8..4365b93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -76,6 +76,8 @@
std::optional<Predictor::PredictedPlan> mPredictedPlan;
NonBufferHash mFlattenedHash = 0;
+
+ bool mPredictorEnabled = false;
};
} // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 5aa53e5..749675d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,6 +36,7 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
+ MOCK_METHOD1(setLayerCachingEnabled, void(bool));
MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 297e687..796fe7d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -55,18 +55,6 @@
namespace impl {
-Output::Output() {
- const bool enableLayerCaching = [] {
- const bool enable =
- android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
- return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
- }();
-
- if (enableLayerCaching) {
- mPlanner = std::make_unique<planner::Planner>();
- }
-}
-
namespace {
template <typename T>
@@ -135,6 +123,21 @@
dirtyEntireOutput();
}
+void Output::setLayerCachingEnabled(bool enabled) {
+ if (enabled == (mPlanner != nullptr)) {
+ return;
+ }
+
+ if (enabled) {
+ mPlanner = std::make_unique<planner::Planner>();
+ if (mRenderSurface) {
+ mPlanner->setDisplaySize(mRenderSurface->getSize());
+ }
+ } else {
+ mPlanner.reset();
+ }
+}
+
void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) {
auto& outputState = editState();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 01da070..cd14327 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <DisplayHardware/Hal.h>
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/LayerFECompositionState.h>
@@ -350,12 +351,14 @@
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
- writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough);
+ writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
+ skipLayer);
// Always set the layer color after setting the composition type.
writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
editState().hwc->stateOverridden = isOverridden;
+ editState().hwc->layerSkipped = skipLayer;
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
@@ -482,7 +485,8 @@
const Region& surfaceDamage = getState().overrideInfo.buffer
? getState().overrideInfo.damageRegion
- : outputIndependentState.surfaceDamage;
+ : (getState().hwc->stateOverridden ? Region::INVALID_REGION
+ : outputIndependentState.surfaceDamage);
if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
@@ -573,17 +577,19 @@
void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
hal::Composition requestedCompositionType,
- bool isPeekingThrough) {
+ bool isPeekingThrough, bool skipLayer) {
auto& outputDependentState = editState();
- // If we are forcing client composition, we need to tell the HWC
- if (outputDependentState.forceClientComposition ||
- (!isPeekingThrough && getLayerFE().hasRoundedCorners())) {
+ if (isClientCompositionForced(isPeekingThrough)) {
+ // If we are forcing client composition, we need to tell the HWC
requestedCompositionType = hal::Composition::CLIENT;
}
// Set the requested composition type with the HWC whenever it changes
- if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+ // We also resend the composition type when this layer was previously skipped, to ensure that
+ // the composition type is up-to-date.
+ if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType ||
+ (outputDependentState.hwc->layerSkipped && !skipLayer)) {
outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
@@ -663,12 +669,22 @@
}
}
+bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
+ return getState().forceClientComposition ||
+ (!isPeekingThrough && getLayerFE().hasRoundedCorners());
+}
+
void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
auto& state = editState();
LOG_FATAL_IF(!state.hwc);
auto& hwcState = *state.hwc;
- detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+ // Only detected disallowed changes if this was not a skip layer, because the
+ // validated composition type may be arbitrary (usually DEVICE, to reflect that there were
+ // fewer GPU layers)
+ if (!hwcState.layerSkipped) {
+ detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+ }
hwcState.hwcCompositionType = compositionType;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index a63f21f..4453a99 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -20,7 +20,6 @@
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
using time_point = std::chrono::steady_clock::time_point;
using namespace std::chrono_literals;
@@ -126,9 +125,11 @@
return left.first < right.first;
};
- const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
- mInitialLayerCounts.cend(), compareLayerCounts)
- ->first;
+ const size_t maxLayerCount = mInitialLayerCounts.empty()
+ ? 0u
+ : std::max_element(mInitialLayerCounts.cbegin(), mInitialLayerCounts.cend(),
+ compareLayerCounts)
+ ->first;
result.append("\n Initial counts:\n");
for (size_t count = 1; count < maxLayerCount; ++count) {
@@ -408,7 +409,7 @@
}
// TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
- mPredictor.getPredictedPlan({}, 0);
+ // and feedback into the predictor
++mCachedSetCreationCount;
mCachedSetCreationCost += mNewCachedSet->getCreationCost();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 7e85dca..c26eb1e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -27,8 +27,13 @@
namespace android::compositionengine::impl::planner {
Planner::Planner()
- : mFlattener(mPredictor,
- base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {}
+ : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {
+ // Implicitly, layer caching must also be enabled.
+ // E.g., setprop debug.sf.enable_layer_caching 1, or
+ // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
+ mPredictorEnabled =
+ base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
+}
void Planner::setDisplaySize(ui::Size size) {
mFlattener.setDisplaySize(size);
@@ -99,19 +104,25 @@
const bool layersWereFlattened = hash != mFlattenedHash;
ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
- mPredictedPlan =
- mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
- : mCurrentLayers,
- mFlattenedHash);
- if (mPredictedPlan) {
- ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
- } else {
- ALOGV("[%s] No prediction found\n", __func__);
+ if (mPredictorEnabled) {
+ mPredictedPlan =
+ mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+ : mCurrentLayers,
+ mFlattenedHash);
+ if (mPredictedPlan) {
+ ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+ } else {
+ ALOGV("[%s] No prediction found\n", __func__);
+ }
}
}
void Planner::reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ if (!mPredictorEnabled) {
+ return;
+ }
+
Plan finalPlan;
const GraphicBuffer* currentOverrideBuffer = nullptr;
bool hasSkippedLayers = false;
@@ -185,7 +196,9 @@
return;
}
- mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ if (mPredictorEnabled) {
+ mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ }
} else if (command == "--describe" || command == "-d") {
if (args.size() < 3) {
base::StringAppendF(&result,
@@ -209,7 +222,9 @@
return;
}
- mPredictor.describeLayerStack(hash, result);
+ if (mPredictorEnabled) {
+ mPredictor.describeLayerStack(hash, result);
+ }
} else if (command == "--help" || command == "-h") {
dumpUsage(result);
} else if (command == "--similar" || command == "-s") {
@@ -232,7 +247,9 @@
return;
}
- mPredictor.listSimilarStacks(*plan, result);
+ if (mPredictorEnabled) {
+ mPredictor.listSimilarStacks(*plan, result);
+ }
} else if (command == "--layers" || command == "-l") {
mFlattener.dumpLayers(result);
} else {
@@ -247,7 +264,9 @@
mFlattener.dump(result);
result.append("\n");
- mPredictor.dump(result);
+ if (mPredictorEnabled) {
+ mPredictor.dump(result);
+ }
}
void Planner::dumpUsage(std::string& result) const {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8a83639..492db43 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -60,9 +60,10 @@
using testing::SetArgPointee;
using testing::StrictMock;
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId{42};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
// TODO(b/160679868) Use VirtualDisplayId
-constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId{43};
+constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId::fromPort(43u);
+
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
constexpr int32_t DEFAULT_LAYER_STACK = 123;
@@ -521,7 +522,11 @@
TEST_F(DisplayCreateRenderSurfaceTest, setsRenderSurface) {
EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
- mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
+ mDisplay->createRenderSurface(RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(640)
+ .setDisplayHeight(480)
+ .setNativeWindow(mNativeWindow)
+ .build());
EXPECT_TRUE(mDisplay->getRenderSurface() != nullptr);
}
@@ -1029,9 +1034,12 @@
);
impl::RenderSurface* mRenderSurface =
new impl::RenderSurface{mCompositionEngine, *mDisplay,
- RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
- mDisplaySurface}};
+ RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(DEFAULT_DISPLAY_WIDTH)
+ .setDisplayHeight(DEFAULT_DISPLAY_HEIGHT)
+ .setNativeWindow(mNativeWindow)
+ .setDisplaySurface(mDisplaySurface)
+ .build()};
};
TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
@@ -1049,4 +1057,4 @@
} // namespace android::compositionengine
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 3adfe40..5bd1216 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1065,12 +1065,62 @@
kOverrideSurfaceDamage);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->stateOverridden = true;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+ Region::INVALID_REGION);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->stateOverridden = true;
+ mOutputLayer.editState().hwc->layerSkipped = true;
+ mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+ Region::INVALID_REGION);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().forceClientComposition = true;
+ mOutputLayer.editState().hwc->stateOverridden = true;
+ mOutputLayer.editState().hwc->layerSkipped = true;
+ mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+ Region::INVALID_REGION);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 27980a0..11736d1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -238,6 +238,28 @@
}
/*
+ * Output::setLayerCachingEnabled()
+ */
+
+TEST_F(OutputTest, setLayerCachingEnabled_enablesCaching) {
+ const auto kSize = ui::Size(1, 1);
+ EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+ mOutput->setLayerCachingEnabled(false);
+ mOutput->setLayerCachingEnabled(true);
+
+ EXPECT_TRUE(mOutput->plannerEnabled());
+}
+
+TEST_F(OutputTest, setLayerCachingEnabled_disablesCaching) {
+ const auto kSize = ui::Size(1, 1);
+ EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+ mOutput->setLayerCachingEnabled(true);
+ mOutput->setLayerCachingEnabled(false);
+
+ EXPECT_FALSE(mOutput->plannerEnabled());
+}
+
+/*
* Output::setProjection()
*/
@@ -972,9 +994,7 @@
mOutput.editState().usesDeviceComposition = true;
EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
- if (mOutput.plannerEnabled()) {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- }
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
mOutput.prepareFrame();
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 9aeb290..431cc93 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#include "renderengine/ExternalTexture.h"
-#include "ui/GraphicBuffer.h"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
#include <cstdarg>
#include <cstdint>
@@ -32,14 +26,16 @@
#include <compositionengine/mock/NativeWindow.h>
#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
namespace android::compositionengine {
namespace {
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123u);
const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
using testing::_;
@@ -67,9 +63,12 @@
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
- RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
- mDisplaySurface}};
+ RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(DEFAULT_DISPLAY_WIDTH)
+ .setDisplayHeight(DEFAULT_DISPLAY_HEIGHT)
+ .setNativeWindow(mNativeWindow)
+ .setDisplaySurface(mDisplaySurface)
+ .build()};
};
/*
@@ -367,11 +366,8 @@
mSurface.flip();
- EXPECT_EQ(501, mSurface.getPageFlipCount());
+ EXPECT_EQ(501u, mSurface.getPageFlipCount());
}
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 71757f6..25fab49 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -18,7 +18,6 @@
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
@@ -31,7 +30,6 @@
using impl::planner::Flattener;
using impl::planner::LayerState;
using impl::planner::NonBufferHash;
-using impl::planner::Predictor;
using testing::_;
using testing::ByMove;
@@ -47,7 +45,7 @@
class FlattenerTest : public testing::Test {
public:
- FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor, true)) {}
+ FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {}
void SetUp() override;
protected:
@@ -55,10 +53,6 @@
void initializeFlattener(const std::vector<const LayerState*>& layers);
void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
- // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
- // mPredictor should be mocked and checked for expectations.
- Predictor mPredictor;
-
// mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
renderengine::mock::RenderEngine mRenderEngine;
std::unique_ptr<Flattener> mFlattener;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 8692ee6..ca4b6ab 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -70,13 +70,14 @@
mIsPrimary(args.isPrimary) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
- compositionengine::
- RenderSurfaceCreationArgs{ANativeWindow_getWidth(args.nativeWindow.get()),
- ANativeWindow_getHeight(args.nativeWindow.get()),
- args.nativeWindow, args.displaySurface,
- static_cast<size_t>(
- SurfaceFlinger::
- maxFrameBufferAcquiredBuffers)});
+ compositionengine::RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(ANativeWindow_getWidth(args.nativeWindow.get()))
+ .setDisplayHeight(ANativeWindow_getHeight(args.nativeWindow.get()))
+ .setNativeWindow(std::move(args.nativeWindow))
+ .setDisplaySurface(std::move(args.displaySurface))
+ .setMaxTextureCacheSize(
+ static_cast<size_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers))
+ .build());
if (!mFlinger->mDisableClientCompositionCache &&
SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
@@ -138,6 +139,10 @@
getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
}
+void DisplayDevice::enableLayerCaching(bool enable) {
+ getCompositionDisplay()->setLayerCachingEnabled(enable);
+}
+
hal::PowerMode DisplayDevice::getPowerMode() const {
return mPowerMode;
}
@@ -268,8 +273,9 @@
StringAppendF(&result, "+ %s\n", getDebugName().c_str());
StringAppendF(&result, " powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
+ const auto activeMode = getActiveMode();
StringAppendF(&result, " activeMode=%s\n",
- mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
+ activeMode ? to_string(*activeMode).c_str() : "none");
result.append(" supportedModes=\n");
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 68846d3..bf249cd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -157,6 +157,9 @@
void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
bool isPoweredOn() const;
+ // Enables layer caching on this DisplayDevice
+ void enableLayerCaching(bool enable);
+
ui::Dataspace getCompositionDataSpace() const;
/* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 21c9d74..4fee723 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -129,10 +129,10 @@
mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
- mCurrentState.treeHasFrameRateVote = false;
mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
mCurrentState.frameTimelineInfo = {};
mCurrentState.postTime = -1;
+ mCurrentState.destinationFrame.makeInvalid();
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -315,55 +315,6 @@
return reduce(mBounds, activeTransparentRegion);
}
-ui::Transform Layer::getBufferScaleTransform() const {
- // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
- // it isFixedSize) then there may be additional scaling not accounted
- // for in the layer transform.
- if (!isFixedSize() || getBuffer() == nullptr) {
- return {};
- }
-
- // If the layer is a buffer state layer, the active width and height
- // could be infinite. In that case, return the effective transform.
- const uint32_t activeWidth = getActiveWidth(getDrawingState());
- const uint32_t activeHeight = getActiveHeight(getDrawingState());
- if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) {
- return {};
- }
-
- int bufferWidth = getBuffer()->getWidth();
- int bufferHeight = getBuffer()->getHeight();
-
- if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
-
- float sx = activeWidth / static_cast<float>(bufferWidth);
- float sy = activeHeight / static_cast<float>(bufferHeight);
-
- ui::Transform extraParentScaling;
- extraParentScaling.set(sx, 0, 0, sy);
- return extraParentScaling;
-}
-
-ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
- // We need to mirror this scaling to child surfaces or we will break the contract where WM can
- // treat child surfaces as pixels in the parent surface.
- if (!isFixedSize() || getBuffer() == nullptr) {
- return mEffectiveTransform;
- }
- return mEffectiveTransform * bufferScaleTransform;
-}
-
-FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
- // We need the pre scaled layer bounds when computing child bounds to make sure the child is
- // cropped to its parent layer after any buffer transform scaling is applied.
- if (!isFixedSize() || getBuffer() == nullptr) {
- return mBounds;
- }
- return bufferScaleTransform.inverse().transform(mBounds);
-}
-
void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
float parentShadowRadius) {
const State& s(getDrawingState());
@@ -400,11 +351,8 @@
// don't pass it to its children.
const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
- // Add any buffer scaling to the layer's children.
- ui::Transform bufferScaleTransform = getBufferScaleTransform();
for (const sp<Layer>& child : mDrawingChildren) {
- child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
- getTransformWithScale(bufferScaleTransform), childShadowRadius);
+ child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
}
}
@@ -858,6 +806,11 @@
const State& s(getDrawingState());
State& c(getCurrentState());
+ // Translates dest frame into scale and position updates. This helps align geometry calculations
+ // for BufferStateLayer with other layers. This should ideally happen in the client once client
+ // has the display orientation details from WM.
+ updateGeometry();
+
if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
@@ -907,8 +860,15 @@
// list.
addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
}
+ const bool frameRateVoteChanged =
+ mDrawingState.frameRateForLayerTree != stateToCommit.frameRateForLayerTree;
mDrawingState = stateToCommit;
+ if (frameRateVoteChanged) {
+ mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+ LayerHistory::LayerUpdateType::SetFrameRate);
+ }
+
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -1311,8 +1271,7 @@
};
// update parents and children about the vote
- // First traverse the tree and count how many layers has votes. In addition
- // activate the layers in Scheduler's LayerHistory for it to check for changes
+ // First traverse the tree and count how many layers has votes.
int layersWithVote = 0;
traverseTree([&layersWithVote](Layer* layer) {
const auto layerVotedWithDefaultCompatibility =
@@ -1332,20 +1291,11 @@
}
});
- // Now update the other layers
+ // Now we can update the tree frame rate vote for each layer in the tree
+ const bool treeHasFrameRateVote = layersWithVote > 0;
bool transactionNeeded = false;
- traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) {
- const bool treeHasFrameRateVote = layersWithVote > 0;
- if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) {
- layer->mCurrentState.sequence++;
- layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote;
- layer->mCurrentState.modified = true;
- layer->setTransactionFlags(eTransactionNeeded);
- transactionNeeded = true;
-
- mFlinger->mScheduler->recordLayerHistory(layer, systemTime(),
- LayerHistory::LayerUpdateType::SetFrameRate);
- }
+ traverseTree([treeHasFrameRateVote, &transactionNeeded](Layer* layer) {
+ transactionNeeded = layer->updateFrameRateForLayerTree(treeHasFrameRateVote);
});
if (transactionNeeded) {
@@ -1474,32 +1424,42 @@
return surfaceFrame;
}
-Layer::FrameRate Layer::getFrameRateForLayerTree() const {
- const auto frameRate = getDrawingState().frameRate;
+bool Layer::updateFrameRateForLayerTree(bool treeHasFrameRateVote) {
+ const auto updateCurrentState = [&](FrameRate frameRate) {
+ if (mCurrentState.frameRateForLayerTree == frameRate) {
+ return false;
+ }
+ mCurrentState.frameRateForLayerTree = frameRate;
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+ };
+
+ const auto frameRate = mCurrentState.frameRate;
if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) {
- return frameRate;
+ return updateCurrentState(frameRate);
}
// This layer doesn't have a frame rate. Check if its ancestors have a vote
- if (sp<Layer> parent = getParent(); parent) {
- if (const auto parentFrameRate = parent->getFrameRateForLayerTree();
- parentFrameRate.rate.isValid()) {
- return parentFrameRate;
+ for (sp<Layer> parent = getParent(); parent; parent = parent->getParent()) {
+ if (parent->mCurrentState.frameRate.rate.isValid()) {
+ return updateCurrentState(parent->mCurrentState.frameRate);
}
}
// This layer and its ancestors don't have a frame rate. If one of successors
// has a vote, return a NoVote for successors to set the vote
- if (getDrawingState().treeHasFrameRateVote) {
- return {Fps(0.0f), FrameRateCompatibility::NoVote};
+ if (treeHasFrameRateVote) {
+ return updateCurrentState(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
}
- return frameRate;
+ return updateCurrentState(frameRate);
}
-// ----------------------------------------------------------------------------
-// pageflip handling...
-// ----------------------------------------------------------------------------
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+ return getDrawingState().frameRateForLayerTree;
+}
bool Layer::isHiddenByPolicy() const {
const State& s(mDrawingState);
@@ -1768,8 +1728,7 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
- child->computeBounds(newParent->mBounds,
- newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+ child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
newParent->mEffectiveShadowRadius);
}
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 284adbd..8139d8a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -239,8 +239,8 @@
FrameRate frameRate;
- // Indicates whether parents / children of this layer had set FrameRate
- bool treeHasFrameRateVote;
+ // The combined frame rate of parents / children of this layer
+ FrameRate frameRateForLayerTree;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -276,6 +276,7 @@
StretchEffect stretchEffect;
Rect bufferCrop;
+ Rect destinationFrame;
};
/*
@@ -646,16 +647,6 @@
// Compute bounds for the layer and cache the results.
void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
- // Returns the buffer scale transform if a scaling mode is set.
- ui::Transform getBufferScaleTransform() const;
-
- // Get effective layer transform, taking into account all its parent transform with any
- // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
- ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
-
- // Returns the bounds of the layer without any buffer scaling.
- FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
-
int32_t getSequence() const override { return sequence; }
// For tracing.
@@ -885,8 +876,10 @@
StretchEffect getStretchEffect() const;
virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
+ virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
virtual std::string getPendingBufferCounterName() { return ""; }
+ virtual void updateGeometry() {}
protected:
friend class impl::SurfaceInterceptor;
@@ -1064,6 +1057,8 @@
// Fills in the frame and transform info for the InputWindowInfo
void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
+ bool updateFrameRateForLayerTree(bool treeHasFrameRateVote);
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index beda834..4b0bdcb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -287,6 +287,8 @@
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
const String16 sDump("android.permission.DUMP");
+const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
@@ -381,6 +383,12 @@
mColorSpaceAgnosticDataspace =
static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
+ mLayerCachingEnabled = [] {
+ const bool enable =
+ android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+ return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+ }();
+
useContextPriority = use_context_priority(true);
using Values = SurfaceFlingerProperties::primary_display_orientation_values;
@@ -2174,11 +2182,10 @@
mFpsReporter->dispatchLayerFps();
}
hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
- for (auto& [key, value] : mHdrLayerInfoListeners) {
- if (value && value->hasListeners()) {
- auto listenersDisplay = getDisplayById(key);
- if (listenersDisplay) {
- hdrInfoListeners.emplace_back(listenersDisplay->getCompositionDisplay(), value);
+ for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
+ if (reporter && reporter->hasListeners()) {
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
}
}
}
@@ -2642,6 +2649,7 @@
builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
builder.setName(state.displayName);
const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+ compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled);
sp<compositionengine::DisplaySurface> displaySurface;
sp<IGraphicBufferProducer> producer;
@@ -2816,16 +2824,19 @@
}
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
- /*
- * Traversal of the children
- * (perform the transaction for each of them if needed)
- */
+ // Commit display transactions
+ const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
+ if (displayTransactionNeeded) {
+ processDisplayChangesLocked();
+ processDisplayHotplugEventsLocked();
+ }
- if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
- mForceTraversal = false;
+ // Commit layer transactions. This needs to happen after display transactions are
+ // committed because some geometry logic relies on display orientation.
+ if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
- if (!trFlags) return;
+ if (!trFlags && !displayTransactionNeeded) return;
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
@@ -2837,15 +2848,7 @@
});
}
- /*
- * Perform display own transactions if needed
- */
-
- if (transactionFlags & eDisplayTransactionNeeded) {
- processDisplayChangesLocked();
- processDisplayHotplugEventsLocked();
- }
-
+ // Update transform hint
if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
// The transform hint might have changed for some layers
// (either because a display has changed, or because a layer
@@ -4039,6 +4042,11 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eDestinationFrameChanged) {
+ if (layer->setDestinationFrame(s.destinationFrame)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
@@ -5187,9 +5195,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1038 are currently used for backdoors. The code
+ // Numbers from 1000 to 1040 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1039) {
+ if (code >= 1000 && code <= 1040) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5504,37 +5512,24 @@
}
case 1035: {
const int modeId = data.readInt32();
- mDebugDisplayModeSetByBackdoor = false;
- const auto displayId = [&]() -> std::optional<PhysicalDisplayId> {
- uint64_t inputDisplayId = 0;
- if (data.readUint64(&inputDisplayId) == NO_ERROR) {
- const auto token = getPhysicalDisplayToken(
- static_cast<PhysicalDisplayId>(inputDisplayId));
- if (!token) {
- ALOGE("No display with id: %" PRIu64, inputDisplayId);
- return std::nullopt;
- }
-
- return std::make_optional<PhysicalDisplayId>(inputDisplayId);
+ const auto display = [&]() -> sp<IBinder> {
+ uint64_t value;
+ if (data.readUint64(&value) != NO_ERROR) {
+ return getInternalDisplayToken();
}
- return getInternalDisplayId();
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) {
+ return getPhysicalDisplayToken(*id);
+ }
+
+ ALOGE("Invalid physical display ID");
+ return nullptr;
}();
- if (!displayId) {
- ALOGE("No display found");
- return NO_ERROR;
- }
-
- status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId);
- if (result != NO_ERROR) {
- return result;
- }
-
- mDebugDisplayModeSetByBackdoor = true;
-
- return NO_ERROR;
+ const status_t result = setActiveMode(display, modeId);
+ mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
+ return result;
}
case 1036: {
if (data.readInt32() > 0) {
@@ -5586,6 +5581,33 @@
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
return NO_ERROR;
}
+ // Toggle caching feature
+ // First argument is an int32 - nonzero enables caching and zero disables caching
+ // Second argument is an optional uint64 - if present, then limits enabling/disabling
+ // caching to a particular physical display
+ case 1040: {
+ n = data.readInt32();
+ std::optional<PhysicalDisplayId> inputId = std::nullopt;
+ if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
+ inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
+ if (!inputId || getPhysicalDisplayToken(*inputId)) {
+ ALOGE("No display with id: %" PRIu64, inputDisplayId);
+ return NAME_NOT_FOUND;
+ }
+ }
+ {
+ Mutex::Autolock lock(mStateLock);
+ mLayerCachingEnabled = n != 0;
+ for (const auto& [_, display] : mDisplays) {
+ if (!inputId || *inputId == display->getPhysicalId()) {
+ display->enableLayerCaching(mLayerCachingEnabled);
+ }
+ }
+ }
+ invalidateHwcGeometry();
+ repaintEverything();
+ return NO_ERROR;
+ }
}
}
return err;
@@ -5681,6 +5703,14 @@
}
}
+static bool hasCaptureBlackoutContentPermission() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+ PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid);
+}
+
static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -5719,34 +5749,6 @@
return NO_ERROR;
}
-sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
- if (const sp<IBinder> displayToken =
- getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
- return getDisplayDeviceLocked(displayToken);
- }
- // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
- // may not have a displayId.
- return getDisplayByLayerStack(displayOrLayerStack);
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayById(DisplayId displayId) const {
- for (const auto& [token, display] : mDisplays) {
- if (display->getId() == displayId) {
- return display;
- }
- }
- return nullptr;
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
- for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == layerStack) {
- return display;
- }
- }
- return nullptr;
-}
-
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -5758,7 +5760,7 @@
if (!args.displayToken) return BAD_VALUE;
- wp<DisplayDevice> displayWeak;
+ wp<const DisplayDevice> displayWeak;
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
ui::Dataspace dataspace;
@@ -5799,21 +5801,22 @@
captureListener);
}
-status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
+status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
- wp<DisplayDevice> displayWeak;
+ wp<const DisplayDevice> displayWeak;
ui::Size size;
ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
- sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+
+ const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
return NAME_NOT_FOUND;
}
- layerStack = display->getLayerStack();
- displayWeak = display;
+ displayWeak = display;
+ layerStack = display->getLayerStack();
size = display->getLayerStackSpaceRect().getSize();
dataspace =
@@ -5851,6 +5854,10 @@
Rect layerStackSpaceRect;
ui::Dataspace dataspace;
bool captureSecureLayers;
+
+ // Call this before holding mStateLock to avoid any deadlocking.
+ bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
+
{
Mutex::Autolock lock(mStateLock);
@@ -5860,9 +5867,8 @@
return NAME_NOT_FOUND;
}
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
+ if (!canCaptureBlackoutContent &&
+ parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
@@ -5895,7 +5901,11 @@
}
}
- const auto display = getDisplayByLayerStack(parent->getLayerStack());
+ const auto display =
+ findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+ return display.getLayerStack() == layerStack;
+ });
+
if (!display) {
return NAME_NOT_FOUND;
}
@@ -6012,8 +6022,7 @@
return BAD_VALUE;
}
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+ bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
if (mRefreshPending) {
@@ -6033,8 +6042,9 @@
status_t result = NO_ERROR;
renderArea->render([&] {
- result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
- regionSampling, grayscale, captureResults);
+ result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+ canCaptureBlackoutContent, regionSampling, grayscale,
+ captureResults);
});
captureResults.result = result;
@@ -6046,8 +6056,9 @@
status_t SurfaceFlinger::renderScreenImplLocked(
const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
- bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
+ ScreenCaptureResults& captureResults) {
ATRACE_CALL();
traverseLayers([&](Layer* layer) {
@@ -6060,7 +6071,7 @@
// We allow the system server to take screenshots of secure layers for
// use in situations like the Screen-rotation animation and place
// the impetus on WindowManager to not persist them.
- if (captureResults.capturedSecureLayers && !forSystem) {
+ if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
ALOGW("FB is protected: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4bbdd48..4aa047a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -620,12 +620,10 @@
sp<IDisplayEventConnection> createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
- status_t captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) override;
- status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener) override;
- status_t captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) override;
+
+ status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override;
+ status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override;
+ status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -911,12 +909,8 @@
const sp<IScreenCaptureListener>&);
status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
const std::shared_ptr<renderengine::ExternalTexture>&,
- bool forSystem, bool regionSampling, bool grayscale,
- ScreenCaptureResults&);
-
- sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
- sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
- sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
+ bool canCaptureBlackoutContent, bool regionSampling,
+ bool grayscale, ScreenCaptureResults&);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
@@ -959,6 +953,20 @@
return getDefaultDisplayDeviceLocked();
}
+ // Returns the first display that matches a `bool(const DisplayDevice&)` predicate.
+ template <typename Predicate>
+ sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
+ const auto it = std::find_if(mDisplays.begin(), mDisplays.end(),
+ [&](const auto& pair) { return p(*pair.second); });
+
+ return it == mDisplays.end() ? nullptr : it->second;
+ }
+
+ sp<const DisplayDevice> getDisplayDeviceLocked(DisplayId id) const REQUIRES(mStateLock) {
+ // TODO(b/182939859): Replace tokens with IDs for display lookup.
+ return findDisplay([id](const auto& display) { return display.getId() == id; });
+ }
+
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
@@ -1236,6 +1244,7 @@
int mDebugRegion = 0;
bool mDebugDisableHWC = false;
bool mDebugDisableTransformHint = false;
+ bool mLayerCachingEnabled = false;
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
bool mPropagateBackpressureClientComposition = false;
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 4753362..34c9182 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -19,6 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#include <gui/BufferItemConsumer.h>
+#include <private/android_filesystem_config.h>
#include "TransactionTestHarnesses.h"
namespace android {
@@ -170,7 +171,11 @@
args.displayToken = mDisplay;
ScreenCaptureResults captureResults;
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+ {
+ // Ensure the UID is not root because root has all permissions
+ UIDFaker f(AID_APP_START);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+ }
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 2e9c10c..6912fcf 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -84,7 +84,11 @@
Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ {
+ // Ensure the UID is not root because root has all permissions
+ UIDFaker f(AID_APP_START);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ }
UIDFaker f(AID_SYSTEM);
@@ -528,7 +532,7 @@
ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}
-TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
ISurfaceComposerClient::eFXSurfaceBufferState);
sp<SurfaceControl> secureLayer =
@@ -552,8 +556,12 @@
args.childrenOnly = false;
ScreenCaptureResults captureResults;
- // Call from outside system with secure layers will result in permission denied
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+ {
+ // Ensure the UID is not root because root has all permissions
+ UIDFaker f(AID_APP_START);
+ // Call from outside system with secure layers will result in permission denied
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+ }
UIDFaker f(AID_SYSTEM);
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 162711d..a9e935d 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -279,7 +279,7 @@
}
bool waitForHotplugEvent(Display displayId, bool connected) {
- return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+ return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
}
bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
@@ -305,7 +305,7 @@
}
bool waitForModeChangedEvent(Display display, int32_t modeId) {
- PhysicalDisplayId displayId(display);
+ PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 3042450..e42cf47 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -77,7 +77,7 @@
constexpr hal::HWLayerId HWC_LAYER = 5000;
constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index a3e8108..359e555 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -118,7 +118,7 @@
sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
+ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index b4a1481..62acc6b 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -41,10 +41,13 @@
namespace {
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(111u);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(222u);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT =
+ PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu);
+
constexpr std::chrono::duration VSYNC_PERIOD(16ms);
+
class MockVSyncSource : public VSyncSource {
public:
const char* getName() const override { return "test"; }
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 6916764..597e5e7 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -72,7 +72,8 @@
mIdleTimer->stop();
}
-TEST_F(OneShotTimerTest, resetTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
@@ -94,7 +95,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, resetBackToBackTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
@@ -144,7 +146,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_idleTimerIdlesTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
@@ -169,7 +172,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_timeoutCallbackExecutionTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 38e503f..7a6ae5b 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -34,7 +34,7 @@
namespace android {
namespace {
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
class SchedulerTest : public testing::Test {
protected: