Merge "Fixing threading around mConnections in Scheduler"
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index e174c8e..e15ac3f 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -100,6 +100,10 @@
}
}
+void DumpPool::deleteTempFiles() {
+ deleteTempFiles(tmp_root_);
+}
+
void DumpPool::setLogDuration(bool log_duration) {
log_duration_ = log_duration;
}
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
index a4ea875..0c3c2cc 100644
--- a/cmds/dumpstate/DumpPool.h
+++ b/cmds/dumpstate/DumpPool.h
@@ -134,6 +134,11 @@
*/
void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+ /*
+ * Deletes temporary files created by DumpPool.
+ */
+ void deleteTempFiles();
+
static const std::string PREFIX_TMPFILE_NAME;
private:
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d108e00..c8277fe 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2688,6 +2688,15 @@
}
tombstone_data_.clear();
anr_data_.clear();
+
+ // Instead of shutdown the pool, we delete temporary files directly since
+ // shutdown blocking the call.
+ if (dump_pool_) {
+ dump_pool_->deleteTempFiles();
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/*do_cancel =*/ true);
+ }
}
/*
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 3467898..67a77f6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -55,6 +55,7 @@
MOCK_METHOD1(listServices, Vector<String16>(int));
MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
MOCK_METHOD1(isDeclared, bool(const String16&));
+ MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 2c3efe5..99cb93a 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -141,7 +141,7 @@
}
}
- PipeRelay relay(out);
+ PipeRelay relay(out, err, interfaceName, instanceName);
if (relay.initCheck() != OK) {
std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 820679f..4e97636 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -23,7 +23,6 @@
#include <atomic>
-#include <android-base/logging.h>
#include <utils/Thread.h>
namespace android {
@@ -31,8 +30,15 @@
static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
+static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
+ auto dot = interfaceName.rfind(".");
+ if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
+ return "RelayThread_" + interfaceName + "_" + instanceName;
+}
+
struct PipeRelay::RelayThread : public Thread {
- explicit RelayThread(int fd, std::ostream &os);
+ explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &fqName);
bool threadLoop() override;
void setFinished();
@@ -40,6 +46,7 @@
private:
int mFd;
std::ostream &mOutStream;
+ NullableOStream<std::ostream> mErrStream;
// If we were to use requestExit() and exitPending() instead, threadLoop()
// may not run at all by the time ~PipeRelay is called (i.e. debug() has
@@ -47,13 +54,17 @@
// read() are executed until data are drained.
std::atomic_bool mFinished;
+ std::string mFqName;
+
DISALLOW_COPY_AND_ASSIGN(RelayThread);
};
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
- : mFd(fd), mOutStream(os), mFinished(false) {}
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
+ const NullableOStream<std::ostream> &err,
+ const std::string &fqName)
+ : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
bool PipeRelay::RelayThread::threadLoop() {
char buffer[1024];
@@ -66,13 +77,14 @@
int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
if (res < 0) {
- PLOG(INFO) << "select() failed";
+ mErrStream << "debug " << mFqName << ": select() failed";
return false;
}
if (res == 0 || !FD_ISSET(mFd, &set)) {
if (mFinished) {
- LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated.";
+ mErrStream << "debug " << mFqName
+ << ": timeout reading from pipe, output may be truncated.";
return false;
}
// timeout, but debug() has not returned, so wait for HAL to finish.
@@ -83,7 +95,7 @@
ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
if (n < 0) {
- PLOG(ERROR) << "read() failed";
+ mErrStream << "debug " << mFqName << ": read() failed";
}
if (n <= 0) {
@@ -101,8 +113,9 @@
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::PipeRelay(std::ostream &os)
- : mInitCheck(NO_INIT) {
+PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &interfaceName, const std::string &instanceName)
+ : mInitCheck(NO_INIT) {
int res = pipe(mFds);
if (res < 0) {
@@ -110,8 +123,8 @@
return;
}
- mThread = new RelayThread(mFds[0], os);
- mInitCheck = mThread->run("RelayThread");
+ mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
+ mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
}
void PipeRelay::CloseFd(int *fd) {
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index 835016041..bd994b4 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -21,6 +21,8 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include "NullableOStream.h"
+
namespace android {
namespace lshal {
@@ -28,7 +30,10 @@
* written to the "write"-end of the pair to the specified output stream "os".
*/
struct PipeRelay {
- explicit PipeRelay(std::ostream &os);
+ explicit PipeRelay(std::ostream& os,
+ const NullableOStream<std::ostream>& err,
+ const std::string& interfaceName,
+ const std::string& instanceName);
~PipeRelay();
status_t initCheck() const;
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index e80c321..c8355e2 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -37,6 +37,27 @@
namespace android {
#ifndef VENDORSERVICEMANAGER
+struct ManifestWithDescription {
+ std::shared_ptr<const vintf::HalManifest> manifest;
+ const char* description;
+};
+// func true -> stop search and forEachManifest will return true
+static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
+ for (const ManifestWithDescription& mwd : {
+ ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
+ ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
+ }) {
+ if (mwd.manifest == nullptr) {
+ LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
+ // note, we explicitly do not retry here, so that we can detect VINTF
+ // or other bugs (b/151696835)
+ continue;
+ }
+ if (func(mwd)) return true;
+ }
+ return false;
+}
+
static bool isVintfDeclared(const std::string& name) {
size_t firstSlash = name.find('/');
size_t lastDot = name.rfind('.', firstSlash);
@@ -49,31 +70,41 @@
const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
const std::string instance = name.substr(firstSlash+1);
- struct ManifestWithDescription {
- std::shared_ptr<const vintf::HalManifest> manifest;
- const char* description;
- };
- for (const ManifestWithDescription& mwd : {
- ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
- ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
- }) {
- if (mwd.manifest == nullptr) {
- LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
- // note, we explicitly do not retry here, so that we can detect VINTF
- // or other bugs (b/151696835)
- continue;
- }
+ bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
return true;
}
+ return false; // continue
+ });
+
+ if (!found) {
+ // Although it is tested, explicitly rebuilding qualified name, in case it
+ // becomes something unexpected.
+ LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
+ << " in the VINTF manifest.";
}
- // Although it is tested, explicitly rebuilding qualified name, in case it
- // becomes something unexpected.
- LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
- << " in the VINTF manifest.";
- return false;
+ return found;
+}
+
+static std::vector<std::string> getVintfInstances(const std::string& interface) {
+ size_t lastDot = interface.rfind('.');
+ if (lastDot == std::string::npos) {
+ LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface;
+ return {};
+ }
+ const std::string package = interface.substr(0, lastDot);
+ const std::string iface = interface.substr(lastDot+1);
+
+ std::vector<std::string> ret;
+ (void)forEachManifest([&](const ManifestWithDescription& mwd) {
+ auto instances = mwd.manifest->getAidlInstances(package, iface);
+ ret.insert(ret.end(), instances.begin(), instances.end());
+ return false; // continue
+ });
+
+ return ret;
}
static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
@@ -331,6 +362,30 @@
return Status::ok();
}
+binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ std::vector<std::string> allInstances;
+#ifndef VENDORSERVICEMANAGER
+ allInstances = getVintfInstances(interface);
+#endif
+
+ outReturn->clear();
+
+ for (const std::string& instance : allInstances) {
+ // TODO(b/169275998): allow checking policy only once for the interface
+ if (mAccess->canFind(ctx, interface + "/" + instance)) {
+ outReturn->push_back(instance);
+ }
+ }
+
+ if (outReturn->size() == 0 && allInstances.size() != 0) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ return Status::ok();
+}
+
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index a2fc5a8..9f43eb4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,6 +44,7 @@
const sp<IServiceCallback>& callback) override;
binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) override;
binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index cf78102..ccf4dc8 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,7 +38,6 @@
<!-- basic system services -->
<feature name="android.software.connectionservice" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
- <feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
diff --git a/include/input/Input.h b/include/input/Input.h
index 258adae..7d81fed 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -141,14 +141,6 @@
#define MAX_CONTROLLER_LEDS 4
/*
- * SystemUiVisibility constants from View.
- */
-enum {
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
-};
-
-/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8e00969..ad0a14e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -246,6 +246,8 @@
/* Return a new object that has a duplicate of this channel's fd. */
std::unique_ptr<InputChannel> dup() const;
+ void copyTo(InputChannel& outChannel) const;
+
status_t readFromParcel(const android::Parcel* parcel) override;
status_t writeToParcel(android::Parcel* parcel) const override;
@@ -277,6 +279,8 @@
}
private:
+ base::unique_fd dupFd() const;
+
std::string mName;
android::base::unique_fd mFd;
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index 3d04331..451918b 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -17,6 +17,7 @@
#ifndef _UTILS_PROPERTY_MAP_H
#define _UTILS_PROPERTY_MAP_H
+#include <android-base/result.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
@@ -78,7 +79,7 @@
inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
/* Loads a property map from a file. */
- static status_t load(const String8& filename, PropertyMap** outMap);
+ static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
private:
class Parser {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 16afecd..05fcc2b 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -448,6 +448,14 @@
return mLastTransactionBinderFlags;
}
+void IPCThreadState::setCallRestriction(ProcessState::CallRestriction restriction) {
+ mCallRestriction = restriction;
+}
+
+ProcessState::CallRestriction IPCThreadState::getCallRestriction() const {
+ return mCallRestriction;
+}
+
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9aa82d9..6d728dc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -74,6 +74,7 @@
Vector<String16> listServices(int dumpsysPriority) override;
sp<IBinder> waitForService(const String16& name16) override;
bool isDeclared(const String16& name) override;
+ Vector<String16> getDeclaredInstances(const String16& interface) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -373,4 +374,18 @@
return declared;
}
+Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
+ std::vector<std::string> out;
+ if (!mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out).isOk()) {
+ return {};
+ }
+
+ Vector<String16> res;
+ res.setCapacity(out.size());
+ for (const std::string& instance : out) {
+ res.push(String16(instance.c_str()));
+ }
+ return res;
+}
+
} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a530565..83ca687 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -282,9 +282,17 @@
// a driver API to get a handle to the context manager with
// proper reference counting.
+ IPCThreadState* ipc = IPCThreadState::self();
+
+ CallRestriction originalCallRestriction = ipc->getCallRestriction();
+ ipc->setCallRestriction(CallRestriction::NONE);
+
Parcel data;
- status_t status = IPCThreadState::self()->transact(
+ status_t status = ipc->transact(
0, IBinder::PING_TRANSACTION, data, nullptr, 0);
+
+ ipc->setCallRestriction(originalCallRestriction);
+
if (status == DEAD_OBJECT)
return nullptr;
}
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ff15460..2b1e492 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -99,6 +99,14 @@
boolean isDeclared(@utf8InCpp String name);
/**
+ * Returns all declared instances for a particular interface.
+ *
+ * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+ * passed here, then ["foo"] would be returned.
+ */
+ @utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
+
+ /**
* Request a callback when the number of clients of the service changes.
* Used by LazyServiceRegistrar to dynamically stop services that have no clients.
*/
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
index 8c0495c..e5c6333 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/fuzzer/binder.cpp
@@ -19,6 +19,8 @@
#include "util.h"
#include <android/os/IServiceManager.h>
+#include <binder/ParcelableHolder.h>
+#include <binder/PersistableBundle.h>
using ::android::status_t;
@@ -271,5 +273,20 @@
PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+
+ // additional parcelable objects defined in libbinder
+ [] (const ::android::Parcel& p, uint8_t data) {
+ using ::android::os::ParcelableHolder;
+ using ::android::Parcelable;
+ FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status";
+ Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL;
+ if ( (data & 1) == 1 ) {
+ stability = Parcelable::Stability::STABILITY_VINTF;
+ }
+ ParcelableHolder t = ParcelableHolder(stability);
+ status_t status = p.readParcelable(&t);
+ FUZZ_LOG() << "ParcelableHolder status: " << status;
+ },
+ PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
};
// clang-format on
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index aa256d4..49ef253 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -32,6 +32,8 @@
class IPCThreadState
{
public:
+ using CallRestriction = ProcessState::CallRestriction;
+
static IPCThreadState* self();
static IPCThreadState* selfOrNull(); // self(), but won't instantiate
@@ -99,6 +101,9 @@
void setLastTransactionBinderFlags(int32_t flags);
int32_t getLastTransactionBinderFlags() const;
+ void setCallRestriction(CallRestriction restriction);
+ CallRestriction getCallRestriction() const;
+
int64_t clearCallingIdentity();
// Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
@@ -157,7 +162,6 @@
// This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
// side.
static const int32_t kUnsetWorkSource = -1;
-
private:
IPCThreadState();
~IPCThreadState();
@@ -204,8 +208,7 @@
bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
-
- ProcessState::CallRestriction mCallRestriction;
+ CallRestriction mCallRestriction;
};
} // namespace android
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 1d520c1..3c5ccc1 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -96,6 +96,11 @@
* service.
*/
virtual bool isDeclared(const String16& name) = 0;
+
+ /**
+ * Get all instances of a service as declared in the VINTF manifest
+ */
+ virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
};
sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index f59bb75..439b019 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -199,6 +199,9 @@
public:
/**
* Takes ownership of a.
+ *
+ * WARNING: this constructor is only expected to be used when reading a
+ * status value. Use `ScopedAStatus::ok()` instead.
*/
explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
~ScopedAStatus() {}
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
new file mode 100644
index 0000000..6701518
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcelable_utils.h
+ * @brief Helper for parcelable.
+ */
+
+#pragma once
+
+namespace ndk {
+// Also see Parcelable.h in libbinder.
+typedef int32_t parcelable_stability_t;
+enum {
+ STABILITY_LOCAL,
+ STABILITY_VINTF, // corresponds to @VintfStability
+};
+} // namespace ndk
+
+/** @} */
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c33c44f..722ae23 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -272,7 +272,7 @@
}
binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) {
- return PruneStatusT(status->get()->writeToParcel(parcel->get()));
+ return PruneStatusT(status->get().writeToParcel(parcel->get()));
}
binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) {
::android::binder::Status bstatus;
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index d889593..a8ae441 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -23,7 +23,8 @@
using ::android::binder::Status;
AStatus* AStatus_newOk() {
- return new AStatus();
+ static AStatus status = AStatus();
+ return &status;
}
AStatus* AStatus_fromExceptionCode(binder_exception_t exception) {
@@ -47,27 +48,27 @@
}
bool AStatus_isOk(const AStatus* status) {
- return status->get()->isOk();
+ return status->get().isOk();
}
binder_exception_t AStatus_getExceptionCode(const AStatus* status) {
- return PruneException(status->get()->exceptionCode());
+ return PruneException(status->get().exceptionCode());
}
int32_t AStatus_getServiceSpecificError(const AStatus* status) {
- return status->get()->serviceSpecificErrorCode();
+ return status->get().serviceSpecificErrorCode();
}
binder_status_t AStatus_getStatus(const AStatus* status) {
- return PruneStatusT(status->get()->transactionError());
+ return PruneStatusT(status->get().transactionError());
}
const char* AStatus_getMessage(const AStatus* status) {
- return status->get()->exceptionMessage().c_str();
+ return status->get().exceptionMessage().c_str();
}
const char* AStatus_getDescription(const AStatus* status) {
- android::String8 description = status->get()->toString8();
+ android::String8 description = status->get().toString8();
char* cStr = new char[description.size() + 1];
memcpy(cStr, description.c_str(), description.size() + 1);
return cStr;
@@ -78,7 +79,9 @@
}
void AStatus_delete(AStatus* status) {
- delete status;
+ if (status != AStatus_newOk()) {
+ delete status;
+ }
}
binder_status_t PruneStatusT(status_t status) {
diff --git a/libs/binder/ndk/status_internal.h b/libs/binder/ndk/status_internal.h
index f6227f7..cb96e48 100644
--- a/libs/binder/ndk/status_internal.h
+++ b/libs/binder/ndk/status_internal.h
@@ -25,8 +25,7 @@
AStatus() {} // ok
explicit AStatus(::android::binder::Status&& status) : mStatus(std::move(status)) {}
- ::android::binder::Status* get() { return &mStatus; }
- const ::android::binder::Status* get() const { return &mStatus; }
+ const ::android::binder::Status& get() const { return mStatus; }
private:
::android::binder::Status mStatus;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 0234820..dc8270e 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -28,11 +28,8 @@
rust_bindgen {
name: "libbinder_ndk_bindgen",
crate_name: "binder_ndk_bindgen",
- wrapper_src: "sys/BinderBindings.h",
+ wrapper_src: "sys/BinderBindings.hpp",
source_stem: "bindings",
- cflags: [
- "-x c++",
- ],
bindgen_flags: [
// Unfortunately the only way to specify the rust_non_exhaustive enum
// style for a type is to make it the default
diff --git a/libs/binder/rust/sys/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.hpp
similarity index 100%
rename from libs/binder/rust/sys/BinderBindings.h
rename to libs/binder/rust/sys/BinderBindings.hpp
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6964324..4ecbe53 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -61,4 +61,16 @@
return mNameToService.find(name) != mNameToService.end();
}
+Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+ Vector<String16> out;
+ const String16 prefix = name + String16("/");
+ for (const auto& [registeredName, service] : mNameToService) {
+ (void) service;
+ if (registeredName.startsWith(prefix)) {
+ out.add(String16(registeredName.string() + prefix.size()));
+ }
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index 62311d4..4ef47fb 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -30,42 +30,23 @@
public:
ServiceManager();
- /**
- * Equivalent of checkService.
- */
sp<IBinder> getService( const String16& name) const override;
- /**
- * Retrieve an existing service, non-blocking.
- */
sp<IBinder> checkService( const String16& name) const override;
- /**
- * Register a service.
- */
status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated = false,
int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
- /**
- * Return list of all existing services.
- */
Vector<String16> listServices(int dumpsysFlags = 0) override;
IBinder* onAsBinder() override;
- /**
- * Effectively no-oped in this implementation - equivalent to checkService.
- */
sp<IBinder> waitForService(const String16& name) override;
- /**
- * Check if a service is declared (e.g. VINTF manifest).
- *
- * If this returns true, waitForService should always be able to return the
- * service.
- */
- bool isDeclared(const String16& name) override;
+ bool isDeclared(const String16& name) override;
+
+ Vector<String16> getDeclaredInstances(const String16& iface) override;
private:
std::map<String16, sp<IBinder>> mNameToService;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 4d306e7..ef4d870 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -68,12 +68,10 @@
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
mSurfaceControl = sc;
- std::unique_ptr<InputChannel> clientChannel;
- InputChannel::openInputChannelPair("testchannels", mServerChannel, clientChannel);
- mClientChannel = std::move(clientChannel);
mInputFlinger = getInputFlinger();
- mInputFlinger->registerInputChannel(*mServerChannel);
+ mClientChannel = std::make_shared<InputChannel>();
+ mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
populateInputInfo(width, height);
@@ -155,7 +153,7 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel->getConnectionToken()); }
+ ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
const sp<SurfaceControl>&)> transactionBody) {
@@ -192,7 +190,7 @@
}
void populateInputInfo(int width, int height) {
- mInputInfo.token = mServerChannel->getConnectionToken();
+ mInputInfo.token = mClientChannel->getConnectionToken();
mInputInfo.name = "Test info";
mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
@@ -219,7 +217,6 @@
}
public:
sp<SurfaceControl> mSurfaceControl;
- std::unique_ptr<InputChannel> mServerChannel;
std::shared_ptr<InputChannel> mClientChannel;
sp<IInputFlinger> mInputFlinger;
@@ -536,4 +533,73 @@
surface->assertFocusChange(true);
}
+
+TEST_F(InputSurfacesTest, rotate_surface) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(10, 10);
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees
+ });
+ injectTap(8, 11);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees
+ });
+ injectTap(9, 8);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees
+ });
+ injectTap(12, 9);
+ surface->expectTap(1, 2);
+}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(10, 10);
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+ });
+ injectTap(2, 12);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+ });
+ injectTap(8, 2);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+ });
+ injectTap(18, 8);
+ surface->expectTap(1, 2);
+}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->mInputInfo.surfaceInset = 5;
+ surface->showAt(100, 100);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+ });
+ injectTap(40, 120);
+ surface->expectTap(5, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+ });
+ injectTap(80, 40);
+ surface->expectTap(5, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+ });
+ injectTap(160, 80);
+ surface->expectTap(5, 10);
+}
+
} // namespace android::test
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 79e15c1..85df405 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -377,22 +377,16 @@
}
std::unique_ptr<InputChannel> InputChannel::dup() const {
- android::base::unique_fd newFd(::dup(getFd()));
- if (!newFd.ok()) {
- ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
- strerror(errno));
- const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
- // If this process is out of file descriptors, then throwing that might end up exploding
- // on the other side of a binder call, which isn't really helpful.
- // Better to just crash here and hope that the FD leak is slow.
- // Other failures could be client errors, so we still propagate those back to the caller.
- LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
- getName().c_str());
- return nullptr;
- }
+ base::unique_fd newFd(dupFd());
return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
}
+void InputChannel::copyTo(InputChannel& outChannel) const {
+ outChannel.mName = getName();
+ outChannel.mFd = dupFd();
+ outChannel.mToken = getConnectionToken();
+}
+
status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
if (parcel == nullptr) {
ALOGE("%s: Null parcel", __func__);
@@ -415,6 +409,23 @@
return mToken;
}
+base::unique_fd InputChannel::dupFd() const {
+ android::base::unique_fd newFd(::dup(getFd()));
+ if (!newFd.ok()) {
+ ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
+ strerror(errno));
+ const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+ // If this process is out of file descriptors, then throwing that might end up exploding
+ // on the other side of a binder call, which isn't really helpful.
+ // Better to just crash here and hope that the FD leak is slow.
+ // Other failures could be client errors, so we still propagate those back to the caller.
+ LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
+ getName().c_str());
+ return {};
+ }
+ return newFd;
+}
+
// --- InputPublisher ---
InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 4833eb9..a842166 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -107,23 +107,22 @@
}
}
-status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
- *outMap = nullptr;
+android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) {
+ std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>();
+ if (outMap == nullptr) {
+ return android::base::Error(NO_MEMORY) << "Error allocating property map.";
+ }
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
+ Tokenizer* rawTokenizer;
+ status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
+ std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
if (status) {
- ALOGE("Error %d opening property file %s.", status, filename.string());
+ ALOGE("Error %d opening property file %s.", status, filename);
} else {
- PropertyMap* map = new PropertyMap();
- if (!map) {
- ALOGE("Error allocating property map.");
- status = NO_MEMORY;
- } else {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
- Parser parser(map, tokenizer);
+ Parser parser(outMap.get(), tokenizer.get());
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
@@ -132,14 +131,10 @@
elapsedTime / 1000000.0);
#endif
if (status) {
- delete map;
- } else {
- *outMap = map;
+ return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
}
- }
- delete tokenizer;
}
- return status;
+ return std::move(outMap);
}
// --- PropertyMap::Parser ---
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
index 23ead0e..afb97a1 100755
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -42,7 +42,7 @@
android::String8 out;
propertyMap.tryGetProperty(key, out);
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
TemporaryFile tf;
// Generate file contents
std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
@@ -52,8 +52,7 @@
const char* bytes = contents.c_str();
android::base::WriteStringToFd(bytes, tf.fd);
}
- android::PropertyMap* mapPtr = &propertyMap;
- android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+ android::PropertyMap::load(tf.path);
},
[](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
@@ -65,12 +64,12 @@
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider dataProvider(data, size);
- android::PropertyMap proprtyMap = android::PropertyMap();
+ android::PropertyMap propertyMap = android::PropertyMap();
int opsRun = 0;
while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- operations[op](&dataProvider, proprtyMap);
+ operations[op](&dataProvider, propertyMap);
}
return 0;
}
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 0cdf0bb..1771d19 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -30,8 +30,8 @@
// shouldn't be a concern.
oneway void setInputWindows(in InputWindowInfo[] inputHandles,
in @nullable ISetInputWindowsListener setInputWindowsListener);
- void registerInputChannel(in InputChannel channel);
- void unregisterInputChannel(in IBinder connectionToken);
+ InputChannel createInputChannel(in @utf8InCpp String name);
+ void removeInputChannel(in IBinder connectionToken);
/**
* Sets focus to the window identified by the token. This must be called
* after updating any input window handles.
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index b7c5b2a..470f28f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -455,12 +455,18 @@
}
void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AChoreographer_postFrameCallback(choreographer, callback, data);
+#pragma clang diagnostic pop
}
void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+#pragma clang diagnostic pop
}
void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 5438082..cb752b0 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -35,6 +35,7 @@
#include <SkCanvas.h>
#include <SkImage.h>
+#include <SkShadowUtils.h>
#include <SkSurface.h>
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -400,13 +401,12 @@
const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
static_cast<SkScalar>(display.clip.height());
canvas->scale(scaleX, scaleY);
- canvas->clipRect(SkRect::MakeLTRB(display.clip.left, display.clip.top, display.clip.right,
- display.clip.bottom));
+ canvas->clipRect(getSkRect(display.clip));
canvas->drawColor(0, SkBlendMode::kSrc);
for (const auto& layer : layers) {
SkPaint paint;
const auto& bounds = layer->geometry.boundaries;
- const auto dest = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ const auto dest = getSkRect(bounds);
if (layer->source.buffer.buffer) {
ATRACE_NAME("DrawImage");
@@ -442,6 +442,13 @@
canvas->save();
canvas->concat(getSkM44(layer->geometry.positionTransform));
+ if (layer->shadow.length > 0) {
+ const auto rect = layer->geometry.roundedCornersRadius > 0
+ ? getSkRect(layer->geometry.roundedCornersCrop)
+ : dest;
+ drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+ }
+
if (layer->geometry.roundedCornersRadius > 0) {
canvas->drawRRect(getRoundedRect(layer), paint);
} else {
@@ -480,13 +487,24 @@
return NO_ERROR;
}
+inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
- const auto& crop = layer->geometry.roundedCornersCrop;
- const auto rect = SkRect::MakeLTRB(crop.left, crop.top, crop.right, crop.bottom);
+ const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
const auto cornerRadius = layer->geometry.roundedCornersRadius;
return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
}
+inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
+ return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
+}
+
inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
@@ -494,6 +512,10 @@
matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
}
+inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
+ return SkPoint3::Make(vector.x, vector.y, vector.z);
+}
+
size_t SkiaGLRenderEngine::getMaxTextureSize() const {
return mGrContext->maxTextureSize();
}
@@ -502,6 +524,22 @@
return mGrContext->maxRenderTargetSize();
}
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+ 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),
+ getSkPoint3(settings.lightPos), settings.lightRadius,
+ getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
+ flags);
+}
+
EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext, bool useContextPriority,
Protection protection) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 7103bbd..3da7f25 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -60,11 +60,17 @@
Protection protection);
static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
+ inline SkRect getSkRect(const FloatRect& layer);
+ inline SkRect getSkRect(const Rect& layer);
inline SkRRect getRoundedRect(const LayerSettings* layer);
+ inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
+ inline SkPoint3 getSkPoint3(const vec3& vector);
base::unique_fd flush();
bool waitFence(base::unique_fd fenceFd);
+ void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index ec8a78a..cd68c1c 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "Transform"
+
#include <math.h>
#include <android-base/stringprintf.h>
@@ -132,7 +135,7 @@
}
float Transform::getScaleX() const {
- return sqrt(dsdx() * dsdx()) + (dtdx() * dtdx());
+ return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx()));
}
float Transform::getScaleY() const {
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 6670dc0..58323e5 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -202,6 +202,15 @@
// the input.
Rect transform(uint32_t xform, int32_t width, int32_t height) const;
+ Rect scale(float scaleX, float scaleY) const {
+ return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY));
+ }
+
+ Rect& scaleSelf(float scaleX, float scaleY) {
+ set(scale(scaleX, scaleY));
+ return *this;
+ }
+
// this calculates (Region(*this) - exclude).bounds() efficiently
Rect reduce(const Rect& exclude) const;
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 28ef77a..032dd4c 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -85,6 +85,14 @@
}
cc_test {
+ name: "Rect_test",
+ test_suites: ["device-tests"],
+ shared_libs: ["libui"],
+ srcs: ["Rect_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "Size_test",
test_suites: ["device-tests"],
shared_libs: ["libui"],
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
new file mode 100644
index 0000000..5499a5b
--- /dev/null
+++ b/libs/ui/tests/Rect_test.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 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.
+ */
+
+#include <system/graphics.h>
+#include <ui/FloatRect.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(RectTest, constructDefault) {
+ const Rect rect;
+ EXPECT_FALSE(rect.isValid());
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, constructFromWidthAndHeight) {
+ const Rect rect(100, 200);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromSize) {
+ const Rect rect(Size(100, 200));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromLTRB) {
+ const Rect rect(11, 12, 14, 14);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromPoints) {
+ const Rect rect(Point(11, 12), Point(14, 14));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromFloatRect) {
+ {
+ const Rect rect(FloatRect(10, 20, 30, 40));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+ // Construct with floating point error
+ {
+ constexpr float kError = 1e-3;
+ const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+}
+
+TEST(RectTest, makeInvalid) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_TRUE(rect.isValid());
+ rect.makeInvalid();
+ EXPECT_FALSE(rect.isValid());
+}
+
+TEST(RectTest, clear) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_FALSE(rect.isEmpty());
+ rect.clear();
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, getSize) {
+ const Rect rect(10, 20, 60, 60);
+ EXPECT_EQ(Size(50, 40), rect.getSize());
+}
+
+TEST(RectTest, getBounds) {
+ const Rect rect(10, 20, 60, 60);
+ const Rect bounds = rect.getBounds();
+ EXPECT_EQ(0, bounds.left);
+ EXPECT_EQ(0, bounds.top);
+ EXPECT_EQ(50, bounds.right);
+ EXPECT_EQ(40, bounds.bottom);
+ EXPECT_EQ(rect.getSize(), bounds.getSize());
+}
+
+TEST(RectTest, getCornerPoints) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Point(10, 20), rect.leftTop());
+ EXPECT_EQ(Point(10, 60), rect.leftBottom());
+ EXPECT_EQ(Point(50, 20), rect.rightTop());
+ EXPECT_EQ(Point(50, 60), rect.rightBottom());
+}
+
+TEST(RectTest, operatorEquals) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(rect, rect);
+ EXPECT_NE(Rect(0, 20, 50, 60), rect);
+ EXPECT_NE(Rect(10, 0, 50, 60), rect);
+ EXPECT_NE(Rect(10, 20, 0, 60), rect);
+ EXPECT_NE(Rect(10, 20, 50, 0), rect);
+}
+
+TEST(RectTest, operatorsPlusMinus) {
+ Rect rect = Rect(10, 20, 50, 60) + Point(1, 2);
+ EXPECT_EQ(Rect(11, 22, 51, 62), rect);
+ rect -= Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+
+ rect = Rect(10, 20, 50, 60) - Point(1, 2);
+ EXPECT_EQ(Rect(9, 18, 49, 58), rect);
+ rect += Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+}
+
+TEST(RectTest, scale) {
+ Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f));
+ rect.scaleSelf(2.f, 3.f);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+
+ rect = Rect(10, 20, 50, 60);
+ constexpr float kError = 1e-3;
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError));
+ rect.scaleSelf(2.f - kError, 3.f - kError);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+}
+
+TEST(RectTest, inset) {
+ Rect rect(10, 20, 50, 60);
+ rect.inset(0, 0, 0, 0);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+ rect.inset(1, 2, 3, 4);
+ EXPECT_EQ(Rect(11, 22, 47, 56), rect);
+}
+
+TEST(RectTest, intersect) {
+ const Rect rect(10, 20, 50, 60);
+ Rect intersection;
+
+ // Intersect with self is self
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(10, 20, 50, 60), intersection);
+
+ // Intersect with rect contained in us
+ const Rect insideRect(11, 21, 45, 55);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(insideRect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Intersect with rect we are contained in
+ intersection.makeInvalid();
+ EXPECT_TRUE(insideRect.intersect(rect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Empty intersection
+ intersection.makeInvalid();
+ EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection));
+ EXPECT_TRUE(intersection.isEmpty());
+
+ // Partial intersection
+ const Rect other(30, 40, 70, 80);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(other, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+
+ // Intersetion is commutative
+ intersection.makeInvalid();
+ EXPECT_TRUE(other.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+}
+
+TEST(RectTest, reduce) {
+ const Rect rect(10, 20, 50, 60);
+
+ // Reduce with self is empty
+ EXPECT_TRUE(rect.reduce(rect).isEmpty());
+
+ // Reduce with rect entirely inside is a noop
+ const Rect insideRect(11, 21, 45, 55);
+ EXPECT_EQ(rect, rect.reduce(insideRect));
+
+ // Reduce with rect entirely outside is empty
+ EXPECT_TRUE(insideRect.reduce(rect).isEmpty());
+
+ // Reduce with rect on the right
+ EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70)));
+
+ // Reduce with rect on the left
+ EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70)));
+
+ // Reduce with rect at the top
+ EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40)));
+
+ // Reduce with rect at the bottom
+ EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70)));
+}
+
+TEST(RectTest, transform) {
+ const int32_t width = 100, height = 200;
+ const Rect rect(1, 1, 2, 3);
+ EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height));
+ EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height));
+ EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height));
+ EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height));
+ EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height));
+}
+
+TEST(RectTest, toFloatRect) {
+ const Rect rect(10, 20, 50, 60);
+ const FloatRect floatRect = rect.toFloatRect();
+ EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 38f37ad..5f75aea 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -33,8 +33,7 @@
#include <gtest/gtest.h>
-namespace android {
-namespace ui {
+namespace android::ui {
TEST(SizeTest, BasicConstructionAndEqualityComparison) {
Size s(123, 456);
@@ -215,5 +214,4 @@
ClampTest(uint32_t(0), int32_t(0));
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
index 7fcd7de..eece18e 100644
--- a/libs/ui/tests/TEST_MAPPING
+++ b/libs/ui/tests/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "Size_test"
+ },
+ {
+ "name": "Rect_test"
}
]
}
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index 45e8367..c5f8859 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -60,7 +60,7 @@
void SetUp() override {
SKIP_IF_BPF_NOT_SUPPORTED;
- ASSERT_EQ(0, bpf::setrlimitForTest());
+ bpf::setrlimitForTest();
mGpuMem = std::make_unique<GpuMem>();
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 8af9bcb..3d99589 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -31,6 +31,25 @@
namespace android {
+static int32_t exceptionCodeFromStatusT(status_t status) {
+ switch (status) {
+ case OK:
+ return binder::Status::EX_NONE;
+ case INVALID_OPERATION:
+ return binder::Status::EX_UNSUPPORTED_OPERATION;
+ case BAD_VALUE:
+ case BAD_TYPE:
+ case NAME_NOT_FOUND:
+ return binder::Status::EX_ILLEGAL_ARGUMENT;
+ case NO_INIT:
+ return binder::Status::EX_ILLEGAL_STATE;
+ case PERMISSION_DENIED:
+ return binder::Status::EX_SECURITY;
+ default:
+ return binder::Status::EX_TRANSACTION_FAILED;
+ }
+}
+
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
@@ -119,7 +138,7 @@
}
// Used by tests only.
-binder::Status InputManager::registerInputChannel(const InputChannel& channel) {
+binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid != AID_SHELL && uid != AID_ROOT) {
@@ -128,12 +147,17 @@
return binder::Status::ok();
}
- mDispatcher->registerInputChannel(channel.dup());
+ base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
+ if (!channel) {
+ return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
+ channel.error().message().c_str());
+ }
+ (*channel)->copyTo(*outChannel);
return binder::Status::ok();
}
-binder::Status InputManager::unregisterInputChannel(const sp<IBinder>& connectionToken) {
- mDispatcher->unregisterInputChannel(connectionToken);
+binder::Status InputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+ mDispatcher->removeInputChannel(connectionToken);
return binder::Status::ok();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 35d2d58..49bea13 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -108,8 +108,8 @@
const std::vector<InputWindowInfo>& handles,
const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- binder::Status registerInputChannel(const InputChannel& channel) override;
- binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+ binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+ binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
binder::Status setFocusedWindow(const FocusRequest&) override;
private:
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b645d69..46bc055 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -129,17 +129,14 @@
protected:
explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
: mDispatcher(dispatcher) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mServerChannel = std::move(serverChannel);
- mClientChannel = std::move(clientChannel);
+ mClientChannel = *mDispatcher->createInputChannel(name);
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {}
sp<InputDispatcher> mDispatcher;
- std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+ std::shared_ptr<InputChannel> mClientChannel;
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
};
@@ -152,14 +149,12 @@
FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name)
: FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
- mDispatcher->registerInputChannel(mServerChannel);
-
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
}
virtual bool updateInfo() override {
- mInfo.token = mServerChannel->getConnectionToken();
+ mInfo.token = mClientChannel->getConnectionToken();
mInfo.name = "FakeWindowHandle";
mInfo.type = InputWindowInfo::Type::APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a10da66..3ccb0c9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -28,8 +28,8 @@
// Log debug messages about the dispatch cycle.
#define DEBUG_DISPATCH_CYCLE 0
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
+// Log debug messages about channel creation
+#define DEBUG_CHANNEL_CREATION 0
// Log debug messages about input event injection.
#define DEBUG_INJECTION 0
@@ -351,6 +351,16 @@
}
}
+static status_t openInputChannelPair(const std::string& name,
+ std::shared_ptr<InputChannel>& serverChannel,
+ std::unique_ptr<InputChannel>& clientChannel) {
+ std::unique_ptr<InputChannel> uniqueServerChannel;
+ status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
+
+ serverChannel = std::move(uniqueServerChannel);
+ return result;
+}
+
const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
switch (result) {
case InputDispatcher::FocusResult::OK:
@@ -412,7 +422,7 @@
while (!mConnectionsByFd.empty()) {
sp<Connection> connection = mConnectionsByFd.begin()->second;
- unregisterInputChannel(connection->inputChannel->getConnectionToken());
+ removeInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -507,7 +517,7 @@
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- onAnrLocked(connection);
+ onAnrLocked(*connection);
return LONG_LONG_MIN;
}
@@ -1108,11 +1118,17 @@
(entry->policyFlags & POLICY_FLAG_TRUSTED) &&
(!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry &&
- mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode &&
// We have seen two identical key downs in a row which indicates that the device
// driver is automatically generating key repeats itself. We take note of the
// repeat here, but we disable our own next key repeat timer since it is clear that
// we will not need to synthesize key repeats ourselves.
+ mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) {
+ // Make sure we don't get key down from a different device. If a different
+ // device Id has same key pressed down, the new device Id will replace the
+ // current one to hold the key repeat with repeat count reset.
+ // In the future when got a KEY_UP on the device id, drop it and do not
+ // stop the key repeat on current device.
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
@@ -1123,6 +1139,12 @@
}
mKeyRepeatState.lastKeyEntry = entry;
entry->refCount += 1;
+ } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
+ mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
+ // The stale device releases the key, reset staleDeviceId.
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+#endif
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -2825,8 +2847,8 @@
}
}
- // Unregister the channel.
- d->unregisterInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
+ // Remove the channel.
+ d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
return 0; // remove the callback
} // release lock
}
@@ -4403,71 +4425,76 @@
}
}
-status_t InputDispatcher::registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
+base::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> clientChannel;
+ status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+
+ if (result) {
+ return base::Error(result) << "Failed to open input channel pair with name " << name;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
- if (existingConnection != nullptr) {
- ALOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().c_str());
- return BAD_VALUE;
- }
+ sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
- sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
-
- int fd = inputChannel->getFd();
+ int fd = serverChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+ mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
- return OK;
+ return clientChannel;
}
-status_t InputDispatcher::registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) {
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
+ int32_t displayId, bool isGestureMonitor, const std::string& name) {
+ std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+ if (result) {
+ return base::Error(result) << "Failed to open input channel pair with name " << name;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
if (displayId < 0) {
- ALOGW("Attempted to register input monitor without a specified display.");
- return BAD_VALUE;
+ return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
+ << " without a specified display.";
}
- if (inputChannel->getConnectionToken() == nullptr) {
- ALOGW("Attempted to register input monitor without an identifying token.");
- return BAD_VALUE;
- }
+ sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
- sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
-
- const int fd = inputChannel->getFd();
+ const int fd = serverChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+ mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
- monitorsByDisplay[displayId].emplace_back(inputChannel);
+ monitorsByDisplay[displayId].emplace_back(serverChannel);
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
}
+
// Wake the looper because some connections have changed.
mLooper->wake();
- return OK;
+ return clientChannel;
}
-status_t InputDispatcher::unregisterInputChannel(const sp<IBinder>& connectionToken) {
+status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
- status_t status = unregisterInputChannelLocked(connectionToken, false /*notify*/);
+ status_t status = removeInputChannelLocked(connectionToken, false /*notify*/);
if (status) {
return status;
}
@@ -4479,8 +4506,8 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<IBinder>& connectionToken,
- bool notify) {
+status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+ bool notify) {
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
ALOGW("Attempted to unregister already unregistered input channel");
@@ -4643,12 +4670,12 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::onAnrLocked(const Connection& connection) {
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
- if (connection->waitQueue.empty()) {
+ if (connection.waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
- connection->inputChannel->getName().c_str());
+ connection.inputChannel->getName().c_str());
return;
}
/**
@@ -4659,21 +4686,21 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ DispatchEntry* oldestEntry = *connection.waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
- connection->inputChannel->getName().c_str(),
+ connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
- updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->inputChannel = connection.inputChannel;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4773,15 +4800,15 @@
void InputDispatcher::extendAnrTimeoutsLocked(
const std::shared_ptr<InputApplicationHandle>& application,
const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
+ if (connectionToken == nullptr && application != nullptr) {
+ // The ANR happened because there's no focused window
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
+ mAwaitedFocusedApplication = application;
+ }
+
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
- if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
- // Maybe ANR happened because there's no focused window?
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
- mAwaitedFocusedApplication = application;
- } else {
- // It's also possible that the connection already disappeared. No action necessary.
- }
+ // It's possible that the connection already disappeared. No action necessary.
return;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index f3b3dda..4fcdcc2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -116,12 +116,12 @@
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
- virtual status_t registerInputChannel(
- const std::shared_ptr<InputChannel>& inputChannel) override;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ const std::string& name) override;
virtual void setFocusedWindow(const FocusRequest&) override;
- virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) override;
- virtual status_t unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+ int32_t displayId, bool isGestureMonitor, const std::string& name) override;
+ virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
virtual status_t pilferPointers(const sp<IBinder>& token) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
@@ -513,7 +513,7 @@
void removeMonitorChannelLocked(
const sp<IBinder>& connectionToken,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- status_t unregisterInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
+ status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -525,7 +525,7 @@
int32_t displayId, std::string_view reason) REQUIRES(mLock);
void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
REQUIRES(mLock);
- void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const Connection& connection) REQUIRES(mLock);
void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index c3d50ea..67d9a06 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,6 +18,7 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
#include <InputListener.h>
+#include <android-base/result.h>
#include <android/FocusRequest.h>
#include <android/os/ISetInputWindowsListener.h>
#include <input/InputApplication.h>
@@ -155,13 +156,16 @@
*/
virtual void setFocusedWindow(const FocusRequest&) = 0;
- /* Registers input channels that may be used as targets for input events.
+ /**
+ * Creates an input channel that may be used as targets for input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ const std::string& name) = 0;
- /* Registers input channels to be used to monitor input events.
+ /**
+ * Creates an input channel to be used to monitor input events.
*
* Each monitor must target a specific display and will only receive input events sent to that
* display. If the monitor is a gesture monitor, it will only receive pointer events on the
@@ -169,14 +173,14 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool gestureMonitor) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+ int32_t displayId, bool gestureMonitor, const std::string& name) = 0;
- /* Unregister input channels that will no longer receive input events.
+ /* Removes input channels that will no longer receive input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t unregisterInputChannel(const sp<IBinder>& connectionToken) = 0;
+ virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) = 0;
/* Allows an input monitor steal the current pointer stream away from normal input windows.
*
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index cabc782..2ebdbcf 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -36,7 +36,7 @@
#define INDENT2 " "
struct input_property_map {
- android::PropertyMap* propertyMap;
+ std::unique_ptr<android::PropertyMap> propertyMap;
};
struct input_property {
@@ -225,16 +225,17 @@
ALOGD("No input device configuration file found for device '%s'.",
idi.name.c_str());
} else {
- auto propMap = new input_property_map_t();
- status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap);
- if (status) {
+ std::unique_ptr<input_property_map_t> propMap = std::make_unique<input_property_map_t>();
+ android::base::Result<std::unique_ptr<PropertyMap>> result =
+ PropertyMap::load(configFile.c_str());
+ if (!result.ok()) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
idi.name.c_str());
- delete propMap;
return nullptr;
}
- return propMap;
+ propMap->propertyMap = std::move(*result);
+ return propMap.release();
}
return nullptr;
}
@@ -278,7 +279,6 @@
void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) {
if (map != nullptr) {
- delete map->propertyMap;
delete map;
}
}
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 577309a..47773d9 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -48,8 +48,10 @@
const sp<ISetInputWindowsListener>&) {
return binder::Status::ok();
}
- binder::Status registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
- binder::Status unregisterInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
+ binder::Status createInputChannel(const std::string&, InputChannel*) {
+ return binder::Status::ok();
+ }
+ binder::Status removeInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); }
private:
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 67d3cc2..c5210b5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -280,14 +280,14 @@
if (configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
} else {
- PropertyMap* propertyMap;
- status_t status = PropertyMap::load(String8(configurationFile.c_str()), &propertyMap);
- if (status) {
+ android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(configurationFile.c_str());
+ if (!propertyMap.ok()) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
identifier.name.c_str());
} else {
- configuration = std::unique_ptr<PropertyMap>(propertyMap);
+ configuration = std::move(*propertyMap);
}
}
}
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
index b5c5c9e..5c8a8da 100644
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -26,4 +26,5 @@
void getInputWindows(out InputWindowInfo[] inputHandles);
void getInputChannels(out InputChannel[] channels);
void getLastFocusRequest(out FocusRequest request);
+ void resetInputManager();
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ad6a602..70f872a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -152,6 +152,7 @@
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
ADD_FAILURE() << "Did not receive ANR callback";
+ return {};
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
@@ -548,10 +549,9 @@
class FakeInputReceiver {
public:
- explicit FakeInputReceiver(const std::shared_ptr<InputChannel>& clientChannel,
- const std::string name)
+ explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
: mName(name) {
- mConsumer = std::make_unique<InputConsumer>(clientChannel);
+ mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
}
InputEvent* consume() {
@@ -701,11 +701,10 @@
int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
: mName(name) {
if (token == std::nullopt) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
- token = serverChannel->getConnectionToken();
- dispatcher->registerInputChannel(std::move(serverChannel));
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher->createInputChannel(name);
+ token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
}
inputApplicationHandle->updateInfo();
@@ -1653,10 +1652,9 @@
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
- dispatcher->registerInputMonitor(std::move(serverChannel), displayId, isGestureMonitor);
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher->createInputMonitor(displayId, isGestureMonitor, name);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
}
sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -2144,8 +2142,9 @@
mWindow->consumeFocusEvent(true);
}
- void sendAndConsumeKeyDown() {
+ void sendAndConsumeKeyDown(int32_t deviceId) {
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
mDispatcher->notifyKey(&keyArgs);
@@ -2167,8 +2166,9 @@
EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
}
- void sendAndConsumeKeyUp() {
+ void sendAndConsumeKeyUp(int32_t deviceId) {
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
mDispatcher->notifyKey(&keyArgs);
@@ -2179,21 +2179,59 @@
};
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeatFromTwoDevices) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ /* repeatCount will start from 1 for deviceId 2 */
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
expectKeyRepeatOnce(repeatCount);
}
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
expectKeyRepeatOnce(1 /*repeatCount*/);
- sendAndConsumeKeyUp();
+ sendAndConsumeKeyUp(1 /* deviceId */);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ // Stale key up from device 1.
+ sendAndConsumeKeyUp(1 /* deviceId */);
+ // Device 2 is still down, keep repeating
+ expectKeyRepeatOnce(2 /*repeatCount*/);
+ expectKeyRepeatOnce(3 /*repeatCount*/);
+ // Device 2 key up
+ sendAndConsumeKeyUp(2 /* deviceId */);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatStopsAfterRepeatingKeyUp) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ // Device 2 which holds the key repeating goes up, expect the repeating to stop.
+ sendAndConsumeKeyUp(2 /* deviceId */);
+ // Device 1 still holds key down, but the repeating was already stopped
mWindow->assertNoEvents();
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
InputEvent* repeatEvent = mWindow->consume();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
@@ -2203,7 +2241,7 @@
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index 3aef1e4..c368e79 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -135,7 +135,6 @@
public:
TestInputManager(){};
- void checkFdFlags(const android::base::unique_fd& fd);
binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
@@ -147,10 +146,12 @@
const std::vector<InputWindowInfo>& handles,
const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- binder::Status registerInputChannel(const InputChannel& channel) override;
- binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+ binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+ binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
binder::Status setFocusedWindow(const FocusRequest&) override;
+ void reset();
+
private:
mutable Mutex mLock;
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
@@ -164,6 +165,7 @@
binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
binder::Status getLastFocusRequest(FocusRequest*) override;
+ binder::Status resetInputManager() override;
private:
sp<android::TestInputManager> mManager;
@@ -182,6 +184,11 @@
return mManager->getLastFocusRequest(request);
}
+binder::Status TestInputQuery::resetInputManager() {
+ mManager->reset();
+ return binder::Status::ok();
+}
+
binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
if (mCbFunc != nullptr) {
mCbFunc();
@@ -204,23 +211,21 @@
return binder::Status::ok();
}
-void TestInputManager::checkFdFlags(const android::base::unique_fd& fd) {
- const int result = fcntl(fd, F_GETFL);
- EXPECT_NE(result, -1);
- EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
-}
-
-binder::Status TestInputManager::registerInputChannel(const InputChannel& channel) {
+binder::Status TestInputManager::createInputChannel(const std::string& name,
+ InputChannel* outChannel) {
AutoMutex _l(mLock);
- // check Fd flags
- checkFdFlags(channel.getFd());
+ std::unique_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputChannels.push_back(channel.dup());
+ clientChannel->copyTo(*outChannel);
+
+ mInputChannels.emplace_back(std::move(serverChannel));
return binder::Status::ok();
}
-binder::Status TestInputManager::unregisterInputChannel(const sp<IBinder>& connectionToken) {
+binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
AutoMutex _l(mLock);
auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
@@ -271,6 +276,12 @@
return binder::Status::ok();
}
+void TestInputManager::reset() {
+ mHandlesPerDisplay.clear();
+ mInputChannels.clear();
+ mFocusRequest = FocusRequest();
+}
+
void InputFlingerServiceTest::SetUp() {
mSetInputWindowsListener = new SetInputWindowsListener([&]() {
std::unique_lock<std::mutex> lock(mLock);
@@ -316,7 +327,9 @@
InitializeInputFlinger();
}
-void InputFlingerServiceTest::TearDown() {}
+void InputFlingerServiceTest::TearDown() {
+ mQuery->resetInputManager();
+}
void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
EXPECT_EQ(mInfo, info);
@@ -367,45 +380,33 @@
}
/**
- * Test InputFlinger service interface registerInputChannel
+ * Test InputFlinger service interface createInputChannel
*/
-TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannel) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
+TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
+ // Test that the unblocked file descriptor flag is kept across processes over binder
+ // transactions.
- InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
- mService->registerInputChannel(*serverChannel);
+ InputChannel channel;
+ ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
+
+ const base::unique_fd& fd = channel.getFd();
+ ASSERT_TRUE(fd.ok());
+
+ const int result = fcntl(fd, F_GETFL);
+ EXPECT_NE(result, -1);
+ EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) {
+ InputChannel channel;
+ ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
std::vector<::android::InputChannel> channels;
mQuery->getInputChannels(&channels);
ASSERT_EQ(channels.size(), 1UL);
- EXPECT_EQ(channels[0], *serverChannel);
+ EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
- mService->unregisterInputChannel(serverChannel->getConnectionToken());
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 0UL);
-}
-
-/**
- * Test InputFlinger service interface registerInputChannel with invalid cases
- */
-TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannelInvalid) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
-
- std::vector<::android::InputChannel> channels;
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 0UL);
-
- mService->registerInputChannel(InputChannel());
- mService->unregisterInputChannel(clientChannel->getConnectionToken());
-
- mService->registerInputChannel(*serverChannel);
- mService->registerInputChannel(*clientChannel);
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 2UL);
-
- mService->unregisterInputChannel(clientChannel->getConnectionToken());
- mService->unregisterInputChannel(serverChannel->getConnectionToken());
+ mService->removeInputChannel(channel.getConnectionToken());
mQuery->getInputChannels(&channels);
EXPECT_EQ(channels.size(), 0UL);
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 7c08e11..ed0d75b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -38,7 +38,6 @@
"android.hardware.power-cpp",
"libbase",
"libbinder",
- "libbufferhubqueue",
"libcutils",
"libEGL",
"libfmq",
@@ -49,7 +48,6 @@
"liblayers_proto",
"liblog",
"libnativewindow",
- "libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
"libstatslog",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index fc1adcc..d590f23 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -166,7 +166,7 @@
virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) = 0;
// Sets the bounds to use
- virtual void setDisplaySpaceSize(const ui::Size&) = 0;
+ virtual void setDisplaySize(const ui::Size&) = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6fe93bf..5b832a5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -40,7 +40,7 @@
void setCompositionEnabled(bool) override;
void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) override;
- void setDisplaySpaceSize(const ui::Size&) override;
+ void setDisplaySize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f4d2b56..06e6a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -39,7 +39,7 @@
namespace compositionengine::impl {
struct OutputCompositionState {
- // If false, composition will not per performed for this display
+ // If false, composition will not be performed for this display
bool isEnabled{false};
// If false, this output is not considered secure
@@ -71,6 +71,11 @@
// match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0.
ProjectionSpace orientedDisplaySpace;
+ // The space of the framebuffer. Its bounds match the size of the framebuffer and its
+ // orientation matches the orientation of the display. Typically the framebuffer space will
+ // be identical to the physical display space.
+ ProjectionSpace framebufferSpace;
+
// The space of the physical display. It is as big as the currently active display mode. The
// content in this space can be rotated.
ProjectionSpace displaySpace;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 19025c1..d6fbd7f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -37,7 +37,7 @@
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
- MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
+ MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index d201104..5459861 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -55,6 +55,7 @@
mPowerAdvisor = args.powerAdvisor;
editState().isSecure = args.isSecure;
+ editState().displaySpace.bounds = Rect(args.pixels);
setLayerStackFilter(args.layerStackId,
args.physical ? args.physical->type == DisplayConnectionType::Internal
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index abb8769..44edb6e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -69,6 +69,19 @@
return Reversed<T>(c);
}
+struct ScaleVector {
+ float x;
+ float y;
+};
+
+// Returns a ScaleVector (x, y) such that from.scale(x, y) = to',
+// where to' will have the same size as "to". In the case where "from" and "to"
+// start at the origin to'=to.
+ScaleVector getScale(const Rect& from, const Rect& to) {
+ return {.x = static_cast<float>(to.width()) / from.width(),
+ .y = static_cast<float>(to.height()) / from.height()};
+}
+
} // namespace
std::shared_ptr<Output> createOutput(
@@ -110,9 +123,10 @@
auto& outputState = editState();
outputState.displaySpace.orientation = orientation;
- // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
+ LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+ "The display bounds are unknown.");
- // Compute the orientedDisplaySpace bounds
+ // Compute orientedDisplaySpace
ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
@@ -129,17 +143,51 @@
}
outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+ // Compute framebufferSpace
+ outputState.framebufferSpace.orientation = orientation;
+ LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+ "The framebuffer bounds are unknown.");
+ const auto scale =
+ getScale(outputState.framebufferSpace.bounds, outputState.displaySpace.bounds);
+ outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+
+ // Compute layerStackSpace
outputState.layerStackSpace.content = layerStackSpaceRect;
outputState.layerStackSpace.bounds = layerStackSpaceRect;
+
outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
-
dirtyEntireOutput();
}
-void Output::setDisplaySpaceSize(const ui::Size& size) {
+void Output::setDisplaySize(const ui::Size& size) {
mRenderSurface->setDisplaySize(size);
- editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
+
+ auto& state = editState();
+
+ // Update framebuffer space
+ const Rect newBounds(size);
+ ScaleVector scale;
+ scale = getScale(state.framebufferSpace.bounds, newBounds);
+ state.framebufferSpace.bounds = newBounds;
+ state.framebufferSpace.content.scaleSelf(scale.x, scale.y);
+
+ // Update display space
+ scale = getScale(state.displaySpace.bounds, newBounds);
+ state.displaySpace.bounds = newBounds;
+ state.displaySpace.content.scaleSelf(scale.x, scale.y);
+ state.transform = state.layerStackSpace.getTransform(state.displaySpace);
+
+ // Update oriented display space
+ const auto orientation = state.displaySpace.orientation;
+ ui::Size orientedSize = size;
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ const Rect newOrientedBounds(orientedSize);
+ scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds);
+ state.orientedDisplaySpace.bounds = newOrientedBounds;
+ state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y);
dirtyEntireOutput();
}
@@ -247,8 +295,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
-
+ editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -877,7 +924,7 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
clientCompositionDisplay.clip = outputState.layerStackSpace.content;
clientCompositionDisplay.orientation =
ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 776fdde..ee30ad8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -39,6 +39,8 @@
out.append("\n ");
dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
out.append("\n ");
+ dumpVal(out, "framebufferSpace", to_string(framebufferSpace));
+ out.append("\n ");
dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
out.append("\n ");
dumpVal(out, "displaySpace", to_string(displaySpace));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c01f3e0..57b399a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -248,20 +248,46 @@
}
/*
- * Output::setDisplaySpaceSize()
+ * Output::setDisplaySize()
*/
-TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
- const ui::Size displaySize{200, 400};
+TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
+ mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
+ mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
+ mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
+ mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
- EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
- EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+ const ui::Size newDisplaySize{500, 1000};
- mOutput->setDisplaySpaceSize(displaySize);
+ EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1);
- EXPECT_EQ(Rect(displaySize), mOutput->getState().displaySpace.bounds);
+ mOutput->setDisplaySize(newDisplaySize);
- EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+ const auto state = mOutput->getState();
+
+ const Rect displayRect(newDisplaySize);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content);
+ EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+ EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
}
/*
@@ -422,7 +448,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().displaySpace.bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
}
/*
@@ -2778,9 +2804,10 @@
mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+ mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+ mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.dataspace = kDefaultOutputDataspace;
mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
mOutput.mState.isSecure = false;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 7df9b76..a1ccaad 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -156,7 +156,8 @@
}
void DisplayDevice::setDisplaySize(int width, int height) {
- mCompositionDisplay->setDisplaySpaceSize(ui::Size(width, height));
+ LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
+ mCompositionDisplay->setDisplaySize(ui::Size(width, height));
}
void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b19e453..85046a4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2418,40 +2418,64 @@
const float xScale = t.getScaleX();
const float yScale = t.getScaleY();
if (xScale != 1.0f || yScale != 1.0f) {
- info.touchableRegion.scaleSelf(xScale, yScale);
xSurfaceInset = std::round(xSurfaceInset * xScale);
ySurfaceInset = std::round(ySurfaceInset * yScale);
}
- layerBounds = t.transform(layerBounds);
+ Rect transformedLayerBounds = t.transform(layerBounds);
// clamp inset to layer bounds
- xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0;
- ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0;
+ xSurfaceInset = (xSurfaceInset >= 0)
+ ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2)
+ : 0;
+ ySurfaceInset = (ySurfaceInset >= 0)
+ ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2)
+ : 0;
// inset while protecting from overflow TODO(b/161235021): What is going wrong
// in the overflow scenario?
{
int32_t tmp;
- if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp;
- if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp;
- if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp;
- if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp;
+ if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp))
+ transformedLayerBounds.left = tmp;
+ if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp))
+ transformedLayerBounds.right = tmp;
+ if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp))
+ transformedLayerBounds.top = tmp;
+ if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp))
+ transformedLayerBounds.bottom = tmp;
}
// Input coordinate should match the layer bounds.
- info.frameLeft = layerBounds.left;
- info.frameTop = layerBounds.top;
- info.frameRight = layerBounds.right;
- info.frameBottom = layerBounds.bottom;
+ info.frameLeft = transformedLayerBounds.left;
+ info.frameTop = transformedLayerBounds.top;
+ info.frameRight = transformedLayerBounds.right;
+ info.frameBottom = transformedLayerBounds.bottom;
+ // Compute the correct transform to send to input. This will allow it to transform the
+ // input coordinates from display space into window space. Therefore, it needs to use the
+ // final layer frame to create the inverse transform. Since surface insets are added later,
+ // along with the overflow, the best way to ensure we get the correct transform is to use
+ // the final frame calculated.
+ // 1. Take the original transform set on the window and get the inverse transform. This is
+ // used to get the final bounds in display space (ignorning the transform). Apply the
+ // inverse transform on the layerBounds to get the untransformed frame (in display space)
+ // 2. Take the top and left of the untransformed frame to get the real position on screen.
+ // Apply the layer transform on top/left so it includes any scale or rotation. These will
+ // be the new translation values for the transform.
+ // 3. Update the translation of the original transform to the new translation values.
+ // 4. Send the inverse transform to input so the coordinates can be transformed back into
+ // window space.
+ ui::Transform inverseTransform = t.inverse();
+ Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds);
+ vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top);
ui::Transform inputTransform(t);
- inputTransform.set(layerBounds.left, layerBounds.top);
+ inputTransform.set(translation.x, translation.y);
info.transform = inputTransform.inverse();
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
- info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
+ info.touchableRegion = inputTransform.transform(info.touchableRegion);
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 3307388..bf2a509 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -62,6 +62,8 @@
return "VSyncRequest::None";
case VSyncRequest::Single:
return "VSyncRequest::Single";
+ case VSyncRequest::SingleSuppressCallback:
+ return "VSyncRequest::SingleSuppressCallback";
default:
return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
}
@@ -267,6 +269,8 @@
if (connection->vsyncRequest == VSyncRequest::None) {
connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
+ } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {
+ connection->vsyncRequest = VSyncRequest::Single;
}
}
@@ -451,8 +455,11 @@
switch (connection->vsyncRequest) {
case VSyncRequest::None:
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
+ return false;
+ case VSyncRequest::Single:
+ connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
case VSyncRequest::Periodic:
return true;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 80bd606..e42ca05 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -50,8 +50,11 @@
using ResyncCallback = std::function<void()>;
enum class VSyncRequest {
- None = -1,
- Single = 0,
+ None = -2,
+ // Single wakes up for the next two frames to avoid scheduler overhead
+ Single = -1,
+ // SingleSuppressCallback only wakes up for the next frame
+ SingleSuppressCallback = 0,
Periodic = 1,
// Subsequent values are periods.
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6a0f24a..59a2ea4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6189,6 +6189,11 @@
}
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame timeline vsync on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
layer->setFrameTimelineVsync(frameTimelineVsyncId);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 894ee6d..d77387a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -112,7 +112,7 @@
StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
StringAppendF(&result, "displayConfigStats is as below:\n");
for (const auto& [fps, duration] : refreshRateStats) {
- StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+ StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
}
result.back() = '\n';
StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index c45a1a1..fa742c5 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -19,6 +19,7 @@
"VibratorCallbackScheduler.cpp",
"VibratorHalController.cpp",
"VibratorHalWrapper.cpp",
+ "VibratorManagerHalWrapper.cpp",
],
aidl: {
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 46175ad..e8606ca 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -46,8 +46,6 @@
// -------------------------------------------------------------------------------------------------
-static constexpr int MAX_RETRIES = 1;
-
std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
static bool gHalExists = true;
if (!gHalExists) {
@@ -89,6 +87,8 @@
// -------------------------------------------------------------------------------------------------
+static constexpr int MAX_RETRIES = 1;
+
template <typename T>
HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
if (result.isFailed()) {
@@ -126,11 +126,12 @@
// -------------------------------------------------------------------------------------------------
-void HalController::init() {
+bool HalController::init() {
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
mConnectedHal = mHalConnector->connect(mCallbackScheduler);
}
+ return mConnectedHal != nullptr;
}
HalResult<void> HalController::ping() {
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
new file mode 100644
index 0000000..71955af
--- /dev/null
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "VibratorManagerHalWrapper"
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+constexpr int32_t SINGLE_VIBRATOR_ID = 0;
+
+HalResult<void> LegacyManagerHalWrapper::ping() {
+ return mController->ping();
+}
+
+void LegacyManagerHalWrapper::tryReconnect() {
+ mController->tryReconnect();
+}
+
+HalResult<std::vector<int32_t>> LegacyManagerHalWrapper::getVibratorIds() {
+ if (mController->init()) {
+ return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>(1, SINGLE_VIBRATOR_ID));
+ }
+ // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+ return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>());
+}
+
+HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(int32_t id) {
+ if (id == SINGLE_VIBRATOR_ID && mController->init()) {
+ return HalResult<std::shared_ptr<HalController>>::ok(mController);
+ }
+ // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+ return HalResult<std::shared_ptr<HalController>>::failed("No vibrator with id = " +
+ std::to_string(id));
+}
+
+HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
+ return HalResult<void>::unsupported();
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 3b61f42..d1028a4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -51,11 +51,19 @@
mConnectedHal(nullptr) {}
virtual ~HalController() = default;
- void init();
+ /* Connects to the newest HAL version available, possibly waiting for the registered service to
+ * become available. This will automatically be called at the first API usage if it was not
+ * manually called beforehand. Calling this manually during the setup phase can avoid slowing
+ * the first API call later on. Returns true if any HAL version is available, false otherwise.
+ */
+ virtual bool init();
- HalResult<void> ping() final override;
- void tryReconnect() final override;
+ /* reloads HAL service instance without waiting. This relies on the HAL version found by init()
+ * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
+ */
+ virtual void tryReconnect() override;
+ virtual HalResult<void> ping() override;
HalResult<void> on(std::chrono::milliseconds timeout,
const std::function<void()>& completionCallback) final override;
HalResult<void> off() final override;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 7b99bbb..bcb735d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -140,9 +140,12 @@
: mCallbackScheduler(std::move(scheduler)) {}
virtual ~HalWrapper() = default;
- virtual HalResult<void> ping() = 0;
+ /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+ * service restarts, to rapidly retry after a failure.
+ */
virtual void tryReconnect() = 0;
+ virtual HalResult<void> ping() = 0;
virtual HalResult<void> on(std::chrono::milliseconds timeout,
const std::function<void()>& completionCallback) = 0;
virtual HalResult<void> off() = 0;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
new file mode 100644
index 0000000..99947a5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+#define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+
+#include <vibratorservice/VibratorHalController.h>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for VibratorManager HAL handlers.
+class ManagerHalWrapper {
+public:
+ ManagerHalWrapper() = default;
+ virtual ~ManagerHalWrapper() = default;
+
+ virtual HalResult<void> ping() = 0;
+
+ /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+ * service restarts, to rapidly retry after a failure.
+ */
+ virtual void tryReconnect() = 0;
+
+ virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0;
+ virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0;
+
+ virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0;
+ virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> cancelSynced() = 0;
+};
+
+// Wrapper for the VibratorManager over single Vibrator HAL.
+class LegacyManagerHalWrapper : public ManagerHalWrapper {
+public:
+ LegacyManagerHalWrapper() : LegacyManagerHalWrapper(std::make_shared<HalController>()) {}
+ explicit LegacyManagerHalWrapper(std::shared_ptr<HalController> controller)
+ : mController(std::move(controller)) {}
+ virtual ~LegacyManagerHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<std::vector<int32_t>> getVibratorIds() override final;
+ HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+ HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+ HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+ HalResult<void> cancelSynced() override final;
+
+private:
+ const std::shared_ptr<HalController> mController;
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index 9033124..5fc6d45 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -23,6 +23,7 @@
"VibratorHalWrapperHidlV1_1Test.cpp",
"VibratorHalWrapperHidlV1_2Test.cpp",
"VibratorHalWrapperHidlV1_3Test.cpp",
+ "VibratorManagerHalWrapperLegacyTest.cpp",
],
cflags: [
"-Wall",
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index f04e016..cda5e9a 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -184,11 +184,11 @@
// -------------------------------------------------------------------------------------------------
TEST_F(VibratorHalControllerTest, TestInit) {
- mController->init();
+ ASSERT_TRUE(mController->init());
ASSERT_EQ(1, mConnectCounter);
// Noop when wrapper was already initialized.
- mController->init();
+ ASSERT_TRUE(mController->init());
ASSERT_EQ(1, mConnectCounter);
}
@@ -339,6 +339,7 @@
std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
ASSERT_EQ(0, mConnectCounter);
+ ASSERT_FALSE(mController->init());
ASSERT_TRUE(mController->ping().isUnsupported());
ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
ASSERT_TRUE(mController->off().isUnsupported());
@@ -356,7 +357,7 @@
.isUnsupported());
// One connection attempt per api call.
- ASSERT_EQ(12, mConnectCounter);
+ ASSERT_EQ(13, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
new file mode 100644
index 0000000..d5520a1
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "VibratorManagerHalWrapperLegacyTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalController : public vibrator::HalController {
+public:
+ MockHalController() = default;
+ virtual ~MockHalController() = default;
+
+ MOCK_METHOD(bool, init, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorManagerHalWrapperLegacyTest : public Test {
+public:
+ void SetUp() override {
+ mMockController = std::make_shared<StrictMock<MockHalController>>();
+ mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr;
+ std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
+ EXPECT_CALL(*mMockController.get(), ping())
+ .Times(Exactly(2))
+ .WillOnce(Return(vibrator::HalResult<void>::failed("message")))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+ ASSERT_TRUE(mWrapper->ping().isOk());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
+ EXPECT_CALL(*mMockController.get(), tryReconnect()).Times(Exactly(1));
+
+ mWrapper->tryReconnect();
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
+ std::vector<int32_t> expectedIds;
+ expectedIds.push_back(0);
+
+ EXPECT_CALL(*mMockController.get(), init())
+ .Times(Exactly(2))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+
+ auto result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(std::vector<int32_t>(), result.value());
+
+ result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(expectedIds, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithValidIdReturnsController) {
+ EXPECT_CALL(*mMockController.get(), init())
+ .Times(Exactly(2))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+
+ ASSERT_TRUE(mWrapper->getVibrator(0).isFailed());
+
+ auto result = mWrapper->getVibrator(0);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(mMockController.get(), result.value().get());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithInvalidIdFails) {
+ ASSERT_TRUE(mWrapper->getVibrator(-1).isFailed());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) {
+ std::vector<int32_t> vibratorIds;
+ vibratorIds.push_back(0);
+
+ ASSERT_TRUE(mWrapper->prepareSynced(vibratorIds).isUnsupported());
+ ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported());
+ ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
+}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0f791c9..48090af 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -258,7 +258,11 @@
bool shared;
struct Image {
- Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
+ Image()
+ : image(VK_NULL_HANDLE),
+ dequeue_fence(-1),
+ release_fence(-1),
+ dequeued(false) {}
VkImage image;
android::sp<ANativeWindowBuffer> buffer;
// The fence is only valid when the buffer is dequeued, and should be
@@ -266,6 +270,10 @@
// closed: either by closing it explicitly when queueing the buffer,
// or by passing ownership e.g. to ANativeWindow::cancelBuffer().
int dequeue_fence;
+ // This fence is a dup of the sync fd returned from the driver via
+ // vkQueueSignalReleaseImageANDROID upon vkQueuePresentKHR. We must
+ // ensure it is closed upon re-presenting or releasing the image.
+ int release_fence;
bool dequeued;
} images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
@@ -280,10 +288,19 @@
return reinterpret_cast<Swapchain*>(handle);
}
+static bool IsFencePending(int fd) {
+ if (fd < 0)
+ return false;
+
+ errno = 0;
+ return sync_wait(fd, 0 /* timeout */) == -1 && errno == ETIME;
+}
+
void ReleaseSwapchainImage(VkDevice device,
ANativeWindow* window,
int release_fence,
- Swapchain::Image& image) {
+ Swapchain::Image& image,
+ bool defer_if_pending) {
ATRACE_CALL();
ALOG_ASSERT(release_fence == -1 || image.dequeued,
@@ -319,10 +336,18 @@
close(release_fence);
}
}
-
+ release_fence = -1;
image.dequeued = false;
}
+ if (defer_if_pending && IsFencePending(image.release_fence))
+ return;
+
+ if (image.release_fence >= 0) {
+ close(image.release_fence);
+ image.release_fence = -1;
+ }
+
if (image.image) {
ATRACE_BEGIN("DestroyImage");
GetData(device).driver.DestroyImage(device, image.image, nullptr);
@@ -338,7 +363,8 @@
return;
for (uint32_t i = 0; i < swapchain->num_images; i++) {
if (!swapchain->images[i].dequeued)
- ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+ ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i],
+ true);
}
swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
swapchain->timing.clear();
@@ -996,7 +1022,7 @@
}
for (uint32_t i = 0; i < swapchain->num_images; i++) {
- ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+ ReleaseSwapchainImage(device, window, -1, swapchain->images[i], false);
}
if (active) {
@@ -1628,6 +1654,9 @@
ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
swapchain_result = result;
}
+ if (img.release_fence >= 0)
+ close(img.release_fence);
+ img.release_fence = fence < 0 ? -1 : dup(fence);
if (swapchain.surface.swapchain_handle ==
present_info->pSwapchains[sc]) {
@@ -1761,7 +1790,7 @@
WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
}
} else {
- ReleaseSwapchainImage(device, nullptr, fence, img);
+ ReleaseSwapchainImage(device, nullptr, fence, img, true);
swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
}